--- /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;
+ }
+ }
+}
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
-import org.opendaylight.controller.md.sal.binding.api.NotificationService;
-import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.openflowplugin.api.openflow.OpenFlowPluginTimer;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceReplyProcessor;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.MultiMsgCollector;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
/**
* <p>
public interface DeviceContext extends AutoCloseable,
DeviceReplyProcessor,
PortNumberCache,
+ TxFacade,
XidSequencer {
void setStatisticsRpcEnabled(boolean isStatisticsRpcEnabled);
*/
ListenableFuture<Void> onDeviceLostClusterLeadership();
- /**
- * Method creates put operation using provided data in underlying transaction chain.
- */
- <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store, final InstanceIdentifier<T> path, final T data) throws Exception;
-
- /**
- * Method creates delete operation for provided path in underlying transaction chain.
- */
- <T extends DataObject> void addDeleteToTxChain(final LogicalDatastoreType store, final InstanceIdentifier<T> path) throws Exception;
-
- /**
- * Method submits Transaction to DataStore.
- * @return transaction is submitted successfully
- */
- boolean submitTransaction();
-
/**
* Method has to close TxManager ASAP we are notified about Closed Connection
* @return sync. future for Slave and MD-SAL completition for Master
*/
ListenableFuture<Void> shuttingDownDataStoreTransactions();
- /**
- * Method exposes transaction created for device
- * represented by this context. This read only transaction has a fresh dataStore snapshot.
- * There is a possibility to get different data set from DataStore
- * as write transaction in this context.
- * @return readOnlyTransaction - Don't forget to close it after finish reading
- */
- ReadOnlyTransaction getReadTransaction();
-
-
/**
* Method provides current devices connection context.
*
void setDeviceSynchronized(boolean deviceSynchronized);
- void setRole(OfpRole ofpRole);
-
- OfpRole getRole();
-
boolean isStatisticsPollingEnabled();
void setStatisticsPollingEnabledProp(boolean statPollEnabled);
/**
* Translates from input to output
* @param input
- * @param deviceContext
+ * @param deviceState
* @param connectionDistinguisher
* @return message of output type
*/
- O translate(I input, DeviceContext deviceContext, Object connectionDistinguisher);
+ O translate(I input, DeviceState deviceState, Object connectionDistinguisher);
}
--- /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.api.openflow.device;
+
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Handles operations with transactions
+ */
+public interface TxFacade {
+
+ /**
+ * Method creates put operation using provided data in underlying transaction chain.
+ */
+ <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.
+ */
+ <T extends DataObject> void addDeleteToTxChain(final LogicalDatastoreType store, final InstanceIdentifier<T> path) throws Exception;
+
+ /**
+ * Method submits Transaction to DataStore.
+ * @return transaction is submitted successfully
+ */
+ boolean submitTransaction();
+
+ /**
+ * Method exposes transaction created for device
+ * represented by this context. This read only transaction has a fresh dataStore snapshot.
+ * There is a possibility to get different data set from DataStore
+ * as write transaction in this context.
+ * @return readOnlyTransaction - Don't forget to close it after finish reading
+ */
+ ReadOnlyTransaction getReadTransaction();
+}
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();
}
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
<name>binding-notification-broker</name>
</notification-service>
- <ownership-service>
- <type xmlns:entity-ownership="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:entity-ownership-service">entity-ownership:entity-ownership-service</type>
- <name>entity-ownership-service</name>
- </ownership-service>
-
+ <ownership-service>
+ <type xmlns:entity-ownership="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:entity-ownership-service">entity-ownership:entity-ownership-service</type>
+ <name>entity-ownership-service</name>
+ </ownership-service>
+
+ <!-- openflowplugin configuraion -->
+ <skip-table-features>false</skip-table-features>
</module>
</modules>
package org.opendaylight.openflowplugin.impl;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
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);
}
- private void notifyServiceChangeListeners(final NodeId nodeId, final boolean success){
+ @VisibleForTesting
+ void notifyServiceChangeListeners(final NodeId nodeId, final boolean success){
if (serviceChangeListeners.size() == 0) {
return;
}
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);
LOG.info("initialization phase for node {} in device context was successful. All phases initialized OK.", nodeId);
}
}
+
+ @VisibleForTesting
+ public boolean isServiceChangeListenersEmpty() {
+ return this.serviceChangeListeners.isEmpty();
+ }
+
}
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);
try {
handshakeContext.close();
} catch (Exception e) {
- LOG.info("handshake context closing failed: ", e);
+ LOG.error("handshake context closing failed:{} ", e);
} finally {
handshakeContext = null;
}
package org.opendaylight.openflowplugin.impl.connection;
import java.net.InetAddress;
-import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionReadyListener;
final String connectionIdentifier, final int handshakeThreadLimit) {
return new ThreadPoolLoggingExecutor(handshakeThreadLimit,
handshakeThreadLimit, 0L, TimeUnit.MILLISECONDS,
- new ArrayBlockingQueue<Runnable>(HELLO_LIMIT), "OFHandshake-" + connectionIdentifier);
+ new LinkedBlockingQueue<>(HELLO_LIMIT), "OFHandshake-" + connectionIdentifier);
}
/**
try {
handshakePool.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
- LOG.info("Error while awaiting termination on pool. Will use shutdownNow method.");
+ LOG.error("Error while awaiting termination on pool. Will use shutdownNow method.");
} finally {
handshakePool.purge();
if (! handshakePool.isTerminated()) {
try {
wait();
} catch (InterruptedException e) {
- LOG.info("Interrupted while waiting for entry", e);
+ LOG.error("Interrupted while waiting for entry", e);
return null;
}
}
@Override
public void onConnectionReady() {
- LOG.debug("device is connected and ready-to-use (pipeline prepared): {}",
- connectionContext.getConnectionAdapter().getRemoteAddress());
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("device is connected and ready-to-use (pipeline prepared): {}",
+ connectionContext.getConnectionAdapter().getRemoteAddress());
+ }
if (connectionContext.getConnectionState() == null) {
synchronized (connectionContext) {
// as we run not in netty thread, need to remain in sync lock until initial handshake step processed
handshakeResult.get();
} catch (Exception e) {
- LOG.warn("failed to process onConnectionReady event on device {}",
+ LOG.error("failed to process onConnectionReady event on device {}, reason {}",
connectionContext.getConnectionAdapter().getRemoteAddress(),
e);
connectionContext.closeConnection(false);
try {
handshakeContext.close();
} catch (Exception e1) {
- LOG.info("failed to close handshake context for device {}",
+ LOG.error("failed to close handshake context for device {}, reason {}",
connectionContext.getConnectionAdapter().getRemoteAddress(),
e1
);
}
}
} else {
- LOG.debug("already touched by hello message from device {}", connectionContext.getConnectionAdapter().getRemoteAddress());
+ LOG.debug("already touched by hello message from device {} after second check",
+ connectionContext.getConnectionAdapter().getRemoteAddress());
}
}
} else {
- LOG.debug("already touched by hello message from device {}", connectionContext.getConnectionAdapter().getRemoteAddress());
+ LOG.debug("already touched by hello message from device {} after first check",
+ connectionContext.getConnectionAdapter().getRemoteAddress());
}
}
SessionStatistics.countEvent(connectionContext.getNodeId().toString(),
SessionStatistics.ConnectionStatus.CONNECTION_CREATED);
} catch (final Exception e) {
- LOG.info("ConnectionContext initial processing failed: {}", e.getMessage());
+ LOG.error("ConnectionContext initial processing failed: {}", e.getMessage());
SessionStatistics.countEvent(connectionContext.getNodeId().toString(),
SessionStatistics.ConnectionStatus.CONNECTION_DISCONNECTED_BY_OFP);
connectionContext.closeConnection(true);
@Override
public void onFailure(final Throwable t) {
- LOG.info("failed to get sweep barrier after posthandshake for device {}", connectionContext.getNodeId());
+ LOG.error("failed to get sweep barrier after posthandshake for device {}", connectionContext.getNodeId());
connectionContext.closeConnection(false);
}
});
try {
handshakeContext.close();
} catch (final Exception e) {
- LOG.warn("Closing handshake context failed: {}", e.getMessage());
- LOG.debug("Detail in handshake context close:", e);
+ LOG.error("Closing handshake context failed: {}", e.getMessage());
+ LOG.debug("Detail in handshake context close: {}", e);
}
}
@Override
public void onEchoRequestMessage(final EchoRequestMessage echoRequestMessage) {
- LOG.debug("echo request received: {}", echoRequestMessage.getXid());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("echo request received: {}", echoRequestMessage.getXid());
+ }
EchoReplyInputBuilder builder = new EchoReplyInputBuilder();
builder.setVersion(echoRequestMessage.getVersion());
builder.setXid(echoRequestMessage.getXid());
@Override
public void onErrorMessage(final ErrorMessage notification) {
- LOG.warn("NOOP: Error message received during handshake phase: {}", notification);
+ LOG.debug("NOOP: Error message received during handshake phase: {}", notification);
}
@Override
public void onExperimenterMessage(final ExperimenterMessage notification) {
- LOG.info("NOOP: Experimenter message during handshake phase not supported: {}", notification);
+ LOG.debug("NOOP: Experimenter message during handshake phase not supported: {}", notification);
}
@Override
public void onFlowRemovedMessage(final FlowRemovedMessage notification) {
- LOG.info("NOOP: Flow-removed message during handshake phase not supported: {}", notification);
+ LOG.debug("NOOP: Flow-removed message during handshake phase not supported: {}", notification);
}
@Override
public void onHelloMessage(final HelloMessage hello) {
- LOG.debug("processing HELLO.xid: {} from device {}", hello.getXid(), connectionContext.getConnectionAdapter().getRemoteAddress());
+ LOG.debug("processing HELLO.xid: {} from device {}", hello.getXid(),
+ connectionContext.getConnectionAdapter().getRemoteAddress());
final ConnectionContext.CONNECTION_STATE connectionState = connectionContext.getConnectionState();
if (connectionState == null
|| ConnectionContext.CONNECTION_STATE.HANDSHAKING.equals(connectionState)) {
// use up netty thread
handshakeStepWrapper.run();
} else {
- LOG.debug("already out of handshake phase but still received hello message from device {}", connectionContext.getConnectionAdapter().getRemoteAddress());
+ LOG.debug("already out of handshake phase but still received hello message from device {}",
+ connectionContext.getConnectionAdapter().getRemoteAddress());
}
}
} else {
//TODO: consider disconnecting of bad behaving device
- LOG.warn("Hello message received outside handshake phase: ", hello);
- LOG.debug("already touched by onConnectionReady event from device {} (or finished handshake)", connectionContext.getConnectionAdapter().getRemoteAddress());
+ LOG.warn("Hello message received outside handshake phase:{} ", hello);
+ LOG.debug("already touched by onConnectionReady event from device {} (or finished handshake)",
+ connectionContext.getConnectionAdapter().getRemoteAddress());
}
}
@Override
public void onMultipartReplyMessage(final MultipartReplyMessage notification) {
- LOG.info("NOOP: Multipart-reply message during handshake phase not supported: {}", notification);
+ LOG.debug("NOOP: Multipart-reply message during handshake phase not supported: {}", notification);
}
@Override
public void onPacketInMessage(final PacketInMessage notification) {
- LOG.info("NOOP: Packet-in message during handshake phase not supported: {}", notification);
+ LOG.debug("NOOP: Packet-in message during handshake phase not supported: {}", notification);
}
@Override
public void onPortStatusMessage(final PortStatusMessage notification) {
- LOG.info("NOOP: Port-status message during handshake phase not supported: {}", notification);
+ LOG.debug("NOOP: Port-status message during handshake phase not supported: {}", notification);
}
/**
import org.opendaylight.openflowplugin.api.openflow.device.TranslatorLibrary;
import org.opendaylight.openflowplugin.api.openflow.device.Xid;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.MultiMsgCollector;
+import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
import org.opendaylight.openflowplugin.api.openflow.md.core.SwitchConnectionDistinguisher;
import org.opendaylight.openflowplugin.api.openflow.md.core.TranslatorKey;
import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegistry;
DeviceContextImpl(@Nonnull final ConnectionContext primaryConnectionContext,
@Nonnull final DeviceState deviceState,
@Nonnull final DataBroker dataBroker,
- @Nonnull final MessageSpy _messageSpy,
+ @Nonnull final LifecycleConductor conductor,
@Nonnull final OutboundQueueProvider outboundQueueProvider,
@Nonnull final TranslatorLibrary translatorLibrary,
final boolean switchFeaturesMandatory) {
this.primaryConnectionContext = Preconditions.checkNotNull(primaryConnectionContext);
this.deviceState = Preconditions.checkNotNull(deviceState);
this.dataBroker = Preconditions.checkNotNull(dataBroker);
+ Preconditions.checkNotNull(conductor);
this.outboundQueueProvider = Preconditions.checkNotNull(outboundQueueProvider);
- this.transactionChainManager = new TransactionChainManager(dataBroker, deviceState);
+ this.transactionChainManager = new TransactionChainManager(dataBroker, deviceState, conductor);
auxiliaryConnectionContexts = new HashMap<>();
deviceFlowRegistry = new DeviceFlowRegistryImpl();
deviceGroupRegistry = new DeviceGroupRegistryImpl();
deviceMeterRegistry = new DeviceMeterRegistryImpl();
- messageSpy = _messageSpy;
+ messageSpy = conductor.getMessageIntelligenceAgency();
packetInLimiter = new PacketInRateLimiter(primaryConnectionContext.getConnectionAdapter(),
/*initial*/ 1000, /*initial*/2000, messageSpy, REJECTED_DRAIN_FACTOR);
@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
if (itemLifecycleListener != null) {
//1. translate to general flow (table, priority, match, cookie)
final org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved flowRemovedNotification =
- flowRemovedTranslator.translate(flowRemoved, this, null);
+ flowRemovedTranslator.translate(flowRemoved, this.getDeviceState(), null);
//2. create registry key
final FlowRegistryKey flowRegKey = FlowRegistryKeyFactory.create(flowRemovedNotification);
//3. lookup flowId
.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());
@Override
public void processPortStatusMessage(final PortStatusMessage portStatus) {
messageSpy.spyMessage(portStatus.getImplementedInterface(), MessageSpy.STATISTIC_GROUP.FROM_SWITCH_PUBLISHED_SUCCESS);
- final FlowCapableNodeConnector flowCapableNodeConnector = portStatusTranslator.translate(portStatus, this, null);
+ final FlowCapableNodeConnector flowCapableNodeConnector = portStatusTranslator.translate(portStatus, this.getDeviceState(), null);
final KeyedInstanceIdentifier<NodeConnector, NodeConnectorKey> iiToNodeConnector = provideIIToNodeConnector(portStatus.getPortNo(), portStatus.getVersion());
try {
public void processPacketInMessage(final PacketInMessage packetInMessage) {
messageSpy.spyMessage(packetInMessage.getImplementedInterface(), MessageSpy.STATISTIC_GROUP.FROM_SWITCH);
final ConnectionAdapter connectionAdapter = getPrimaryConnectionContext().getConnectionAdapter();
- final PacketReceived packetReceived = packetInTranslator.translate(packetInMessage, this, null);
+ final PacketReceived packetReceived = packetInTranslator.translate(packetInMessage, this.getDeviceState(), null);
if (packetReceived == null) {
LOG.debug("Received a null packet from switch {}", connectionAdapter.getRemoteAddress());
// publish
notificationPublishService.offerNotification(experimenterMessageFromDevBld.build());
} catch (final ConversionException e) {
- LOG.warn("Conversion of experimenter notification failed", e);
+ LOG.error("Conversion of experimenter notification failed", e);
}
}
deviceState.setValid(false);
return transactionChainManager.shuttingDown();
}
+
+ @VisibleForTesting
+ TransactionChainManager getTransactionChainManager() {
+ return this.transactionChainManager;
+ }
}
import javax.annotation.Nonnull;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
-import org.opendaylight.controller.md.sal.binding.api.NotificationService;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
final DeviceContext deviceContext = new DeviceContextImpl(connectionContext,
deviceState,
dataBroker,
- conductor.getMessageIntelligenceAgency(),
+ conductor,
outboundQueueProvider,
translatorLibrary,
switchFeaturesMandatory);
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;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
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;
private final Object txLock = new Object();
private final KeyedInstanceIdentifier<Node, NodeKey> nodeII;
private final DataBroker dataBroker;
+ private final LifecycleConductor conductor;
@GuardedBy("txLock")
private WriteTransaction wTx;
@GuardedBy("txLock")
private ListenableFuture<Void> lastSubmittedFuture;
+ private boolean initCommit;
+
public TransactionChainManagerStatus getTransactionChainManagerStatus() {
return transactionChainManagerStatus;
}
@GuardedBy("txLock")
private TransactionChainManagerStatus transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
- TransactionChainManager(@Nonnull final DataBroker dataBroker, @Nonnull final DeviceState deviceState) {
+ TransactionChainManager(@Nonnull final DataBroker dataBroker,
+ @Nonnull final DeviceState deviceState,
+ @Nonnull final LifecycleConductor conductor) {
this.dataBroker = Preconditions.checkNotNull(dataBroker);
+ this.conductor = Preconditions.checkNotNull(conductor);
this.nodeII = Preconditions.checkNotNull(deviceState.getNodeInstanceIdentifier());
this.transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
lastSubmittedFuture = Futures.immediateFuture(null);
Preconditions.checkState(wTx == null, "We have some unexpected WriteTransaction.");
this.transactionChainManagerStatus = TransactionChainManagerStatus.WORKING;
this.submitIsEnabled = false;
+ this.initCommit = true;
createTxChain();
} else {
LOG.debug("Transaction is active {}", nodeId());
final ListenableFuture<Void> future;
synchronized (txLock) {
if (TransactionChainManagerStatus.WORKING.equals(transactionChainManagerStatus)) {
- LOG.debug("Submitting all transactions if we were in status WORKING for Node", nodeId());
+ LOG.debug("Submitting all transactions if we were in status WORKING for Node {}", nodeId());
transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
future = txChainShuttingDown();
Preconditions.checkState(wTx == null, "We have some unexpected WriteTransaction.");
Futures.addCallback(submitFuture, new FutureCallback<Void>() {
@Override
public void onSuccess(final Void result) {
- //no action required
+ if (initCommit) {
+ initCommit = false;
+ }
}
@Override
} else {
LOG.error("Exception during transaction submitting. {}", t);
}
+ if (initCommit) {
+ LOG.error("Initial commit failed. {}", t);
+ conductor.closeConnection(nodeId());
+ }
}
});
lastSubmittedFuture = submitFuture;
}
<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.");
public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
final AsyncTransaction<?, ?> transaction, final Throwable cause) {
if (transactionChainManagerStatus.equals(TransactionChainManagerStatus.WORKING)) {
- LOG.warn("txChain failed -> recreating", cause);
+ LOG.warn("txChain failed -> recreating due to {}", cause);
recreateTxChain();
}
}
@Override
public void close() {
- LOG.debug("Setting transactionChainManagerStatus to SHUTTING_DOWN, will wait for ownershipservice to notify", nodeII);
+ LOG.debug("Setting transactionChainManagerStatus to SHUTTING_DOWN for {}, will wait for ownershipservice to notify"
+ , nodeII);
Preconditions.checkState(TransactionChainManagerStatus.SHUTTING_DOWN.equals(transactionChainManagerStatus));
Preconditions.checkState(wTx == null);
synchronized (txLock) {
@Override
public void onEchoRequestMessage(final EchoRequestMessage echoRequestMessage) {
- LOG.debug("echo request received: {}", echoRequestMessage.getXid());
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("echo request received: {}", echoRequestMessage.getXid());
+ }
final EchoReplyInputBuilder builder = new EchoReplyInputBuilder();
builder.setVersion(echoRequestMessage.getVersion());
builder.setXid(echoRequestMessage.getXid());
@Override
public void onMultipartReplyMessage(final MultipartReplyMessage notification) {
- LOG.trace("Multipart Reply with XID: {}", notification.getXid());
+ if(LOG.isTraceEnabled()) {
+ LOG.trace("Multipart Reply with XID: {}", notification.getXid());
+ }
// multiMsgCollector.addMultipartMsg(notification);
}
});
}
-
- private ListenableFuture<RpcResult<SetRoleOutput>> sendRoleChangeToDevice(final OfpRole newRole, final RoleContext roleContext) {
+ @VisibleForTesting
+ ListenableFuture<RpcResult<SetRoleOutput>> sendRoleChangeToDevice(final OfpRole newRole, final RoleContext roleContext) {
LOG.debug("Sending new role {} to device {}", newRole, roleContext.getNodeId());
final Future<RpcResult<SetRoleOutput>> setRoleOutputFuture;
final Short version = conductor.gainVersionSafely(roleContext.getNodeId());
return contexts.get(nodeId);
}
+ /**
+ * This method is only for testing
+ */
+ @VisibleForTesting
+ void setRoleContext(NodeId nodeId, RoleContext roleContext){
+ if(!contexts.containsKey(nodeId)) {
+ contexts.put(nodeId, roleContext);
+ }
+ }
+
@Override
public void addRoleChangeListener(final RoleChangeListener roleChangeListener) {
this.listeners.add(roleChangeListener);
*/
package org.opendaylight.openflowplugin.impl.rpc;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterators;
import java.util.Iterator;
@Override
public <S extends RpcService> S lookupRpcService(final Class<S> serviceClass) {
- final RpcService rpcService = rpcRegistrations.get(serviceClass).getInstance();
+ RoutedRpcRegistration<?> registration = rpcRegistrations.get(serviceClass);
+ final RpcService rpcService = registration.getInstance();
return (S) rpcService;
}
LOG.debug("Unregistration serviceClass {} for Node {}", serviceClass, nodeInstanceIdentifier.getKey().getId());
}
}
+
+ @VisibleForTesting
+ public boolean isEmptyRpcRegistrations() {
+ return this.rpcRegistrations.isEmpty();
+ }
+
+
}
*/
package org.opendaylight.openflowplugin.impl.rpc;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.collect.Iterators;
public void setDeviceTerminationPhaseHandler(final DeviceTerminationPhaseHandler handler) {
this.deviceTerminPhaseHandler = handler;
}
+
+ /**
+ * This method is only for testing
+ */
+ @VisibleForTesting
+ void addRecordToContexts(NodeId nodeId, RpcContext rpcContexts) {
+ if(!contexts.containsKey(nodeId)) {
+ this.contexts.put(nodeId,rpcContexts);
+ }
+ }
}
@Override
protected final FutureCallback<OfHeader> createCallback(final RequestContext<List<MultipartReply>> context, final Class<?> requestType) {
- return new MultipartRequestOnTheFlyCallback(context, requestType, getDeviceContext(), getEventIdentifier());
+ return new MultipartRequestOnTheFlyCallback(context, requestType,
+ getDeviceContext().getMessageSpy(), getEventIdentifier(), getDeviceContext().getDeviceState(),
+ getDeviceContext().getDeviceFlowRegistry(), getDeviceContext());
}
import com.google.common.util.concurrent.ListenableFuture;
import java.math.BigInteger;
import javax.annotation.Nonnull;
-import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
import org.opendaylight.openflowplugin.api.openflow.device.Xid;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.EventIdentifier;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.common.RpcError;
private final BigInteger datapathId;
private final RequestContextStack requestContextStack;
private final DeviceContext deviceContext;
- private final ConnectionAdapter primaryConnectionAdapter;
private final MessageSpy messageSpy;
+ private final NodeId nodeId;
private EventIdentifier eventIdentifier;
public AbstractService(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
+ final DeviceState deviceState = deviceContext.getDeviceState();
+ final GetFeaturesOutput features = deviceState.getFeatures();
+
this.requestContextStack = requestContextStack;
this.deviceContext = deviceContext;
- final FeaturesReply features = this.deviceContext.getPrimaryConnectionContext().getFeatures();
this.datapathId = features.getDatapathId();
this.version = features.getVersion();
- this.primaryConnectionAdapter = deviceContext.getPrimaryConnectionContext().getConnectionAdapter();
this.messageSpy = deviceContext.getMessageSpy();
+ this.nodeId = deviceState.getNodeId();
}
public EventIdentifier getEventIdentifier() {
return datapathId;
}
+ public NodeId getNodeId() {
+ return nodeId;
+ }
+
public RequestContextStack getRequestContextStack() {
return requestContextStack;
}
final RequestContext<O> requestContext = requestContextStack.createRequestContext();
if (requestContext == null) {
LOG.trace("Request context refused.");
- deviceContext.getMessageSpy().spyMessage(AbstractService.class, MessageSpy.STATISTIC_GROUP.TO_SWITCH_DISREGARDED);
+ getMessageSpy().spyMessage(AbstractService.class, MessageSpy.STATISTIC_GROUP.TO_SWITCH_DISREGARDED);
return failedFuture();
}
if (requestContext.getXid() == null) {
- deviceContext.getMessageSpy().spyMessage(requestContext.getClass(), MessageSpy.STATISTIC_GROUP.TO_SWITCH_RESERVATION_REJECTED);
+ getMessageSpy().spyMessage(requestContext.getClass(), MessageSpy.STATISTIC_GROUP.TO_SWITCH_RESERVATION_REJECTED);
return RequestContextUtil.closeRequestContextWithRpcError(requestContext, "Outbound queue wasn't able to reserve XID.");
}
- messageSpy.spyMessage(requestContext.getClass(), MessageSpy.STATISTIC_GROUP.TO_SWITCH_READY_FOR_SUBMIT);
+ getMessageSpy().spyMessage(requestContext.getClass(), MessageSpy.STATISTIC_GROUP.TO_SWITCH_READY_FOR_SUBMIT);
final Xid xid = requestContext.getXid();
OfHeader request = null;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collections;
import java.util.List;
-import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
+import org.opendaylight.openflowplugin.api.openflow.device.TxFacade;
+import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.EventIdentifier;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
import org.opendaylight.openflowplugin.impl.statistics.SinglePurposeMultipartReplyTranslator;
final class MultipartRequestOnTheFlyCallback extends AbstractRequestCallback<List<MultipartReply>> {
private static final Logger LOG = LoggerFactory.getLogger(MultipartRequestOnTheFlyCallback.class);
- private final DeviceContext deviceContext;
private static final SinglePurposeMultipartReplyTranslator MULTIPART_REPLY_TRANSLATOR = new SinglePurposeMultipartReplyTranslator();
+ private final DeviceState deviceState;
+ private final DeviceFlowRegistry registry;
private boolean virgin = true;
private boolean finished = false;
private final EventIdentifier doneEventIdentifier;
+ private final TxFacade txFacade;
public MultipartRequestOnTheFlyCallback(final RequestContext<List<MultipartReply>> context,
final Class<?> requestType,
- final DeviceContext deviceContext,
- final EventIdentifier eventIdentifier) {
- super(context, requestType, deviceContext.getMessageSpy(), eventIdentifier);
- this.deviceContext = deviceContext;
+ final MessageSpy messageSpy,
+ final EventIdentifier eventIdentifier,
+ final DeviceState deviceState,
+ final DeviceFlowRegistry registry,
+ final TxFacade txFacade) {
+ super(context, requestType, messageSpy, eventIdentifier);
+
+ this.deviceState = deviceState;
+ this.registry = registry;
+ this.txFacade = txFacade;
+
//TODO: this is focused on flow stats only - need more general approach if used for more than flow stats
- doneEventIdentifier = new EventIdentifier(MultipartType.OFPMPFLOW.name(), deviceContext.getPrimaryConnectionContext().getNodeId().toString());
+ doneEventIdentifier = new EventIdentifier(MultipartType.OFPMPFLOW.name(), deviceState.getNodeId().toString());
}
public EventIdentifier getDoneEventIdentifier() {
final MultipartReply multipartReply = (MultipartReply) result;
final MultipartReply singleReply = multipartReply;
- final List<? extends DataObject> multipartDataList = MULTIPART_REPLY_TRANSLATOR.translate(deviceContext, singleReply);
+ final List<? extends DataObject> multipartDataList = MULTIPART_REPLY_TRANSLATOR.translate(
+ deviceState.getFeatures().getDatapathId(), deviceState.getFeatures().getVersion(), singleReply);
final Iterable<? extends DataObject> allMultipartData = multipartDataList;
//TODO: following part is focused on flow stats only - need more general approach if used for more than flow stats
ListenableFuture<Void> future;
if (virgin) {
- future = StatisticsGatheringUtils.deleteAllKnownFlows(deviceContext);
+ future = StatisticsGatheringUtils.deleteAllKnownFlows(deviceState, registry, txFacade);
virgin = false;
} else {
future = Futures.immediateFuture(null);
@Override
public Void apply(final Void input) {
- StatisticsGatheringUtils.writeFlowStatistics((Iterable<FlowsStatisticsUpdate>) allMultipartData,deviceContext);
+ StatisticsGatheringUtils.writeFlowStatistics((Iterable<FlowsStatisticsUpdate>) allMultipartData,
+ deviceState, registry, txFacade);
if (!multipartReply.getFlags().isOFPMPFREQMORE()) {
endCollecting();
final RpcResult<List<MultipartReply>> rpcResult = RpcResultBuilder.success(Collections.<MultipartReply>emptyList()).build();
spyMessage(MessageSpy.STATISTIC_GROUP.FROM_SWITCH_TRANSLATE_OUT_SUCCESS);
setResult(rpcResult);
- deviceContext.submitTransaction();
+ txFacade.submitTransaction();
finished = true;
}
}
}
public Future<BigInteger> getGenerationIdFromDevice(final Short version) {
- final NodeId nodeId = deviceContext.getPrimaryConnectionContext().getNodeId();
- LOG.info("getGenerationIdFromDevice called for device:{}", nodeId.getValue());
+ LOG.info("getGenerationIdFromDevice called for device:{}", getNodeId().getValue());
// send a dummy no-change role request to get the generation-id of the switch
final RoleRequestInputBuilder roleRequestInputBuilder = new RoleRequestInputBuilder();
finalFuture.set(roleRequestOutput.getGenerationId());
} else {
LOG.info("roleRequestOutput is null in getGenerationIdFromDevice");
- finalFuture.setException(new RoleChangeException("Exception in getting generationId for device:" + nodeId.getValue()));
+ finalFuture.setException(new RoleChangeException("Exception in getting generationId for device:" + getNodeId().getValue()));
}
} else {
public Future<RpcResult<SetRoleOutput>> submitRoleChange(final OfpRole ofpRole, final Short version, final BigInteger generationId) {
LOG.info("submitRoleChange called for device:{}, role:{}",
- deviceContext.getPrimaryConnectionContext().getNodeId(), ofpRole);
+ getNodeId(), ofpRole);
final RoleRequestInputBuilder roleRequestInputBuilder = new RoleRequestInputBuilder();
roleRequestInputBuilder.setRole(toOFJavaRole(ofpRole));
roleRequestInputBuilder.setVersion(version);
@Override
public void onSuccess(final RpcResult<RoleRequestOutput> roleRequestOutputRpcResult) {
LOG.info("submitRoleChange onSuccess for device:{}, role:{}",
- deviceContext.getPrimaryConnectionContext().getNodeId(), ofpRole);
+ getNodeId(), ofpRole);
final RoleRequestOutput roleRequestOutput = roleRequestOutputRpcResult.getResult();
final Collection<RpcError> rpcErrors = roleRequestOutputRpcResult.getErrors();
if (roleRequestOutput != null) {
@Override
public void onFailure(final Throwable throwable) {
LOG.error("submitRoleChange onFailure for device:{}, role:{}",
- deviceContext.getPrimaryConnectionContext().getNodeId(), ofpRole, throwable);
+ getNodeId(), ofpRole, throwable);
finalFuture.setException(throwable);
}
});
private final DeviceContext deviceContext;
private final RoleService roleService;
- private final NodeId nodeId;
- private final Short version;
private final Semaphore currentRoleGuard = new Semaphore(1, true);
super(requestContextStack, deviceContext, SetRoleOutput.class);
this.deviceContext = Preconditions.checkNotNull(deviceContext);
this.roleService = new RoleService(requestContextStack, deviceContext, RoleRequestOutput.class);
- nodeId = deviceContext.getPrimaryConnectionContext().getNodeId();
- version = deviceContext.getPrimaryConnectionContext().getFeatures().getVersion();
}
@Override
LOG.info("SetRole called with input:{}", input);
try {
currentRoleGuard.acquire();
- LOG.trace("currentRole lock queue: " + currentRoleGuard.getQueueLength());
+ LOG.trace("currentRole lock queue length: {} " + currentRoleGuard.getQueueLength());
} catch (final InterruptedException e) {
- LOG.warn("Unexpected exception for acquire semaphor for input {}", input);
+ LOG.error("Unexpected exception {} for acquire semaphor for input {}", e, input);
return RpcResultBuilder.<SetRoleOutput> failed().buildFuture();
}
// compare with last known role and set if different. If they are same, then return.
@Override
public void onFailure(final Throwable t) {
- LOG.warn("SetRoleService set Role {} for Node: {} fail.", input.getControllerRole(),
+ LOG.error("SetRoleService set Role {} for Node: {} fail . Reason {}", input.getControllerRole(),
input.getNode().getValue(), t);
currentRoleGuard.release();
}
return;
case WORKING:
// We can proceed
+ LOG.trace("Device {} has been working", input.getNode());
break;
default:
LOG.warn("Device {} is in state {}, role change is not allowed", input.getNode(), state);
currentRole = input.getControllerRole();
future.set(RpcResultBuilder.<SetRoleOutput> success().withResult(result.getResult()).build());
} else {
- LOG.info("setRole() failed with errors, will retry: {} times.", MAX_RETRIES - retryCounter);
+ LOG.error("setRole() failed with errors, will retry: {} times.", MAX_RETRIES - retryCounter);
repeaterForChangeRole(future, input, (retryCounter + 1));
}
}
@Override
public void onFailure(final Throwable t) {
- LOG.info("Exception in setRole(), will retry: {} times.", MAX_RETRIES - retryCounter, t);
+ LOG.error("Exception in setRole(), will retry: {} times.", t, MAX_RETRIES - retryCounter);
repeaterForChangeRole(future, input, (retryCounter + 1));
}
});
}
private ListenableFuture<RpcResult<SetRoleOutput>> tryToChangeRole(final OfpRole role) {
- LOG.info("RoleChangeTask called on device:{} OFPRole:{}", nodeId.getValue(), role);
+ LOG.info("RoleChangeTask called on device:{} OFPRole:{}", getNodeId().getValue(), role);
- final Future<BigInteger> generationFuture = roleService.getGenerationIdFromDevice(version);
+ final Future<BigInteger> generationFuture = roleService.getGenerationIdFromDevice(getVersion());
return Futures.transform(JdkFutureAdapters.listenInPoolThread(generationFuture), (AsyncFunction<BigInteger, RpcResult<SetRoleOutput>>) generationId -> {
- LOG.debug("RoleChangeTask, GenerationIdFromDevice from device {} is {}", nodeId.getValue(), generationId);
+ LOG.debug("RoleChangeTask, GenerationIdFromDevice from device {} is {}", getNodeId().getValue(), generationId);
final BigInteger nextGenerationId = getNextGenerationId(generationId);
- LOG.debug("nextGenerationId received from device:{} is {}", nodeId.getValue(), nextGenerationId);
- final Future<RpcResult<SetRoleOutput>> submitRoleFuture = roleService.submitRoleChange(role, version, nextGenerationId);
+ LOG.debug("nextGenerationId received from device:{} is {}", getNodeId().getValue(), nextGenerationId);
+ final Future<RpcResult<SetRoleOutput>> submitRoleFuture = roleService.submitRoleChange(role, getVersion(), nextGenerationId);
return JdkFutureAdapters.listenInPoolThread(submitRoleFuture);
});
}
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.device.TxFacade;
import org.opendaylight.openflowplugin.api.openflow.device.Xid;
import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.TableFeaturesConvertor;
import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.TableFeaturesReplyConvertor;
public final class SalTableServiceImpl extends AbstractMultipartService<UpdateTableInput> implements SalTableService {
private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(SalTableServiceImpl.class);
+ private final TxFacade txFacade;
+ private final NodeId nodeId;
- public SalTableServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
+ public SalTableServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext,
+ final NodeId nodeId) {
super(requestContextStack, deviceContext);
+ this.txFacade = deviceContext;
+ this.nodeId = nodeId;
}
@Override
@Override
public void onFailure(final Throwable t) {
- LOG.debug("Failure multipart response for table features request. Exception: {}", t);
+ LOG.error("Failure multipart response for table features request. Exception: {}", t);
finalFuture.set(RpcResultBuilder.<UpdateTableOutput>failed()
.withError(ErrorType.RPC, "Future error", t).build());
}
final List<org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures> salTableFeatures = convertToSalTableFeatures(multipartReplies);
- final DeviceContext deviceContext = getDeviceContext();
- final NodeId nodeId = deviceContext.getPrimaryConnectionContext().getNodeId();
final InstanceIdentifier<FlowCapableNode> flowCapableNodeII = InstanceIdentifier.create(Nodes.class)
- .child(Node.class, new NodeKey(nodeId)).augmentation(FlowCapableNode.class);
+ .child(Node.class, new NodeKey(getNodeId())).augmentation(FlowCapableNode.class);
for (final org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures tableFeatureData : salTableFeatures) {
final Short tableId = tableFeatureData.getTableId();
final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures, TableFeaturesKey> tableFeaturesII = flowCapableNodeII
.child(org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures.class,
new TableFeaturesKey(tableId));
- deviceContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, tableFeaturesII,
+ txFacade.writeToTransaction(LogicalDatastoreType.OPERATIONAL, tableFeaturesII,
tableFeatureData);
}
- deviceContext.submitTransaction();
+
+ txFacade.submitTransaction();
}
protected static List<org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures> convertToSalTableFeatures(
private static MeterStatsResponseConvertor meterStatsConvertor = new MeterStatsResponseConvertor();
- public List<DataObject> translate(final DeviceContext deviceContext, final OfHeader msg) {
+ public List<DataObject> translate(final BigInteger datapathId, final short version, final OfHeader msg) {
List<DataObject> listDataObject = new ArrayList<>();
- OpenflowVersion ofVersion = OpenflowVersion.get(deviceContext.getPrimaryConnectionContext().getFeatures().getVersion());
+ OpenflowVersion ofVersion = OpenflowVersion.get(version);
- final FeaturesReply features = deviceContext.getPrimaryConnectionContext().getFeatures();
if (msg instanceof MultipartReplyMessage) {
MultipartReplyMessage mpReply = (MultipartReplyMessage) msg;
- NodeId node = SinglePurposeMultipartReplyTranslator.nodeIdFromDatapathId(features.getDatapathId());
+ NodeId node = SinglePurposeMultipartReplyTranslator.nodeIdFromDatapathId(datapathId);
switch (mpReply.getType()) {
case OFPMPFLOW: {
FlowsStatisticsUpdateBuilder message = new FlowsStatisticsUpdateBuilder();
message.setTransactionId(generateTransactionId(mpReply.getXid()));
MultipartReplyFlowCase caseBody = (MultipartReplyFlowCase) mpReply.getMultipartReplyBody();
MultipartReplyFlow replyBody = caseBody.getMultipartReplyFlow();
- message.setFlowAndStatisticsMapList(flowStatsConvertor.toSALFlowStatsList(replyBody.getFlowStats(), features.getDatapathId(), ofVersion));
+ message.setFlowAndStatisticsMapList(flowStatsConvertor.toSALFlowStatsList(replyBody.getFlowStats(), datapathId, ofVersion));
listDataObject.add(message.build());
return listDataObject;
NodeConnectorStatisticsAndPortNumberMapBuilder statsBuilder =
new NodeConnectorStatisticsAndPortNumberMapBuilder();
statsBuilder.setNodeConnectorId(
- InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(features.getDatapathId(),
+ InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(datapathId,
portStats.getPortNo(), ofVersion));
BytesBuilder bytesBuilder = new BytesBuilder();
QueueIdAndStatisticsMapBuilder statsBuilder =
new QueueIdAndStatisticsMapBuilder();
statsBuilder.setNodeConnectorId(
- InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(features.getDatapathId(),
+ InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(datapathId,
queueStats.getPortNo(), ofVersion));
statsBuilder.setTransmissionErrors(new Counter64(queueStats.getTxErrors()));
statsBuilder.setTransmittedBytes(new Counter64(queueStats.getTxBytes()));
statsBuilder.setDuration(durationBuilder.build());
statsBuilder.setQueueId(new QueueId(queueStats.getQueueId()));
- statsBuilder.setNodeConnectorId(InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(features.getDatapathId(),
+ statsBuilder.setNodeConnectorId(InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(datapathId,
queueStats.getPortNo(), ofVersion));
statsMap.add(statsBuilder.build());
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) {
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.openflowplugin.api.openflow.device.TxFacade;
+import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowRegistryKey;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.EventIdentifier;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.StatisticsGatherer;
try {
for (final MultipartReply singleReply : rpcResult.getResult()) {
- final List<? extends DataObject> multipartDataList = MULTIPART_REPLY_TRANSLATOR.translate(deviceContext, singleReply);
+ final List<? extends DataObject> multipartDataList = MULTIPART_REPLY_TRANSLATOR.translate(
+ deviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId(),
+ deviceContext.getPrimaryConnectionContext().getFeatures().getVersion(), singleReply);
multipartData = multipartDataList.get(0);
allMultipartData = Iterables.concat(allMultipartData, multipartDataList);
}
}
private static void processMeterConfigStatsUpdated(final Iterable<MeterConfigStatsUpdated> data, final DeviceContext deviceContext) throws Exception {
- final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext);
+ final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext.getDeviceState());
deleteAllKnownMeters(deviceContext, fNodeIdent);
for (final MeterConfigStatsUpdated meterConfigStatsUpdated : data) {
for (final MeterConfigStats meterConfigStats : meterConfigStatsUpdated.getMeterConfigStats()) {
private static ListenableFuture<Boolean> processFlowStatistics(final Iterable<FlowsStatisticsUpdate> data,
final DeviceContext deviceContext, final EventIdentifier eventIdentifier) {
- final ListenableFuture<Void> deleFuture = deleteAllKnownFlows(deviceContext);
+ final ListenableFuture<Void> deleFuture = deleteAllKnownFlows(deviceContext.getDeviceState(),
+ deviceContext.getDeviceFlowRegistry(), deviceContext);
return Futures.transform(deleFuture, new Function<Void, Boolean>() {
@Override
public Boolean apply(final Void input) {
- writeFlowStatistics(data, deviceContext);
+ writeFlowStatistics(data, deviceContext.getDeviceState(), deviceContext.getDeviceFlowRegistry(),
+ deviceContext);
deviceContext.submitTransaction();
EventsTimeCounter.markEnd(eventIdentifier);
return Boolean.TRUE;
});
}
- public static void writeFlowStatistics(final Iterable<FlowsStatisticsUpdate> data, final DeviceContext deviceContext) {
- final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext);
+ public static void writeFlowStatistics(final Iterable<FlowsStatisticsUpdate> data,
+ final DeviceState deviceState,
+ final DeviceFlowRegistry registry,
+ final TxFacade txFacade) {
+ final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceState);
try {
for (final FlowsStatisticsUpdate flowsStatistics : data) {
for (final FlowAndStatisticsMapList flowStat : flowsStatistics.getFlowAndStatisticsMapList()) {
final short tableId = flowStat.getTableId();
final FlowRegistryKey flowRegistryKey = FlowRegistryKeyFactory.create(flowBuilder.build());
- final FlowId flowId = deviceContext.getDeviceFlowRegistry().storeIfNecessary(flowRegistryKey, tableId);
+ final FlowId flowId = registry.storeIfNecessary(flowRegistryKey, tableId);
final FlowKey flowKey = new FlowKey(flowId);
flowBuilder.setKey(flowKey);
final TableKey tableKey = new TableKey(tableId);
final InstanceIdentifier<Flow> flowIdent = fNodeIdent.child(Table.class, tableKey).child(Flow.class, flowKey);
- deviceContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, flowIdent, flowBuilder.build());
+ txFacade.writeToTransaction(LogicalDatastoreType.OPERATIONAL, flowIdent, flowBuilder.build());
}
}
} catch (Exception e) {
return flowStatisticsDataBld;
}
- public static ListenableFuture<Void> deleteAllKnownFlows(final DeviceContext deviceContext) {
+ public static ListenableFuture<Void> deleteAllKnownFlows(final DeviceState deviceState,
+ final DeviceFlowRegistry registry,
+ final TxFacade txFacade) {
/* DeviceState.deviceSynchronized is a marker for actual phase - false means initPhase, true means noInitPhase */
- if (deviceContext.getDeviceState().deviceSynchronized()) {
- final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = assembleFlowCapableNodeInstanceIdentifier(deviceContext);
- final ReadOnlyTransaction readTx = deviceContext.getReadTransaction();
+ if (deviceState.deviceSynchronized()) {
+ final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = assembleFlowCapableNodeInstanceIdentifier(deviceState);
+ final ReadOnlyTransaction readTx = txFacade.getReadTransaction();
final CheckedFuture<Optional<FlowCapableNode>, ReadFailedException> flowCapableNodeFuture = readTx.read(
LogicalDatastoreType.OPERATIONAL, flowCapableNodePath);
for (final Table tableData : flowCapNodeOpt.get().getTable()) {
final Table table = new TableBuilder(tableData).setFlow(Collections.<Flow>emptyList()).build();
final InstanceIdentifier<Table> iiToTable = flowCapableNodePath.child(Table.class, tableData.getKey());
- deviceContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, iiToTable, table);
+ txFacade.writeToTransaction(LogicalDatastoreType.OPERATIONAL, iiToTable, table);
}
}
- deviceContext.getDeviceFlowRegistry().removeMarked();
+ registry.removeMarked();
readTx.close();
return Futures.immediateFuture(null);
}
}
private static void processFlowTableStatistics(final Iterable<FlowTableStatisticsUpdate> data, final DeviceContext deviceContext) throws Exception {
- final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext);
+ final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext.getDeviceState());
for (final FlowTableStatisticsUpdate flowTableStatisticsUpdate : data) {
for (final FlowTableAndStatisticsMap tableStat : flowTableStatisticsUpdate.getFlowTableAndStatisticsMap()) {
private static void processMetersStatistics(final Iterable<MeterStatisticsUpdated> data,
final DeviceContext deviceContext) throws Exception {
- final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext);
+ final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext.getDeviceState());
for (final MeterStatisticsUpdated meterStatisticsUpdated : data) {
for (final MeterStats mStat : meterStatisticsUpdated.getMeterStats()) {
final MeterStatistics stats = new MeterStatisticsBuilder(mStat).build();
}
private static void processGroupStatistics(final Iterable<GroupStatisticsUpdated> data, final DeviceContext deviceContext) throws Exception {
- final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext);
+ final InstanceIdentifier<FlowCapableNode> fNodeIdent = assembleFlowCapableNodeInstanceIdentifier(deviceContext.getDeviceState());
for (final GroupStatisticsUpdated groupStatistics : data) {
for (final GroupStats groupStats : groupStatistics.getGroupStats()) {
deviceContext.submitTransaction();
}
- private static InstanceIdentifier<FlowCapableNode> assembleFlowCapableNodeInstanceIdentifier(final DeviceContext deviceContext) {
- return deviceContext.getDeviceState().getNodeInstanceIdentifier().augmentation(FlowCapableNode.class);
+ private static InstanceIdentifier<FlowCapableNode> assembleFlowCapableNodeInstanceIdentifier(final DeviceState deviceState) {
+ return deviceState.getNodeInstanceIdentifier().augmentation(FlowCapableNode.class);
}
/**
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);
}
- private void pollStatistics(final DeviceContext deviceContext,
+ @VisibleForTesting
+ 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) {
public final class AggregateFlowsInTableService extends AbstractCompatibleStatService<GetAggregateFlowStatisticsFromFlowTableForAllFlowsInput,
GetAggregateFlowStatisticsFromFlowTableForAllFlowsOutput, AggregateFlowStatisticsUpdate> {
- public AggregateFlowsInTableService(final RequestContextStack requestContextStack, final DeviceContext deviceContext, AtomicLong compatibilityXidSeed) {
+ final TranslatorLibrary translatorLibrary;
+
+ public static AggregateFlowsInTableService createWithOook(final RequestContextStack requestContextStack,
+ final DeviceContext deviceContext,
+ AtomicLong compatibilityXidSeed) {
+ return new AggregateFlowsInTableService(requestContextStack, deviceContext, compatibilityXidSeed, deviceContext.oook());
+ }
+
+ public AggregateFlowsInTableService(final RequestContextStack requestContextStack, final DeviceContext deviceContext,
+ AtomicLong compatibilityXidSeed, TranslatorLibrary translatorLibrary) {
super(requestContextStack, deviceContext, compatibilityXidSeed);
+
+ this.translatorLibrary = translatorLibrary;
}
@Override
final int mpSize = result.size();
Preconditions.checkArgument(mpSize == 1, "unexpected (!=1) mp-reply size received: {}", mpSize);
- TranslatorLibrary translatorLibrary = getDeviceContext().oook();
MultipartReply mpReply = result.get(0);
final TranslatorKey translatorKey = new TranslatorKey(mpReply.getVersion(), MultipartReplyAggregateCase.class.getName());
final MessageTranslator<MultipartReply, AggregatedFlowStatistics> messageTranslator = translatorLibrary.lookupTranslator(translatorKey);
- final AggregatedFlowStatistics flowStatistics = messageTranslator.translate(mpReply, getDeviceContext(), null);
+ final AggregatedFlowStatistics flowStatistics = messageTranslator.translate(mpReply, getDeviceContext().getDeviceState(), null);
final AggregateFlowStatisticsUpdateBuilder notification = new AggregateFlowStatisticsUpdateBuilder(flowStatistics)
.setId(getDeviceContext().getDeviceState().getNodeId())
.setMoreReplies(Boolean.FALSE)
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());
mprAggregateRequestBuilder.setCookieMask(OFConstants.DEFAULT_COOKIE_MASK);
}
- MatchReactor.getInstance().convert(input.getMatch(), version, mprAggregateRequestBuilder,
- deviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId());
+ MatchReactor.getInstance().convert(input.getMatch(), version, mprAggregateRequestBuilder, getDatapathId());
FlowCreatorUtil.setWildcardedFlowMatch(version, mprAggregateRequestBuilder);
@Override
public RpcResult<GetAggregateFlowStatisticsFromFlowTableForGivenMatchOutput> apply(final RpcResult<List<MultipartReply>> input) {
final DeviceContext deviceContext = matchingFlowsInTable.getDeviceContext();
- TranslatorLibrary translatorLibrary = deviceContext.oook();
final RpcResult<GetAggregateFlowStatisticsFromFlowTableForGivenMatchOutput> rpcResult;
if (input.isSuccessful()) {
MultipartReply reply = input.getResult().get(0);
List<AggregatedFlowStatistics> aggregStats = new ArrayList<AggregatedFlowStatistics>();
for (MultipartReply multipartReply : input.getResult()) {
- aggregStats.add(messageTranslator.translate(multipartReply, deviceContext, null));
+ aggregStats.add(messageTranslator.translate(multipartReply, deviceContext.getDeviceState(), null));
}
GetAggregateFlowStatisticsFromFlowTableForGivenMatchOutputBuilder getAggregateFlowStatisticsFromFlowTableForGivenMatchOutputBuilder =
};
private final MatchingFlowsInTableService matchingFlowsInTable;
+ private final TranslatorLibrary translatorLibrary;
private OpendaylightFlowStatisticsService delegate;
- public OpendaylightFlowStatisticsServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext) {
+ public static OpendaylightFlowStatisticsServiceImpl createWithOook(final RequestContextStack requestContextStack,
+ final DeviceContext deviceContext) {
+ return new OpendaylightFlowStatisticsServiceImpl(requestContextStack, deviceContext, deviceContext.oook());
+ }
+
+ public OpendaylightFlowStatisticsServiceImpl(final RequestContextStack requestContextStack, final DeviceContext deviceContext,
+ final TranslatorLibrary translatorLibrary) {
matchingFlowsInTable = new MatchingFlowsInTableService(requestContextStack, deviceContext);
+ this.translatorLibrary = translatorLibrary;
}
@Override
public AbstractCompatibleStatService(RequestContextStack requestContextStack, DeviceContext deviceContext, AtomicLong compatibilityXidSeed) {
super(requestContextStack, deviceContext);
this.compatibilityXidSeed = compatibilityXidSeed;
- ofVersion = OpenflowVersion.get(getDeviceContext().getDeviceState().getVersion());
+ ofVersion = OpenflowVersion.get(getVersion());
}
public OpenflowVersion getOfVersion() {
final NotificationPublishService notificationService,
final AtomicLong compatibilityXidSeed) {
this.notificationService = notificationService;
- aggregateFlowsInTable = new AggregateFlowsInTableService(requestContextStack, deviceContext, compatibilityXidSeed);
+ aggregateFlowsInTable = AggregateFlowsInTableService.createWithOook(requestContextStack, deviceContext, compatibilityXidSeed);
allFlowsInAllTables = new AllFlowsInAllTablesService(requestContextStack, deviceContext, compatibilityXidSeed);
allFlowsInTable = new AllFlowsInTableService(requestContextStack, deviceContext, compatibilityXidSeed);
flowsInTable = new FlowsInTableService(requestContextStack, deviceContext, compatibilityXidSeed);
@Override
public Future<RpcResult<List<MultipartReply>>> getStatisticsOfType(final EventIdentifier eventIdentifier, final MultipartType type) {
- LOG.debug("Getting statistics (onTheFly) for node {} of type {}", getDeviceContext().getDeviceState().getNodeId(), type);
+ LOG.debug("Getting statistics (onTheFly) for node {} of type {}", getNodeId(), type);
EventsTimeCounter.markStart(eventIdentifier);
setEventIdentifier(eventIdentifier);
return handleServiceCall(type);
@Override
public Future<RpcResult<List<MultipartReply>>> getStatisticsOfType(final EventIdentifier eventIdentifier, final MultipartType type) {
- LOG.debug("Getting statistics for node {} of type {}", getDeviceContext().getDeviceState().getNodeId(), type);
+ LOG.debug("Getting statistics for node {} of type {}", getNodeId(), type);
EventsTimeCounter.markStart(eventIdentifier);
setEventIdentifier(eventIdentifier);
return handleServiceCall(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 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
package org.opendaylight.openflowplugin.impl.translator;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.MessageTranslator;
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;
*/
public class AggregatedFlowStatisticsTranslator implements MessageTranslator<MultipartReply, AggregatedFlowStatistics> {
@Override
- public AggregatedFlowStatistics translate(final MultipartReply input, final DeviceContext deviceContext, final Object connectionDistinguisher) {
+ public AggregatedFlowStatistics translate(final MultipartReply input, final DeviceState deviceState, final Object connectionDistinguisher) {
AggregatedFlowStatisticsBuilder aggregatedFlowStatisticsBuilder = new AggregatedFlowStatisticsBuilder();
MultipartReplyAggregateCase caseBody = (MultipartReplyAggregateCase)input.getMultipartReplyBody();
package org.opendaylight.openflowplugin.impl.translator;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.MessageTranslator;
import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchConvertorImpl;
public class FlowRemovedTranslator implements MessageTranslator<FlowRemoved, org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved> {
@Override
- public org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved translate(FlowRemoved input, DeviceContext deviceContext, Object connectionDistinguisher) {
+ public org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved translate(FlowRemoved input, DeviceState deviceState, Object connectionDistinguisher) {
FlowRemovedBuilder flowRemovedBld = new FlowRemovedBuilder()
- .setMatch(translateMatch(input, deviceContext).build())
+ .setMatch(translateMatch(input, deviceState).build())
.setCookie(new FlowCookie(input.getCookie()))
- .setNode(new NodeRef(deviceContext.getDeviceState().getNodeInstanceIdentifier()))
+ .setNode(new NodeRef(deviceState.getNodeInstanceIdentifier()))
.setPriority(input.getPriority())
.setTableId(input.getTableId().getValue().shortValue());
return flowRemovedBld.build();
}
- protected MatchBuilder translateMatch(FlowRemoved flowRemoved, DeviceContext deviceContext) {
+ protected MatchBuilder translateMatch(FlowRemoved flowRemoved, DeviceState deviceState) {
return MatchConvertorImpl.fromOFMatchToSALMatch(flowRemoved.getMatch(),
- deviceContext.getDeviceState().getFeatures().getDatapathId(), OpenflowVersion.OF13);
+ deviceState.getFeatures().getDatapathId(), OpenflowVersion.OF13);
}
}
package org.opendaylight.openflowplugin.impl.translator;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchConvertorImpl;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
*/
public class FlowRemovedV10Translator extends FlowRemovedTranslator {
- protected MatchBuilder translateMatch(FlowRemoved flowRemoved, DeviceContext deviceContext) {
+ @Override
+ protected MatchBuilder translateMatch(FlowRemoved flowRemoved, DeviceState deviceState) {
return MatchConvertorImpl.fromOFMatchV10ToSALMatch(flowRemoved.getMatchV10(),
- deviceContext.getDeviceState().getFeatures().getDatapathId(), OpenflowVersion.OF10);
+ deviceState.getFeatures().getDatapathId(), OpenflowVersion.OF10);
}
}
import java.math.BigInteger;
import java.util.List;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.MessageTranslator;
import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
import org.opendaylight.openflowplugin.extension.api.AugmentTuple;
*/
public class PacketReceivedTranslator implements MessageTranslator<PacketInMessage, PacketReceived> {
@Override
- public PacketReceived translate(final PacketInMessage input, final DeviceContext deviceContext, final Object connectionDistinguisher) {
+ public PacketReceived translate(final PacketInMessage input, final DeviceState deviceState, final Object connectionDistinguisher) {
PacketReceivedBuilder packetReceivedBuilder = new PacketReceivedBuilder();
- BigInteger datapathId = deviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId();
+ BigInteger datapathId = deviceState.getFeatures().getDatapathId();
// TODO: connection cookie from connection distinguisher
// packetReceivedBuilder.setConnectionCookie(new ConnectionCookie(input.getCookie().longValue()));
}
// Try to create the NodeConnectorRef
- BigInteger dataPathId = deviceContext.getDeviceState().getFeatures().getDatapathId();
+ BigInteger dataPathId = deviceState.getFeatures().getDatapathId();
NodeConnectorRef nodeConnectorRef = NodeConnectorRefToPortTranslator.toNodeConnectorRef(input, dataPathId);
// If we was able to create NodeConnectorRef, use it
import java.util.Collections;
import org.opendaylight.openflowplugin.api.OFConstants;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.MessageTranslator;
import org.opendaylight.openflowplugin.openflow.md.util.PortTranslatorUtil;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
@Override
public FlowCapableNodeConnector translate(final PortGrouping input,
- final DeviceContext deviceContext, final Object connectionDistinguisher) {
+ final DeviceState deviceState, final Object connectionDistinguisher) {
final FlowCapableNodeConnectorBuilder builder = new FlowCapableNodeConnectorBuilder();
//OF1.0
- if (deviceContext.getDeviceState().getVersion() == OFConstants.OFP_VERSION_1_0) {
+ if (deviceState.getVersion() == OFConstants.OFP_VERSION_1_0) {
builder.setAdvertisedFeatures(PortTranslatorUtil.translatePortFeatures(input.getAdvertisedFeaturesV10()));
builder.setConfiguration(PortTranslatorUtil.translatePortConfig(input.getConfigV10()));
builder.setCurrentFeature(PortTranslatorUtil.translatePortFeatures(input.getCurrentFeaturesV10()));
builder.setPeerFeatures(PortTranslatorUtil.translatePortFeatures(input.getPeerFeaturesV10()));
builder.setState(PortTranslatorUtil.translatePortState(input.getStateV10()));
builder.setSupported(PortTranslatorUtil.translatePortFeatures(input.getSupportedFeaturesV10()));
- } else if (deviceContext.getDeviceState().getVersion() == OFConstants.OFP_VERSION_1_3) {
+ } else if (deviceState.getVersion() == OFConstants.OFP_VERSION_1_3) {
builder.setAdvertisedFeatures(PortTranslatorUtil.translatePortFeatures(input.getAdvertisedFeatures()));
builder.setConfiguration(PortTranslatorUtil.translatePortConfig(input.getConfig()));
builder.setCurrentFeature(PortTranslatorUtil.translatePortFeatures(input.getCurrentFeatures()));
final BigInteger dataPathId = deviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId();
for (final PortGrouping port : connectionContext.getFeatures().getPhyPort()) {
- final FlowCapableNodeConnector fcNodeConnector = translator.translate(port, deviceContext, null);
+ final FlowCapableNodeConnector fcNodeConnector = translator.translate(port, deviceContext.getDeviceState(), null);
final NodeConnectorId nodeConnectorId = NodeStaticReplyTranslatorUtil.nodeConnectorId(
dataPathId.toString(), port.getPortNo(), ofVersion);
final TranslatorKey translatorKey = new TranslatorKey(ofVersion, PortGrouping.class.getName());
final MessageTranslator<PortGrouping, FlowCapableNodeConnector> translator = dContext.oook()
.lookupTranslator(translatorKey);
- final FlowCapableNodeConnector fcNodeConnector = translator.translate(port, dContext, null);
+ final FlowCapableNodeConnector fcNodeConnector = translator.translate(port, dContext.getDeviceState(), null);
final BigInteger dataPathId = dContext.getPrimaryConnectionContext().getFeatures()
.getDatapathId();
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(FlowCapableTransactionService.class, flowCapableTransactionService);
rpcContext.registerRpcServiceImplementation(SalMeterService.class, salMeterService);
rpcContext.registerRpcServiceImplementation(SalGroupService.class, salGroupService);
- rpcContext.registerRpcServiceImplementation(SalTableService.class, new SalTableServiceImpl(rpcContext, deviceContext));
+ rpcContext.registerRpcServiceImplementation(SalTableService.class, new SalTableServiceImpl(rpcContext, deviceContext, deviceContext.getPrimaryConnectionContext().getNodeId()));
rpcContext.registerRpcServiceImplementation(SalPortService.class, new SalPortServiceImpl(rpcContext, deviceContext));
rpcContext.registerRpcServiceImplementation(PacketProcessingService.class, new PacketProcessingServiceImpl(rpcContext, deviceContext));
rpcContext.registerRpcServiceImplementation(NodeConfigService.class, new NodeConfigServiceImpl(rpcContext, deviceContext));
- rpcContext.registerRpcServiceImplementation(OpendaylightFlowStatisticsService.class, new OpendaylightFlowStatisticsServiceImpl(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),
rpcContext.unregisterRpcServiceImplementation(SalFlatBatchService.class);
// TODO: experimenter symmetric and multipart message services
rpcContext.unregisterRpcServiceImplementation(SalExperimenterMessageService.class);
+ rpcContext.unregisterRpcServiceImplementation(OpendaylightDirectStatisticsService.class);
}
/**
--- /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;
+
+import static org.junit.Assert.assertEquals;
+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 io.netty.util.HashedWheelTimer;
+import io.netty.util.TimerTask;
+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.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+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;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LifecycleConductorImplTest {
+
+ private LifecycleConductorImpl lifecycleConductor;
+
+ @Mock
+ private MessageIntelligenceAgency messageIntelligenceAgency;
+ @Mock
+ private ServiceChangeListener serviceChangeListener;
+ @Mock
+ private ConcurrentHashMap<NodeId, ServiceChangeListener> serviceChangeListeners;
+ @Mock
+ private DeviceContext deviceContext;
+ @Mock
+ private DeviceManager deviceManager;
+ @Mock
+ private DeviceState deviceState;
+ @Mock
+ private ConnectionContext connectionContext;
+ @Mock
+ private FeaturesReply featuresReply;
+ @Mock
+ private TimerTask timerTask;
+ @Mock
+ private TimeUnit timeUnit;
+ @Mock
+ 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;
+ private long delay = 42;
+
+ @Before
+ public void setUp() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
+ when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
+
+ lifecycleConductor = new LifecycleConductorImpl(messageIntelligenceAgency);
+ lifecycleConductor.setSafelyDeviceManager(deviceManager);
+ lifecycleConductor.setSafelyStatisticsManager(statisticsManager);
+
+ when(connectionContext.getFeatures()).thenReturn(featuresReply);
+ }
+
+
+
+ @Test
+ public void addOneTimeListenerWhenServicesChangesDoneTest() {
+ lifecycleConductor.addOneTimeListenerWhenServicesChangesDone(serviceChangeListener, nodeId);
+ assertEquals(false,lifecycleConductor.isServiceChangeListenersEmpty());
+ }
+
+
+ /**
+ * If serviceChangeListeners is empty NOTHING should happen
+ */
+ @Test
+ public void notifyServiceChangeListenersTest1() {
+ lifecycleConductor.notifyServiceChangeListeners(nodeId,true);
+ when(serviceChangeListeners.size()).thenReturn(0);
+ verify(serviceChangeListeners,times(0)).remove(nodeId);
+ }
+
+ /**
+ * If serviceChangeListeners is NOT empty remove(nodeID) should be removed
+ */
+ @Test
+ public void notifyServiceChangeListenersTest2() {
+ lifecycleConductor.addOneTimeListenerWhenServicesChangesDone(serviceChangeListener, nodeId);
+ assertEquals(false,lifecycleConductor.isServiceChangeListenersEmpty());
+ lifecycleConductor.notifyServiceChangeListeners(nodeId,true);
+ assertEquals(true,lifecycleConductor.isServiceChangeListenersEmpty());
+ }
+
+
+ /**
+ * When success flag is set to FALSE nodeID connection should be closed
+ */
+ @Test
+ public void roleInitializationDoneTest1() {
+ lifecycleConductor.addOneTimeListenerWhenServicesChangesDone(serviceChangeListener, nodeId);
+ lifecycleConductor.roleInitializationDone(nodeId,false);
+ verify(deviceContext,times(1)).shutdownConnection();
+ }
+
+ /**
+ * When success flag is set to TRUE LOG should be printed
+ */
+ @Test
+ public void roleInitializationDoneTest2() {
+ lifecycleConductor.addOneTimeListenerWhenServicesChangesDone(serviceChangeListener, nodeId);
+ lifecycleConductor.roleInitializationDone(nodeId,true);
+ verify(deviceContext,times(0)).shutdownConnection();
+ }
+
+ /**
+ * When getDeviceContext returns null nothing should happen
+ */
+ @Test
+ public void roleChangeOnDeviceTest1() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(null);
+ lifecycleConductor.roleChangeOnDevice(nodeId,true,ofpRole,false);
+ verify(deviceContext,times(0)).shutdownConnection();
+ lifecycleConductor.roleChangeOnDevice(nodeId,false,ofpRole,false);
+ verify(deviceContext,times(0)).shutdownConnection();
+ }
+
+ /**
+ * When success flag is set to FALSE connection should be closed
+ */
+ @Test
+ public void roleChangeOnDeviceTest2() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
+ lifecycleConductor.roleChangeOnDevice(nodeId,false,ofpRole,false);
+ verify(deviceContext,times(1)).shutdownConnection();
+ }
+
+ /**
+ * When success flag is set to TRUE and initializationPahse flag is set to TRUE starting
+ * device should be skipped
+ */
+ @Test
+ public void roleChangeOnDeviceTest3() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
+ lifecycleConductor.roleChangeOnDevice(nodeId,true,ofpRole,true);
+ verify(deviceContext,times(0)).shutdownConnection();
+ }
+
+ /**
+ * When OfpRole == BECOMEMASTER setRole(OfpRole.BECOMEMASTER) should be called
+ */
+ @Test
+ public void roleChangeOnDeviceTest4() {
+ when(deviceContext.getDeviceState()).thenReturn(deviceState);
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
+ when(deviceContext.onClusterRoleChange(null, OfpRole.BECOMEMASTER)).thenReturn(listenableFuture);
+ lifecycleConductor.roleChangeOnDevice(nodeId,true,OfpRole.BECOMEMASTER,false);
+ verify(statisticsManager).startScheduling(nodeId);
+ }
+
+ /**
+ * When OfpRole != BECOMEMASTER setRole(OfpRole.ECOMESLAVE) should be called
+ */
+ @Test
+ public void roleChangeOnDeviceTest5() {
+ when(deviceContext.getDeviceState()).thenReturn(deviceState);
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
+ when(deviceContext.onClusterRoleChange(null, OfpRole.BECOMESLAVE)).thenReturn(listenableFuture);
+ lifecycleConductor.roleChangeOnDevice(nodeId,true,OfpRole.BECOMESLAVE,false);
+ verify(statisticsManager).stopScheduling(nodeId);
+ }
+
+ /**
+ * If getDeviceContext returns null nothing should happen
+ */
+ @Test
+ public void gainVersionSafelyTest1() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(null);
+ assertNull(lifecycleConductor.gainVersionSafely(nodeId));
+ }
+
+ /**
+ * If getDeviceContext returns deviceContext getPrimaryConnectionContext() should be called
+ */
+ @Test
+ public void gainVersionSafelyTest2() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
+ lifecycleConductor.gainVersionSafely(nodeId);
+ verify(deviceContext,times(1)).getPrimaryConnectionContext();
+ }
+
+ /**
+ * If getDeviceContext return null then null should be returned
+ */
+ @Test
+ public void gainConnectionStateSafelyTest1() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(null);
+ assertNull(lifecycleConductor.gainConnectionStateSafely(nodeId));
+ }
+
+ /**
+ * If getDeviceContext return deviceContext then getPrimaryConnectionContext should be called
+ */
+ @Test
+ public void gainConnectionStateSafelyTest2() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
+ lifecycleConductor.gainConnectionStateSafely(nodeId);
+ verify(deviceContext,times(1)).getPrimaryConnectionContext();
+ }
+
+ /**
+ * If getDeviceContext returns null then null should be returned
+ */
+ @Test
+ public void reserveXidForDeviceMessageTest1() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(null);
+ assertNull(lifecycleConductor.reserveXidForDeviceMessage(nodeId));
+ }
+
+ /**
+ * If getDeviceContext returns deviceContext reserveXidForDeviceMessage() should be called
+ */
+ @Test
+ public void reserveXidForDeviceMessageTest2() {
+ when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
+ lifecycleConductor.reserveXidForDeviceMessage(nodeId);
+ verify(deviceContext,times(1)).reserveXidForDeviceMessage();
+ }
+
+ /**
+ * When succes flag is set to FALSE connection should be closed
+ */
+ @Test
+ public void deviceStartInitializationDoneTest() {
+ lifecycleConductor.deviceStartInitializationDone(nodeId, false);
+ verify(deviceContext,times(1)).shutdownConnection();
+ }
+
+ /**
+ * When succes flag is set to FALSE connection should be closed
+ */
+ @Test
+ public void deviceInitializationDoneTest() {
+ lifecycleConductor.deviceInitializationDone(nodeId, false);
+ verify(deviceContext,times(1)).shutdownConnection();
+ }
+}
--- /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.impl;
+
+import static org.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import org.junit.After;
+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.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListenerRegistration;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.sm.control.rev150812.StatisticsManagerControlService;
+
+@RunWith(MockitoJUnitRunner.class)
+public class OpenFlowPluginProviderImplTest {
+
+ @Mock
+ DataBroker dataBroker;
+
+ @Mock
+ RpcProviderRegistry rpcProviderRegistry;
+
+ @Mock
+ NotificationService notificationService;
+
+ @Mock
+ WriteTransaction writeTransaction;
+
+ @Mock
+ EntityOwnershipService entityOwnershipService;
+
+ @Mock
+ EntityOwnershipListenerRegistration entityOwnershipListenerRegistration;
+
+ @Mock
+ BindingAwareBroker.RpcRegistration<StatisticsManagerControlService> controlServiceRegistration;
+
+ @Mock
+ SwitchConnectionProvider switchConnectionProvider;
+
+ private static final long RPC_REQUESTS_QUOTA = 500;
+ private static final long GLOBAL_NOTIFICATION_QUOTA = 131072;
+
+ private OpenFlowPluginProviderImpl provider;
+
+ @Before
+ public void setUp() throws Exception {
+ when(dataBroker.newWriteOnlyTransaction()).thenReturn(writeTransaction);
+ when(writeTransaction.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+ when(entityOwnershipService.registerListener(any(), any())).thenReturn(entityOwnershipListenerRegistration);
+ when(rpcProviderRegistry.addRpcImplementation(eq(StatisticsManagerControlService.class), any())).thenReturn(controlServiceRegistration);
+ when(switchConnectionProvider.startup()).thenReturn(Futures.immediateCheckedFuture(null));
+
+ provider = new OpenFlowPluginProviderImpl(RPC_REQUESTS_QUOTA, GLOBAL_NOTIFICATION_QUOTA);
+ provider.setDataBroker(dataBroker);
+ provider.setRpcProviderRegistry(rpcProviderRegistry);
+ provider.setNotificationProviderService(notificationService);
+ provider.setEntityOwnershipService(entityOwnershipService);
+ provider.setSwitchConnectionProviders(Lists.newArrayList(switchConnectionProvider));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ @Test
+ public void testInitializeAndClose() throws Exception {
+ provider.initialize();
+ verify(switchConnectionProvider).startup();
+
+ provider.close();
+ verify(entityOwnershipListenerRegistration, times(2)).close();
+ }
+}
\ No newline at end of file
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+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;
+
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.CheckedFuture;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
+import org.opendaylight.openflowjava.protocol.api.keys.MessageTypeKey;
import org.opendaylight.openflowplugin.api.OFConstants;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
import org.opendaylight.openflowplugin.api.openflow.connection.OutboundQueueProvider;
import org.opendaylight.openflowplugin.api.openflow.registry.group.DeviceGroupRegistry;
import org.opendaylight.openflowplugin.api.openflow.registry.meter.DeviceMeterRegistry;
import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleSource;
+import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
+import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageIntelligenceAgency;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
+import org.opendaylight.openflowplugin.extension.api.ConvertorMessageFromOFJava;
+import org.opendaylight.openflowplugin.extension.api.core.extension.ExtensionConverterProvider;
import org.opendaylight.openflowplugin.impl.registry.flow.FlowDescriptorFactory;
import org.opendaylight.openflowplugin.impl.registry.flow.FlowRegistryKeyFactory;
import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
import org.opendaylight.openflowplugin.openflow.md.util.OpenflowPortsUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.experimenter.message.service.rev151020.ExperimenterMessageFromDev;
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.FlowId;
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.common.types.rev130731.Capabilities;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.PortReason;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.*;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.Error;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemovedMessageBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetAsyncReply;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
-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.PacketIn;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PacketInMessage;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PortGrouping;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PortStatusMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.experimenter.core.ExperimenterDataOfChoice;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
@Mock
DeviceState deviceState;
@Mock
+ GetFeaturesOutput featuresOutput;
+ @Mock
DataBroker dataBroker;
@Mock
WriteTransaction wTx;
private final AtomicLong atomicLong = new AtomicLong(0);
+ private DeviceContext deviceContextSpy;
+
@Before
public void setUp() {
final CheckedFuture<Optional<Node>, ReadFailedException> noExistNodeFuture = Futures.immediateCheckedFuture(Optional.<Node>absent());
Mockito.when(dataBroker.newReadOnlyTransaction()).thenReturn(rTx);
Mockito.when(connectionContext.getOutboundQueueProvider()).thenReturn(outboundQueueProvider);
Mockito.when(connectionContext.getConnectionAdapter()).thenReturn(connectionAdapter);
+ final FeaturesReply mockedFeaturesReply = mock(FeaturesReply.class);
+ when(connectionContext.getFeatures()).thenReturn(mockedFeaturesReply);
+ when(connectionContext.getFeatures().getCapabilities()).thenReturn(mock(Capabilities.class));
Mockito.when(deviceState.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
- Mockito.when(messageTranslatorPacketReceived.translate(any(Object.class), any(DeviceContext.class), any(Object.class))).thenReturn(mock(PacketReceived.class));
- Mockito.when(messageTranslatorFlowCapableNodeConnector.translate(any(Object.class), any(DeviceContext.class), any(Object.class))).thenReturn(mock(FlowCapableNodeConnector.class));
+ Mockito.when(featuresOutput.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
+ Mockito.when(featuresOutput.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
+ Mockito.when(deviceState.getFeatures()).thenReturn(featuresOutput);
+ Mockito.when(messageTranslatorPacketReceived.translate(any(Object.class), any(DeviceState.class), any(Object.class))).thenReturn(mock(PacketReceived.class));
+ Mockito.when(messageTranslatorFlowCapableNodeConnector.translate(any(Object.class), any(DeviceState.class), any(Object.class))).thenReturn(mock(FlowCapableNodeConnector.class));
Mockito.when(translatorLibrary.lookupTranslator(eq(new TranslatorKey(OFConstants.OFP_VERSION_1_3, PacketIn.class.getName())))).thenReturn(messageTranslatorPacketReceived);
Mockito.when(translatorLibrary.lookupTranslator(eq(new TranslatorKey(OFConstants.OFP_VERSION_1_3, PortGrouping.class.getName())))).thenReturn(messageTranslatorFlowCapableNodeConnector);
Mockito.when(translatorLibrary.lookupTranslator(eq(new TranslatorKey(OFConstants.OFP_VERSION_1_3,
org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemoved.class.getName()))))
.thenReturn(messageTranslatorFlowRemoved);
+ Mockito.when(lifecycleConductor.getMessageIntelligenceAgency()).thenReturn(messageIntelligenceAgency);
+
+ deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker, lifecycleConductor, outboundQueueProvider, translatorLibrary, false);
- deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, false);
+ deviceContextSpy = Mockito.spy(deviceContext);
xid = new Xid(atomicLong.incrementAndGet());
xidMulti = new Xid(atomicLong.incrementAndGet());
@Test(expected = NullPointerException.class)
public void testDeviceContextImplConstructorNullDataBroker() throws Exception {
- new DeviceContextImpl(connectionContext, deviceState, null, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, false).close();
+ new DeviceContextImpl(connectionContext, deviceState, null, lifecycleConductor, outboundQueueProvider, translatorLibrary, false).close();
}
@Test(expected = NullPointerException.class)
public void testDeviceContextImplConstructorNullDeviceState() throws Exception {
- new DeviceContextImpl(connectionContext, null, dataBroker, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, false).close();
+ new DeviceContextImpl(connectionContext, null, dataBroker, lifecycleConductor, outboundQueueProvider, translatorLibrary, false).close();
}
@Test(expected = NullPointerException.class)
public void testDeviceContextImplConstructorNullTimer() throws Exception {
- new DeviceContextImpl(null, deviceState, dataBroker, messageIntelligenceAgency, outboundQueueProvider, translatorLibrary, false).close();
+ new DeviceContextImpl(null, deviceState, dataBroker, lifecycleConductor, outboundQueueProvider, translatorLibrary, false).close();
}
@Test
public void testGetDeviceState() {
final DeviceState deviceSt = deviceContext.getDeviceState();
assertNotNull(deviceSt);
- Assert.assertEquals(deviceState, deviceSt);
+ assertEquals(deviceState, deviceSt);
}
@Test
public void testGetReadTransaction() {
final ReadTransaction readTx = deviceContext.getReadTransaction();
assertNotNull(readTx);
- Assert.assertEquals(rTx, readTx);
+ assertEquals(rTx, readTx);
}
/**
- * FIXME: Need to change the test on behalf the clustering transaction chain manager changes
* @throws Exception
*/
- @Ignore
@Test
public void testInitialSubmitTransaction() throws Exception {
+ Mockito.when(wTx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+ final InstanceIdentifier<Nodes> dummyII = InstanceIdentifier.create(Nodes.class);
+ deviceContext.getTransactionChainManager().activateTransactionManager() ;
+ deviceContext.getTransactionChainManager().enableSubmit();
+ deviceContext.addDeleteToTxChain(LogicalDatastoreType.CONFIGURATION, dummyII);
deviceContext.initialSubmitTransaction();
- verify(txChainManager).initialSubmitWriteTransaction();
+ verify(wTx).submit();
}
@Test
final ConnectionContext pickedConnectiobContexts = deviceContext.getAuxiliaryConnectiobContexts(DUMMY_COOKIE);
assertEquals(mockedConnectionContext, pickedConnectiobContexts);
}
+ @Test
+ public void testRemoveAuxiliaryConnectionContext() {
+ final ConnectionContext mockedConnectionContext = addDummyAuxiliaryConnectionContext();
+
+ final ConnectionAdapter mockedAuxConnectionAdapter = mock(ConnectionAdapter.class);
+ when(mockedConnectionContext.getConnectionAdapter()).thenReturn(mockedAuxConnectionAdapter);
+
+ assertNotNull(deviceContext.getAuxiliaryConnectiobContexts(DUMMY_COOKIE));
+ deviceContext.removeAuxiliaryConnectionContext(mockedConnectionContext);
+ assertNull(deviceContext.getAuxiliaryConnectiobContexts(DUMMY_COOKIE));
+ }
private ConnectionContext addDummyAuxiliaryConnectionContext() {
final ConnectionContext mockedConnectionContext = prepareConnectionContext();
}
/**
- * FIXME: Need to change the test on behalf the clustering transaction chain manager changes
* @throws Exception
*/
- @Ignore
@Test
public void testAddDeleteToTxChain() throws Exception{
final InstanceIdentifier<Nodes> dummyII = InstanceIdentifier.create(Nodes.class);
+ deviceContext.getTransactionChainManager().activateTransactionManager() ;
+ deviceContext.getTransactionChainManager().enableSubmit();
deviceContext.addDeleteToTxChain(LogicalDatastoreType.CONFIGURATION, dummyII);
- verify(txChainManager).addDeleteOperationTotTxChain(eq(LogicalDatastoreType.CONFIGURATION), eq(dummyII));
+ verify(wTx).delete(eq(LogicalDatastoreType.CONFIGURATION), eq(dummyII));
}
/**
- * FIXME: Need to change the test on behalf the clustering transaction chain manager changes
* @throws Exception
*/
- @Ignore
@Test
public void testSubmitTransaction() throws Exception {
- deviceContext.submitTransaction();
- verify(txChainManager).submitWriteTransaction();
+ deviceContext.getTransactionChainManager().activateTransactionManager() ;
+ deviceContext.getTransactionChainManager().enableSubmit();
+ assertTrue(deviceContext.submitTransaction());
}
@Test
assertNotNull(deviceMeterRegistry);
}
+ @Test
+ public void testGetRpcContext() {
+ final RpcContext rpcContext = mock(RpcContext.class);
+ deviceContext.setRpcContext(rpcContext);
+ assertNotNull(deviceContext.getRpcContext());
+ }
+
@Test
public void testProcessReply() {
final Error mockedError = mock(Error.class);
.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(DeviceContext.class), any(Object.class)))
+ Mockito.when(messageTranslatorFlowRemoved.translate(any(Object.class), any(DeviceState.class), any(Object.class)))
.thenReturn(flowRemovedMdsalBld.build());
// insert flow+flowId into local registry
.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);
}
+ @Test
+ public void testProcessExperimenterMessage() {
+ final ConvertorMessageFromOFJava mockedMessageConverter = mock(ConvertorMessageFromOFJava.class);
+ final ExtensionConverterProvider mockedExtensionConverterProvider = mock(ExtensionConverterProvider.class);
+ when(mockedExtensionConverterProvider.getMessageConverter(any(MessageTypeKey.class))).thenReturn(mockedMessageConverter);
+
+ final ExperimenterDataOfChoice mockedExperimenterDataOfChoice = mock(ExperimenterDataOfChoice.class);
+ final ExperimenterMessage experimenterMessage = new ExperimenterMessageBuilder()
+ .setExperimenterDataOfChoice(mockedExperimenterDataOfChoice).build();
+
+ final NotificationPublishService mockedNotificationPublishService = mock(NotificationPublishService.class);
+
+ deviceContext.setNotificationPublishService(mockedNotificationPublishService);
+ deviceContext.setExtensionConverterProvider(mockedExtensionConverterProvider);
+ deviceContext.processExperimenterMessage(experimenterMessage);
+
+ verify(mockedNotificationPublishService).offerNotification(any(ExperimenterMessageFromDev.class));
+ }
+
@Test
public void testOnDeviceDisconnected() throws Exception {
final DeviceTerminationPhaseHandler deviceContextClosedHandler = mock(DeviceTerminationPhaseHandler.class);
-// Mockito.verify(deviceState).setValid(false);
-// Mockito.verify(deviceContextClosedHandler).onDeviceContextClosed(deviceContext);
- Assert.assertEquals(0, deviceContext.getDeviceFlowRegistry().getAllFlowDescriptors().size());
- Assert.assertEquals(0, deviceContext.getDeviceGroupRegistry().getAllGroupIds().size());
- Assert.assertEquals(0, deviceContext.getDeviceMeterRegistry().getAllMeterIds().size());
+ assertEquals(0, deviceContext.getDeviceFlowRegistry().getAllFlowDescriptors().size());
+ assertEquals(0, deviceContext.getDeviceGroupRegistry().getAllGroupIds().size());
+ assertEquals(0, deviceContext.getDeviceMeterRegistry().getAllMeterIds().size());
}
+ @Test
+ public void testOnClusterRoleChange() throws Exception {
+ // test role.equals(oldRole)
+ Assert.assertNull(deviceContextSpy.onClusterRoleChange(OfpRole.BECOMEMASTER, OfpRole.BECOMEMASTER).get());
+
+ // test call transactionChainManager.deactivateTransactionManager()
+ Assert.assertNull(deviceContextSpy.onClusterRoleChange(OfpRole.BECOMESLAVE, OfpRole.NOCHANGE).get());
+
+ // test call MdSalRegistrationUtils.unregisterServices(rpcContext)
+ final RpcContext rpcContext = mock(RpcContext.class);
+ deviceContextSpy.setRpcContext(rpcContext);
+ Assert.assertNull(deviceContextSpy.onClusterRoleChange(OfpRole.BECOMESLAVE, OfpRole.NOCHANGE).get());
+
+ final StatisticsContext statisticsContext = mock(StatisticsContext.class);
+ deviceContextSpy.setStatisticsContext(statisticsContext);
+
+ deviceContextSpy.onClusterRoleChange(OfpRole.NOCHANGE, OfpRole.BECOMEMASTER);
+ verify(deviceContextSpy).onDeviceTakeClusterLeadership();
+
+ Mockito.when(wTx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+ deviceContextSpy.onClusterRoleChange(OfpRole.NOCHANGE, OfpRole.BECOMESLAVE);
+ verify(deviceContextSpy).onDeviceLostClusterLeadership();
+ }
}
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.util.concurrent.Futures;
+import io.netty.util.TimerTask;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
import org.opendaylight.openflowplugin.api.openflow.md.core.TranslatorKey;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageIntelligenceAgency;
-import org.opendaylight.openflowplugin.impl.LifecycleConductorImpl;
import org.opendaylight.openflowplugin.openflow.md.util.OpenflowPortsUtil;
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.openflow.common.types.rev130731.Capabilities;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.CapabilitiesV10;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.features.reply.PhyPortBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
@RunWith(MockitoJUnitRunner.class)
public class DeviceManagerImplTest {
- private static final boolean TEST_VALUE_SWITCH_FEATURE_MANDATORY = true;
private static final long TEST_VALUE_GLOBAL_NOTIFICATION_QUOTA = 2000l;
- private static final KeyedInstanceIdentifier<Node, NodeKey> DUMMY_NODE_II = InstanceIdentifier.create(Nodes.class)
- .child(Node.class, new NodeKey(new NodeId("dummyNodeId")));
- private static final Short DUMMY_TABLE_ID = 1;
- private static final Long DUMMY_MAX_METER = 544L;
- private static final String DUMMY_DATAPATH_ID = "44";
- private static final Long DUMMY_PORT_NUMBER = 21L;
private static final int barrierCountLimit = 25600;
private static final int barrierIntervalNanos = 500;
private DeviceContextImpl mockedDeviceContext;
@Mock
private NodeId mockedNodeId;
+ @Mock
+ private LifecycleConductor lifecycleConductor;
+ @Mock
+ private MessageIntelligenceAgency messageIntelligenceAgency;
@Before
public void setUp() throws Exception {
when(mockConnectionContext.getConnectionAdapter()).thenReturn(mockedConnectionAdapter);
when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockConnectionContext);
- final Capabilities capabilitiesV13 = Mockito.mock(Capabilities.class);
- final CapabilitiesV10 capabilitiesV10 = Mockito.mock(CapabilitiesV10.class);
+ final Capabilities capabilitiesV13 = mock(Capabilities.class);
+ final CapabilitiesV10 capabilitiesV10 = mock(CapabilitiesV10.class);
when(mockFeatures.getCapabilities()).thenReturn(capabilitiesV13);
when(mockFeatures.getCapabilitiesV10()).thenReturn(capabilitiesV10);
when(mockFeatures.getDatapathId()).thenReturn(BigInteger.valueOf(21L));
+
+ when(lifecycleConductor.getMessageIntelligenceAgency()).thenReturn(messageIntelligenceAgency);
}
@Test(expected = IllegalStateException.class)
when(mockedWriteTransaction.submit()).thenReturn(mockedFuture);
- final MessageIntelligenceAgency mockedMessageIntelligenceAgency = mock(MessageIntelligenceAgency.class);
- final LifecycleConductor lifecycleConductor = new LifecycleConductorImpl(mockedMessageIntelligenceAgency);
final DeviceManagerImpl deviceManager = new DeviceManagerImpl(mockedDataBroker,
TEST_VALUE_GLOBAL_NOTIFICATION_QUOTA, false, barrierIntervalNanos, barrierCountLimit, lifecycleConductor);
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) {
order.verify(mockConnectionContext).setOutboundQueueHandleRegistration(
Mockito.<OutboundQueueHandlerRegistration<OutboundQueueProvider>>any());
order.verify(mockConnectionContext).getNodeId();
- Mockito.verify(deviceInitPhaseHandler).onDeviceContextLevelUp(Matchers.<NodeId>any());
+ verify(deviceInitPhaseHandler).onDeviceContextLevelUp(Matchers.<NodeId>any());
}
@Test
final PhyPortBuilder phyPort = new PhyPortBuilder()
.setPortNo(41L);
when(mockFeatures.getPhyPort()).thenReturn(Collections.singletonList(phyPort.build()));
- final MessageTranslator<Object, Object> mockedTranslator = Mockito.mock(MessageTranslator.class);
- when(mockedTranslator.translate(Matchers.<Object>any(), Matchers.<DeviceContext>any(), Matchers.any()))
+ final MessageTranslator<Object, Object> mockedTranslator = mock(MessageTranslator.class);
+ when(mockedTranslator.translate(Matchers.<Object>any(), Matchers.<DeviceState>any(), Matchers.any()))
.thenReturn(null);
when(translatorLibrary.lookupTranslator(Matchers.<TranslatorKey>any())).thenReturn(mockedTranslator);
order.verify(mockConnectionContext).setOutboundQueueHandleRegistration(
Mockito.<OutboundQueueHandlerRegistration<OutboundQueueProvider>>any());
order.verify(mockConnectionContext).getNodeId();
- Mockito.verify(deviceInitPhaseHandler).onDeviceContextLevelUp(Matchers.<NodeId>any());
+ verify(deviceInitPhaseHandler).onDeviceContextLevelUp(Matchers.<NodeId>any());
+ }
+
+ @Test
+ public void deviceDisconnectedTest() throws Exception {
+ final DeviceState deviceState = mock(DeviceState.class);
+
+ final DeviceManagerImpl deviceManager = prepareDeviceManager();
+ injectMockTranslatorLibrary(deviceManager);
+
+ final ConnectionContext connectionContext = buildMockConnectionContext(OFConstants.OFP_VERSION_1_3);
+ when(connectionContext.getNodeId()).thenReturn(mockedNodeId);
+
+ final DeviceContext deviceContext = mock(DeviceContext.class);
+ when(deviceContext.shuttingDownDataStoreTransactions()).thenReturn(Futures.immediateCheckedFuture(null));
+ when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
+ when(deviceContext.getDeviceState()).thenReturn(deviceState);
+
+ final ConcurrentHashMap<NodeId, DeviceContext> deviceContexts = getContextsCollection(deviceManager);
+ deviceContexts.put(mockedNodeId, deviceContext);
+
+ deviceManager.onDeviceDisconnected(connectionContext);
+
+ verify(lifecycleConductor).newTimeout(Mockito.<TimerTask>any(), Mockito.anyLong(), Mockito.<TimeUnit>any());
}
protected ConnectionContext buildMockConnectionContext(final short ofpVersion) {
@Test
public void testClose() throws Exception {
- final DeviceContext deviceContext = Mockito.mock(DeviceContext.class);
+ final DeviceContext deviceContext = mock(DeviceContext.class);
final DeviceManagerImpl deviceManager = prepareDeviceManager();
final ConcurrentHashMap<NodeId, DeviceContext> deviceContexts = getContextsCollection(deviceManager);
deviceContexts.put(mockedNodeId, deviceContext);
deviceManager.close();
- Mockito.verify(deviceContext).shutdownConnection();
- Mockito.verify(deviceContext, Mockito.never()).close();
+ verify(deviceContext).shutdownConnection();
+ verify(deviceContext, Mockito.never()).close();
}
private static ConcurrentHashMap<NodeId, DeviceContext> getContextsCollection(final DeviceManagerImpl deviceManager) throws NoSuchFieldException, IllegalAccessException {
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());
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
Registration registration;
@Mock
DeviceState deviceState;
+ @Mock
+ LifecycleConductor conductor;
@Mock
private KeyedInstanceIdentifier<Node, NodeKey> nodeKeyIdent;
nodeKeyIdent = DeviceStateUtil.createNodeInstanceIdentifier(nodeId);
Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodeKeyIdent);
Mockito.when(deviceState.getNodeId()).thenReturn(nodeId);
- txChainManager = new TransactionChainManager(dataBroker, deviceState);
+ txChainManager = new TransactionChainManager(dataBroker, deviceState, conductor);
Mockito.when(txChain.newWriteOnlyTransaction()).thenReturn(writeTx);
path = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(nodeId));
@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);
}
/**
- * FIXME: Need to change the test on behalf the clustering transaction chain manager changes
+ * test of {@link TransactionChainManager#submitWriteTransaction()}
* @throws Exception
*/
- @Ignore
@Test
public void testSubmitTransaction() throws Exception {
final Node data = new NodeBuilder().setId(nodeId).build();
- txChainManager.enableSubmit();
- txChainManager.activateTransactionManager();
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
- txChainManager.activateTransactionManager();
+ txChainManager.initialSubmitWriteTransaction();
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
+ txChainManager.submitWriteTransaction();
+
+ Mockito.verify(txChain).newWriteOnlyTransaction();
+ Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
+ Mockito.verify(writeTx).submit();
+ }
+
+ /**
+ * test of {@link TransactionChainManager#submitWriteTransaction()}: no submit, never enabled
+ * @throws Exception
+ */
+ @Test
+ public void testSubmitTransaction1() throws Exception {
+ final Node data = new NodeBuilder().setId(nodeId).build();
+ 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();
+ }
+
+ /**
+ * @throws Exception
+ */
+ @Test
+ public void testSubmitTransactionFailed() throws Exception {
+ 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, false);
+ txChainManager.submitWriteTransaction();
+
+ Mockito.verify(txChain).newWriteOnlyTransaction();
+ 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();
}
/**
- * FIXME: Need to change the test on behalf the clustering transaction chain manager changes
* @throws Exception
*/
- @Ignore
@Test
public void testOnTransactionChainFailed() throws Exception {
txChainManager.onTransactionChainFailed(transactionChain, Mockito.mock(AsyncTransaction.class), Mockito.mock(Throwable.class));
+ Mockito.verify(txChain).close();
Mockito.verify(dataBroker, Mockito.times(2)).createTransactionChain(txChainManager);
}
Mockito.verify(txChain).newWriteOnlyTransaction();
Mockito.verify(writeTx).delete(LogicalDatastoreType.CONFIGURATION, path);
}
+
+ @Test
+ public void testDeactivateTransactionChainManager() throws Exception {
+ txChainManager.deactivateTransactionManager();
+
+ Mockito.verify(txChain).close();
+ }
+
+ /**
+ * @throws Exception
+ */
+ @Test
+ 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, false);
+
+ txChainManager.deactivateTransactionManager();
+
+ Mockito.verify(txChain).newWriteOnlyTransaction();
+ 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, false);
+
+ txChainManager.shuttingDown();
+
+ Mockito.verify(txChain).newWriteOnlyTransaction();
+ Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
+ Mockito.verify(writeTx).submit();
+ }
+
+ @Test
+ public void testClose() {
+ txChainManager.shuttingDown();
+ txChainManager.close();
+ Mockito.verify(txChain).close();
+ }
}
\ No newline at end of file
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/**
- * @author Jozef Bacigal
- * Date: 4/19/16
- * Time: 12:56
- */
@RunWith(MockitoJUnitRunner.class)
public class RoleContextImplTest {
Mockito.when(entityOwnershipService.registerCandidate(txEntity)).thenReturn(entityOwnershipCandidateRegistration);
}
-// @Test
-// Run this test only if demanded because it takes 15s to run
+ //@Test
+ //Run this test only if demanded because it takes 15s to run
public void testInitializationThreads() throws Exception {
/*Setting answer which will hold the answer for 5s*/
@Test
public void testCreateRequestContext() throws Exception {
-
+ roleContext.createRequestContext();
+ Mockito.verify(conductor).reserveXidForDeviceMessage(nodeId);
}
@Test(expected = NullPointerException.class)
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceManager;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceTerminationPhaseHandler;
import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
import org.opendaylight.openflowplugin.api.openflow.role.RoleContext;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
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.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
-/**
- * Created by Jozef Bacigal
- * Date: 19.4.2016.
- * Time: 13:08
- */
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
@RunWith(MockitoJUnitRunner.class)
public class RoleManagerImplTest {
@Mock
LifecycleConductor conductor;
+ @Mock
+ DeviceState deviceState;
+
+ @Mock
+ GetFeaturesOutput featuresOutput;
+
private RoleManagerImpl roleManager;
private RoleManagerImpl roleManagerSpy;
private RoleContext roleContextSpy;
private final EntityOwnershipChange masterTxEntity = new EntityOwnershipChange(RoleManagerImpl.makeTxEntity(nodeId), false, true, true);
private final EntityOwnershipChange slaveEntity = new EntityOwnershipChange(RoleManagerImpl.makeEntity(nodeId), true, false, true);
private final EntityOwnershipChange slaveTxEntityLast = new EntityOwnershipChange(RoleManagerImpl.makeTxEntity(nodeId), true, false, false);
+ private final EntityOwnershipChange masterEntityNotOwner = new EntityOwnershipChange(RoleManagerImpl.makeEntity(nodeId), true, false, true);
private InOrder inOrder;
@Before
public void setUp() throws Exception {
CheckedFuture<Void, TransactionCommitFailedException> future = Futures.immediateCheckedFuture(null);
+ Mockito.when(deviceState.getFeatures()).thenReturn(featuresOutput);
Mockito.when(entityOwnershipService.registerListener(Mockito.anyString(), Mockito.any(EntityOwnershipListener.class))).thenReturn(entityOwnershipListenerRegistration);
Mockito.when(entityOwnershipService.registerCandidate(Mockito.any(Entity.class))).thenReturn(entityOwnershipCandidateRegistration);
Mockito.when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
+ Mockito.when(deviceContext.getDeviceState()).thenReturn(deviceState);
Mockito.when(connectionContext.getFeatures()).thenReturn(featuresReply);
Mockito.when(connectionContext.getNodeId()).thenReturn(nodeId);
Mockito.when(connectionContext.getConnectionState()).thenReturn(ConnectionContext.CONNECTION_STATE.WORKING);
roleManager.setDeviceTerminationPhaseHandler(deviceTerminationPhaseHandler);
Mockito.when(conductor.getDeviceContext(Mockito.<NodeId>any())).thenReturn(deviceContext);
roleManagerSpy = Mockito.spy(roleManager);
- Mockito.doNothing().when(roleManagerSpy).makeDeviceRoleChange(Mockito.<OfpRole>any(), Mockito.<RoleContext>any(), Mockito.anyBoolean());
roleManagerSpy.onDeviceContextLevelUp(nodeId);
roleContextSpy = Mockito.spy(roleManager.getRoleContext(nodeId));
inOrder = Mockito.inOrder(entityOwnershipListenerRegistration, roleManagerSpy, roleContextSpy);
@Test
public void testOwnershipChanged2() throws Exception {
+ Mockito.doNothing().when(roleManagerSpy).makeDeviceRoleChange(Mockito.<OfpRole>any(), Mockito.<RoleContext>any(), Mockito.anyBoolean());
roleManagerSpy.ownershipChanged(masterEntity);
roleManagerSpy.ownershipChanged(masterTxEntity);
inOrder.verify(roleManagerSpy, Mockito.calls(1)).changeOwnershipForTxEntity(Mockito.<EntityOwnershipChange>any(),Mockito.<RoleContext>any());
inOrder.verify(roleManagerSpy, Mockito.calls(1)).makeDeviceRoleChange(Mockito.<OfpRole>any(), Mockito.<RoleContext>any(), Mockito.anyBoolean());
}
+ @Test
+ public void testChangeOwnershipForTxEntity3() throws Exception {
+ Mockito.when(roleContextSpy.isTxCandidateRegistered()).thenReturn(false);
+ roleManagerSpy.changeOwnershipForTxEntity(slaveTxEntityLast, roleContextSpy);
+ verify(roleContextSpy).close();
+ verify(roleContextSpy).getNodeId();
+ verify(conductor).closeConnection(nodeId);
+ }
+
+ @Test
+ public void testChangeOwnershipForTxEntity4() throws Exception {
+ Mockito.when(roleContextSpy.isTxCandidateRegistered()).thenReturn(true);
+ roleManagerSpy.changeOwnershipForTxEntity(masterEntityNotOwner, roleContextSpy);
+ verify(roleContextSpy).close();
+ verify(conductor).closeConnection(nodeId);
+ }
+
@Test
public void testAddListener() throws Exception {
roleManager.addRoleChangeListener((new RoleChangeListener() {
roleManager.notifyListenersRoleChangeOnDevice(nodeId, true, OfpRole.BECOMEMASTER, false);
}
+ @Test
+ public void testMakeDeviceRoleChange() throws Exception{
+ roleManagerSpy.makeDeviceRoleChange(OfpRole.BECOMEMASTER, roleContextSpy, true);
+ verify(roleManagerSpy, atLeastOnce()).sendRoleChangeToDevice(Mockito.<OfpRole>any(), Mockito.<RoleContext>any());
+ verify(roleManagerSpy, atLeastOnce()).notifyListenersRoleChangeOnDevice(Mockito.<NodeId>any(), eq(true), Mockito.<OfpRole>any(), eq(true));
+ }
+ @Test
+ public void testServicesChangeDone() throws Exception {
+ final NodeId nodeId2 = NodeId.getDefaultInstance("openflow:2");
+ roleManagerSpy.setRoleContext(nodeId2, roleContextSpy);
+ roleManagerSpy.servicesChangeDone(nodeId2, true);
+ verify(roleContextSpy).unregisterCandidate(Mockito.<Entity>any());
+ }
+
+ @Test
+ public void testServicesChangeDoneContextIsNull() throws Exception {
+ final NodeId nodeId2 = NodeId.getDefaultInstance("openflow:2");
+ roleManagerSpy.setRoleContext(nodeId, roleContextSpy);
+ roleManagerSpy.servicesChangeDone(nodeId2, true);
+ verify(roleContextSpy, never()).unregisterCandidate(Mockito.<Entity>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.rpc;
+
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import org.junit.After;
+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.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
+import org.opendaylight.openflowplugin.impl.rpc.listener.ItemLifecycleListenerImpl;
+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;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ItemLifecycleListenerImplTest {
+
+ @Mock
+ private DeviceContext deviceContext;
+
+ @Mock
+ private Node node;
+
+ private KeyedInstanceIdentifier<Node, NodeKey> nodeInstanceIdentifier;
+ private ItemLifecycleListener itemLifecycleListener;
+
+
+ @Before
+ public void setUp() {
+ final NodeId nodeId = new NodeId("openflow:1");
+ nodeInstanceIdentifier = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(nodeId));
+ itemLifecycleListener = new ItemLifecycleListenerImpl(deviceContext);
+ }
+
+ @After
+ public void tearDown() {
+ verifyNoMoreInteractions(deviceContext);
+ }
+
+ @Test
+ public void testOnAdded() throws Exception {
+ itemLifecycleListener.onAdded(nodeInstanceIdentifier, node);
+ verify(deviceContext).writeToTransaction(eq(LogicalDatastoreType.OPERATIONAL), eq(nodeInstanceIdentifier), eq(node));
+ verify(deviceContext).submitTransaction();
+ }
+
+ @Test
+ public void testOnRemoved() throws Exception {
+ itemLifecycleListener.onRemoved(nodeInstanceIdentifier);
+ verify(deviceContext).addDeleteToTxChain(eq(LogicalDatastoreType.OPERATIONAL), eq(nodeInstanceIdentifier));
+ verify(deviceContext).submitTransaction();
+ }
+}
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
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.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
import org.opendaylight.openflowplugin.api.openflow.device.XidSequencer;
import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeContext;
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.binding.RpcService;
+import java.util.concurrent.Semaphore;
-/**
- * @author joe
- */
@RunWith(MockitoJUnitRunner.class)
public class RpcContextImplTest {
+ private static final int MAX_REQUESTS = 5;
+ private RpcContextImpl rpcContext;
+
+
@Mock
private BindingAwareBroker.ProviderContext rpcProviderRegistry;
@Mock
private XidSequencer xidSequencer;
@Mock
private MessageSpy messageSpy;
+ @Mock
+ private DeviceContext deviceContext;
+ @Mock
+ private BindingAwareBroker.RoutedRpcRegistration routedRpcReg;
+ @Mock
+ private NotificationPublishService notificationPublishService;
+ @Mock
+ private TestRpcService serviceInstance;
private KeyedInstanceIdentifier<Node, NodeKey> nodeInstanceIdentifier;
public void setup() {
final NodeId nodeId = new NodeId("openflow:1");
nodeInstanceIdentifier = InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(nodeId));
+
+ when(deviceContext.getDeviceState()).thenReturn(deviceState);
+ when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodeInstanceIdentifier);
+ when(deviceContext.getMessageSpy()).thenReturn(messageSpy);
+
+ rpcContext = new RpcContextImpl(rpcProviderRegistry,deviceContext, messageSpy, MAX_REQUESTS,nodeInstanceIdentifier);
+
+ when(rpcProviderRegistry.addRoutedRpcImplementation(TestRpcService.class, serviceInstance)).thenReturn(routedRpcReg);
+
}
@Test
assertNull(requestContext);
}
}
+
+ @Test
+ public void testStoreAndCloseOrFail() throws Exception {
+ try (final RpcContext rpcContext = new RpcContextImpl(rpcProviderRegistry, deviceContext, messageSpy,
+ 100, nodeInstanceIdentifier)) {
+ final RequestContext<?> requestContext = rpcContext.createRequestContext();
+ assertNotNull(requestContext);
+ requestContext.close();
+ verify(messageSpy).spyMessage(RpcContextImpl.class, MessageSpy.STATISTIC_GROUP.REQUEST_STACK_FREED);
+ }
+ }
+
+ public void testRegisterRpcServiceImplementation() {
+ rpcContext.registerRpcServiceImplementation(TestRpcService.class, serviceInstance);
+ verify(rpcProviderRegistry, Mockito.times(1)).addRoutedRpcImplementation(TestRpcService.class,serviceInstance);
+ verify(routedRpcReg,Mockito.times(1)).registerPath(NodeContext.class,nodeInstanceIdentifier);
+ assertEquals(rpcContext.isEmptyRpcRegistrations(), false);
+ }
+
+
+ @Test
+ public void testLookupRpcService() {
+ when(routedRpcReg.getInstance()).thenReturn(serviceInstance);
+ rpcContext.registerRpcServiceImplementation(TestRpcService.class, serviceInstance);
+ TestRpcService temp = rpcContext.lookupRpcService(TestRpcService.class);
+ assertEquals(serviceInstance,temp);
+ }
+
+ @Test
+ public void testClose() {
+ rpcContext.registerRpcServiceImplementation(TestRpcService.class, serviceInstance);
+ rpcContext.close();
+ assertEquals(rpcContext.isEmptyRpcRegistrations(), true);
+ }
+
+ /**
+ * When deviceContext.reserveXidForDeviceMessage returns null, null should be returned
+ * @throws InterruptedException
+ */
+ @Test
+ public void testCreateRequestContext1() throws InterruptedException {
+ when(deviceContext.reserveXidForDeviceMessage()).thenReturn(null);
+ assertEquals(rpcContext.createRequestContext(),null);
+ }
+
+ /**
+ * When deviceContext.reserveXidForDeviceMessage returns value, AbstractRequestContext should be returned
+ * @throws InterruptedException
+ */
+
+ @Test
+ public void testCreateRequestContext2() throws InterruptedException {
+ RequestContext temp = rpcContext.createRequestContext();
+ temp.close();
+ verify(messageSpy).spyMessage(RpcContextImpl.class,MessageSpy.STATISTIC_GROUP.REQUEST_STACK_FREED);
+ }
+
+ @Test
+ public void testUnregisterRpcServiceImpl() {
+ rpcContext.registerRpcServiceImplementation(TestRpcService.class, serviceInstance);
+ assertEquals(rpcContext.isEmptyRpcRegistrations(), false);
+ rpcContext.unregisterRpcServiceImplementation(TestRpcService.class);
+ assertEquals(rpcContext.isEmptyRpcRegistrations(), true);
+ }
+
+ //Stub for RpcService class
+ public class TestRpcService implements RpcService {}
}
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.DeviceManager;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceTerminationPhaseHandler;
import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegistry;
+import org.opendaylight.openflowplugin.api.openflow.rpc.RpcContext;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
-import org.opendaylight.openflowplugin.impl.LifecycleConductorImpl;
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.openflow.protocol.rev130731.FeaturesReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.RpcService;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+
@RunWith(MockitoJUnitRunner.class)
public class RpcManagerImplTest {
@Mock
private DeviceInitializationPhaseHandler deviceINitializationPhaseHandler;
@Mock
+ private DeviceTerminationPhaseHandler deviceTerminationPhaseHandler;
+ @Mock
+ private BindingAwareBroker.RoutedRpcRegistration<RpcService> routedRpcRegistration;
+ @Mock
private DeviceState deviceState;
@Mock
private MessageSpy mockMsgSpy;
@Mock
private LifecycleConductor conductor;
+ @Mock
+ private ConnectionContext connectionContext;
+ @Mock
+ private ItemLifeCycleRegistry itemLifeCycleRegistry;
+ @Mock
+ private MessageSpy messageSpy;
+ @Mock
+ private RpcContext removedContexts;
+ @Mock
+ private ConcurrentMap<NodeId, RpcContext> contexts;
@Rule
public ExpectedException expectedException = ExpectedException.none();
final NodeKey nodeKey = new NodeKey(nodeId);
rpcManager = new RpcManagerImpl(rpcProviderRegistry, QUOTA_VALUE, conductor);
rpcManager.setDeviceInitializationPhaseHandler(deviceINitializationPhaseHandler);
+
+ GetFeaturesOutput featuresOutput = new GetFeaturesOutputBuilder()
+ .setVersion(OFConstants.OFP_VERSION_1_3)
+ .build();
+
+ FeaturesReply features = featuresOutput;
+
+ Mockito.when(connectionContext.getFeatures()).thenReturn(features);
+ Mockito.when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
Mockito.when(deviceContext.getDeviceState()).thenReturn(deviceState);
- Mockito.when(deviceContext.getMessageSpy()).thenReturn(mockMsgSpy);
+ Mockito.when(deviceContext.getItemLifeCycleSourceRegistry()).thenReturn(itemLifeCycleRegistry);
+ Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodePath);
+ Mockito.when(deviceState.getFeatures()).thenReturn(featuresOutput);
+ rpcManager.setDeviceTerminationPhaseHandler(deviceTerminationPhaseHandler);
+ Mockito.when(connectionContext.getFeatures()).thenReturn(features);
+ Mockito.when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
+ Mockito.when(deviceContext.getDeviceState()).thenReturn(deviceState);
+ Mockito.when(deviceContext.getItemLifeCycleSourceRegistry()).thenReturn(itemLifeCycleRegistry);
+ Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodePath);
+ Mockito.when(deviceContext.getMessageSpy()).thenReturn(messageSpy);
Mockito.when(deviceState.getNodeId()).thenReturn(nodeKey.getId());
+ Mockito.when(rpcProviderRegistry.addRoutedRpcImplementation(
+ Matchers.<Class<RpcService>>any(), Matchers.any(RpcService.class)))
+ .thenReturn(routedRpcRegistration);
Mockito.when(conductor.getDeviceContext(Mockito.<NodeId>any())).thenReturn(deviceContext);
+ Mockito.when(contexts.remove(nodeId)).thenReturn(removedContexts);
}
@Test
public void onDeviceContextLevelUp() throws Exception {
rpcManager.onDeviceContextLevelUp(nodeId);
- Mockito.verify(conductor).getDeviceContext(Mockito.<NodeId>any());
+ verify(conductor).getDeviceContext(Mockito.<NodeId>any());
}
@Test
expectedException.expect(VerifyException.class);
rpcManager.onDeviceContextLevelUp(nodeId);
}
+
+ @Test
+ public void testOnDeviceContextLevelUpMaster() throws Exception {
+ rpcManager.onDeviceContextLevelUp(nodeId);
+ verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
+ }
+
+ @Test
+ public void testOnDeviceContextLevelUpSlave() throws Exception {
+ rpcManager.onDeviceContextLevelUp(nodeId);
+ verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
+ }
+
+ @Test
+ public void testOnDeviceContextLevelUpOther() throws Exception {
+ rpcManager.onDeviceContextLevelUp(nodeId);
+ verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
+ }
+
+ @Test
+ public void testOnDeviceContextLevelDown() throws Exception {
+ rpcManager.onDeviceContextLevelDown(deviceContext);
+ verify(deviceTerminationPhaseHandler).onDeviceContextLevelDown(deviceContext);
+ }
+
+ /**
+ * On non null context close and onDeviceContextLevelDown should be called
+ */
+ @Test
+ public void onDeviceContextLevelDown1() {
+ rpcManager.addRecordToContexts(nodeId,removedContexts);
+ rpcManager.onDeviceContextLevelDown(deviceContext);
+ verify(removedContexts,times(1)).close();
+ verify(deviceTerminationPhaseHandler,times(1)).onDeviceContextLevelDown(deviceContext);
+ }
+
+
+ /**
+ * On null context only onDeviceContextLevelDown should be called
+ */
+ @Test
+ public void onDeviceContextLevelDown2() {
+ rpcManager.onDeviceContextLevelDown(deviceContext);
+ verify(removedContexts,never()).close();
+ verify(deviceTerminationPhaseHandler,times(1)).onDeviceContextLevelDown(deviceContext);
+
+ }
+
+ @Test
+ public void close() {
+ rpcManager.addRecordToContexts(nodeId,removedContexts);
+ rpcManager.close();
+ verify(removedContexts,atLeastOnce()).close();
+ }
}
when(mockedFeaturesReply.getDatapathId()).thenReturn(BigInteger.valueOf(123L));
when(mocketGetFeaturesOutput.getTables()).thenReturn(tableId);
+ when(mocketGetFeaturesOutput.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
+ when(mocketGetFeaturesOutput.getDatapathId()).thenReturn(BigInteger.valueOf(123L));
when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedPrimaryConnection);
when(mockedDeviceState.getNodeInstanceIdentifier()).thenReturn(NODE_PATH);
when(mockedDeviceState.getFeatures()).thenReturn(mocketGetFeaturesOutput);
when(mockedDeviceState.deviceSynchronized()).thenReturn(true);
+ when(mockedDeviceState.getNodeId()).thenReturn(mockedNodeId);
+
when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
when(mockedDeviceContext.getDeviceFlowRegistry()).thenReturn(mockedFlowRegistry);
//NOOP
}
};
- multipartRequestOnTheFlyCallback = new MultipartRequestOnTheFlyCallback(dummyRequestContext, String.class, mockedDeviceContext, dummyEventIdentifier);
+ multipartRequestOnTheFlyCallback = new MultipartRequestOnTheFlyCallback(dummyRequestContext, String.class,
+ mockedDeviceContext.getMessageSpy(),dummyEventIdentifier, mockedDeviceContext.getDeviceState(),
+ mockedDeviceContext.getDeviceFlowRegistry(), mockedDeviceContext);
}
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.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
private SalFlowServiceImpl salFlowService;
@Mock
- DeviceState mockedDeviceState;
+ private DeviceState mockedDeviceState;
@Mock
private DeviceFlowRegistry deviceFlowRegistry;
+ @Mock
+ private GetFeaturesOutput mockedFeaturesOutput;
@Before
public void initialization() {
when(mockedFeatures.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
when(mockedFeatures.getVersion()).thenReturn(DUMMY_VERSION);
+ when(mockedFeaturesOutput.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
+ when(mockedFeaturesOutput.getVersion()).thenReturn(DUMMY_VERSION);
when(mockedPrimConnectionContext.getFeatures()).thenReturn(mockedFeatures);
when(mockedPrimConnectionContext.getConnectionAdapter()).thenReturn(mockedConnectionAdapter);
when(requestContext.getXid()).thenReturn(new Xid(84L));
when(requestContext.getFuture()).thenReturn(RpcResultBuilder.success().buildFuture());
- salFlowService = new SalFlowServiceImpl(mockedRequestContextStack, mockedDeviceContext);
-
-
when(mockedDeviceState.getNodeInstanceIdentifier()).thenReturn(NODE_II);
+ when(mockedDeviceState.getFeatures()).thenReturn(mockedFeaturesOutput);
when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
+
+ salFlowService = new SalFlowServiceImpl(mockedRequestContextStack, mockedDeviceContext);
}
@Test
import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
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.RequestContext;
import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
import org.opendaylight.openflowplugin.api.openflow.device.Xid;
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.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
@Mock
private RequestContext<RoleRequestOutput> mockRequestContext;
+ @Mock
+ private DeviceState mockDeviceState;
+
+ @Mock
+ private GetFeaturesOutput mockFeaturesOutput;
+
@Mock
private OutboundQueue mockOutboundQueue;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ Mockito.when(mockDeviceState.getNodeId()).thenReturn(testNodeId);
+ Mockito.when(mockDeviceState.getFeatures()).thenReturn(mockFeaturesOutput);
+ Mockito.when(mockFeaturesOutput.getVersion()).thenReturn(testVersion);
+ Mockito.when(mockDeviceContext.getDeviceState()).thenReturn(mockDeviceState);
Mockito.when(mockDeviceContext.getPrimaryConnectionContext()).thenReturn(mockConnectionContext);
Mockito.when(mockConnectionContext.getFeatures()).thenReturn(mockFeaturesReply);
Mockito.when(mockConnectionContext.getNodeId()).thenReturn(testNodeId);
.when(mockedOutboundQueue).commitEntry(
Matchers.anyLong(), Matchers.<OfHeader>any(), Matchers.<FutureCallback<OfHeader>>any());
- salTableService = new SalTableServiceImpl(mockedRequestContextStack, mockedDeviceContext);
+ salTableService = new SalTableServiceImpl(mockedRequestContextStack, mockedDeviceContext,
+ mockedDeviceContext.getPrimaryConnectionContext().getNodeId());
}
@Test
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.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
@Mock
protected FeaturesReply mockedFeatures;
@Mock
+ protected GetFeaturesOutput mockedFeaturesOutput;
+ @Mock
protected ConnectionAdapter mockedConnectionAdapter;
@Mock
protected MessageSpy mockedMessagSpy;
when(mockedFeatures.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
when(mockedFeatures.getVersion()).thenReturn(DUMMY_VERSION);
+ when(mockedFeaturesOutput.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
+ when(mockedFeaturesOutput.getVersion()).thenReturn(DUMMY_VERSION);
+
when(mockedPrimConnectionContext.getFeatures()).thenReturn(mockedFeatures);
when(mockedPrimConnectionContext.getConnectionAdapter()).thenReturn(mockedConnectionAdapter);
when(mockedPrimConnectionContext.getConnectionState()).thenReturn(ConnectionContext.CONNECTION_STATE.WORKING);
when(mockedPrimConnectionContext.getOutboundQueueProvider()).thenReturn(mockedOutboundQueue);
when(mockedDeviceState.getNodeInstanceIdentifier()).thenReturn(NODE_II);
-
+ when(mockedDeviceState.getFeatures()).thenReturn(mockedFeaturesOutput);
when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedPrimConnectionContext);
when(mockedDeviceContext.getMessageSpy()).thenReturn(mockedMessagSpy);
MultipartReplyMessage multipartReplyMessage = prepareMocks(mockedDeviceContext, prepareMultipartReplyFlow(), MultipartType.OFPMPFLOW);
- List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(mockedDeviceContext, multipartReplyMessage);
+ List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId(),
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getVersion(),
+ multipartReplyMessage);
DataObject dataObject = validateOutput(result);
assertTrue(dataObject instanceof FlowsStatisticsUpdate);
MultipartReplyMessage multipartReplyMessage = prepareMocks(mockedDeviceContext, prepareMultipartReplyAggregate(), MultipartType.OFPMPAGGREGATE);
- List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(mockedDeviceContext, multipartReplyMessage);
+ List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId(),
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getVersion(),
+ multipartReplyMessage);
DataObject dataObject = validateOutput(result);
assertTrue(dataObject instanceof AggregateFlowStatisticsUpdate);
MultipartReplyMessage multipartReplyMessage = prepareMocks(mockedDeviceContext, prepareMultipartReplyPortStats(), MultipartType.OFPMPPORTSTATS);
OpenflowPortsUtil.init();
- List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(mockedDeviceContext, multipartReplyMessage);
+ List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId(),
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getVersion(),
+ multipartReplyMessage);
DataObject dataObject = validateOutput(result);
assertTrue(dataObject instanceof NodeConnectorStatisticsUpdate);
MultipartReplyMessage multipartReplyMessage = prepareMocks(mockedDeviceContext, prepareMultipartReplyGroup(), MultipartType.OFPMPGROUP);
- List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(mockedDeviceContext, multipartReplyMessage);
+ List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId(),
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getVersion(),
+ multipartReplyMessage);
DataObject dataObject = validateOutput(result);
assertTrue(dataObject instanceof GroupStatisticsUpdated);
MultipartReplyMessage multipartReplyMessage = prepareMocks(mockedDeviceContext, prepareMultipartReplyGroupDesc(), MultipartType.OFPMPGROUPDESC);
- List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(mockedDeviceContext, multipartReplyMessage);
+ List<DataObject> result = singlePurposeMultipartReplyTranslator.translate(
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getDatapathId(),
+ mockedDeviceContext.getPrimaryConnectionContext().getFeatures().getVersion(),
+ multipartReplyMessage);
DataObject dataObject = validateOutput(result);
assertTrue(dataObject instanceof GroupDescStatsUpdated);
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;
Boolean isQueue = false;
protected DeviceContext mockedDeviceContext;
+ protected DeviceState mockedDeviceState;
+
StatisticsGatheringService mockedStatisticsGatheringService;
StatisticsGatheringOnTheFlyService mockedStatisticsOnFlyGatheringService;
ConnectionContext mockedConnectionContext;
- protected DeviceState mockedDeviceState;
+
static final KeyedInstanceIdentifier<Node, NodeKey> dummyNodeII = InstanceIdentifier.create(Nodes.class)
.child(Node.class, new NodeKey(new NodeId("dummyNodeId")));
- protected MessageSpy mockedMessageSpy;
- protected OutboundQueue mockedOutboundQueue;
- protected DeviceManager mockedDeviceManager;
+
LifecycleConductor mockConductor;
@Before
mockedStatisticsGatheringService = mock(StatisticsGatheringService.class);
mockedStatisticsOnFlyGatheringService = mock(StatisticsGatheringOnTheFlyService.class);
mockedConnectionContext = mock(ConnectionContext.class);
- final FeaturesReply mockedFeatures = mock(FeaturesReply.class);
mockedDeviceState = mock(DeviceState.class);
+
+ final FeaturesReply mockedFeatures = mock(FeaturesReply.class);
final MessageSpy mockedMessageSpy = mock(MessageSpy.class);
final OutboundQueue mockedOutboundQueue = mock(OutboundQueue.class);
final DeviceManager mockedDeviceManager = mock(DeviceManager.class);
+ final GetFeaturesOutput mockedFeaturesOutput = mock(GetFeaturesOutput.class);
+
mockConductor = mock(LifecycleConductor.class);
- when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
- when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedConnectionContext);
- when(mockedDeviceContext.getMessageSpy()).thenReturn(mockedMessageSpy);
when(mockedDeviceState.isTableStatisticsAvailable()).thenReturn(isTable);
when(mockedDeviceState.isFlowStatisticsAvailable()).thenReturn(isFlow);
when(mockedDeviceState.isGroupAvailable()).thenReturn(isGroup);
when(mockedDeviceState.isPortStatisticsAvailable()).thenReturn(isPort);
when(mockedDeviceState.isQueueStatisticsAvailable()).thenReturn(isQueue);
when(mockedDeviceState.getNodeInstanceIdentifier()).thenReturn(dummyNodeII);
+ when(mockedDeviceState.getFeatures()).thenReturn(mockedFeaturesOutput);
+
+ when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
+ when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedConnectionContext);
+ when(mockedDeviceContext.getMessageSpy()).thenReturn(mockedMessageSpy);
when(mockedConnectionContext.getNodeId()).thenReturn(dummyNodeII.getKey().getId());
when(mockedConnectionContext.getFeatures()).thenReturn(mockedFeatures);
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 static org.mockito.Mockito.when;
+
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collections;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.EventIdentifier;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
@Test
public void testGatherDynamicData_all() throws Exception {
Mockito.reset(mockedDeviceState);
+ when(mockedDeviceState.getFeatures()).thenReturn(mock(GetFeaturesOutput.class));
when(mockedDeviceState.isTableStatisticsAvailable()).thenReturn(Boolean.TRUE);
when(mockedDeviceState.isFlowStatisticsAvailable()).thenReturn(Boolean.TRUE);
when(mockedDeviceState.isGroupAvailable()).thenReturn(Boolean.TRUE);
final ArgumentCaptor<InstanceIdentifier> flowPath = ArgumentCaptor.forClass(InstanceIdentifier.class);
final ArgumentCaptor<Flow> flow = ArgumentCaptor.forClass(Flow.class);
- StatisticsGatheringUtils.writeFlowStatistics(prepareFlowStatisticsData(), deviceContext);
+ StatisticsGatheringUtils.writeFlowStatistics(prepareFlowStatisticsData(),
+ deviceContext.getDeviceState(), deviceContext.getDeviceFlowRegistry(), deviceContext);
Mockito.verify(deviceContext).writeToTransaction(
dataStoreType.capture(), flowPath.capture(), flow.capture());
@Test
public void testDeleteAllKnownFlowsNotSync() throws Exception {
when(deviceState.deviceSynchronized()).thenReturn(false);
- StatisticsGatheringUtils.deleteAllKnownFlows(deviceContext);
+ StatisticsGatheringUtils.deleteAllKnownFlows(deviceContext.getDeviceState(),
+ deviceContext.getDeviceFlowRegistry(), deviceContext);
Mockito.verifyNoMoreInteractions(deviceFlowRegistry);
}
final KeyedInstanceIdentifier<Table, TableKey> tablePath = deviceState.getNodeInstanceIdentifier()
.augmentation(FlowCapableNode.class).child(Table.class, new TableKey(tableId));
- StatisticsGatheringUtils.deleteAllKnownFlows(deviceContext);
+ StatisticsGatheringUtils.deleteAllKnownFlows(deviceContext.getDeviceState(),
+ deviceContext.getDeviceFlowRegistry(), deviceContext);
verify(deviceContext).writeToTransaction(
LogicalDatastoreType.OPERATIONAL,
+/**
+ * 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;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
-import io.netty.util.TimerTask;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.opendaylight.openflowplugin.api.openflow.rpc.ItemLifeCycleSource;
import org.opendaylight.openflowplugin.api.openflow.rpc.listener.ItemLifecycleListener;
import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext;
+import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
import org.opendaylight.openflowplugin.impl.registry.flow.DeviceFlowRegistryImpl;
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.openflow.protocol.rev130731.FeaturesReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartRequestInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.sm.control.rev150812.GetStatisticsWorkModeOutput;
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.binding.KeyedInstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
@Mock
FeaturesReply mockedFeatures;
@Mock
+ GetFeaturesOutput mockedFeaturesOutput;
+ @Mock
ConnectionAdapter mockedConnectionAdapter;
@Mock
MessageSpy mockedMessagSpy;
private DeviceManager deviceManager;
@Mock
private LifecycleConductor conductor;
+ @Mock
+ private GetFeaturesOutput featuresOutput;
+ @Mock
+ private DeviceInitializationPhaseHandler deviceInitializationPhaseHandler;
private RequestContext<List<MultipartReply>> currentRequestContext;
private StatisticsManagerImpl statisticsManager;
+
@Before
public void initialization() {
+ final KeyedInstanceIdentifier<Node, NodeKey> nodePath = KeyedInstanceIdentifier
+ .create(Nodes.class)
+ .child(Node.class, new NodeKey(new NodeId("openflow:10")));
+
when(mockedFeatures.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
when(mockedFeatures.getVersion()).thenReturn(DUMMY_VERSION);
+ when(mockedFeaturesOutput.getDatapathId()).thenReturn(DUMMY_DATAPATH_ID);
+ when(mockedFeaturesOutput.getVersion()).thenReturn(DUMMY_VERSION);
when(mockedPrimConnectionContext.getFeatures()).thenReturn(mockedFeatures);
when(mockedPrimConnectionContext.getConnectionAdapter()).thenReturn(mockedConnectionAdapter);
when(mockedDeviceState.isPortStatisticsAvailable()).thenReturn(Boolean.TRUE);
when(mockedDeviceState.isQueueStatisticsAvailable()).thenReturn(Boolean.TRUE);
when(mockedDeviceState.isTableStatisticsAvailable()).thenReturn(Boolean.TRUE);
+ when(mockedDeviceState.getFeatures()).thenReturn(featuresOutput);
+ when(mockedDeviceState.getNodeInstanceIdentifier()).thenReturn(nodePath);
when(mockedDeviceState.getNodeId()).thenReturn(new NodeId("ofp-unit-dummy-node-id"));
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 testOnDeviceContextLevelUp() throws Exception {
- statisticsManager = new StatisticsManagerImpl(rpcProviderRegistry, true, conductor);
Mockito.doAnswer(new Answer<Void>() {
@Override
- public Void answer(final InvocationOnMock invocation) throws Throwable {
+ public Void answer(InvocationOnMock invocation) throws Throwable {
final FutureCallback<OfHeader> callback = (FutureCallback<OfHeader>) invocation.getArguments()[2];
LOG.debug("committing entry: {}", ((MultipartRequestInput) invocation.getArguments()[1]).getType());
callback.onSuccess(null);
statisticsManager.setDeviceInitializationPhaseHandler(mockedDevicePhaseHandler);
statisticsManager.onDeviceContextLevelUp(mockedDeviceContext.getDeviceState().getNodeId());
-
- verify(mockedDeviceContext, Mockito.never()).reserveXidForDeviceMessage();
- verify(mockedDeviceState).setDeviceSynchronized(true);
verify(mockedDevicePhaseHandler).onDeviceContextLevelUp(mockedDeviceContext.getDeviceState().getNodeId());
- verify(hashedWheelTimer, Mockito.never()).newTimeout(Matchers.<TimerTask>any(), Matchers.anyLong(), Matchers.<TimeUnit>any());
}
@Test
statisticsManager.calculateTimerDelay(timeCounter);
Assert.assertEquals(6000L, StatisticsManagerImpl.getCurrentTimerDelay());
}
+
+ @Test
+ 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).getDeviceState();
+
+ when(mockedDeviceContext.getDeviceState().isValid()).thenReturn(true);
+ statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
+ // TODO Make scheduleNextPolling visible for tests?
+
+ when(mockedDeviceContext.getDeviceState().isStatisticsPollingEnabled()).thenReturn(true);
+ statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
+ // TODO Make scheduleNextPolling visible for tests?
+
+ 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();
+
+ when(statisticsContext.gatherDynamicData()).thenReturn(Futures.immediateFailedFuture(new Throwable("error msg")));
+ statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
+ Mockito.verify(mockTimerCounter,times(2)).addTimeMark();
+ }
}
\ No newline at end of file
Mockito.when(deviceState.getNodeId()).thenReturn(NODE_ID);
Mockito.when(deviceState.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
Mockito.when(deviceState.getFeatures()).thenReturn(getFeaturesOutput);
- Mockito.when(getFeaturesOutput.getDatapathId()).thenReturn(BigInteger.valueOf(123L));
Mockito.when(connectionContext.getFeatures()).thenReturn(features);
Mockito.when(connectionContext.getOutboundQueueProvider()).thenReturn(outboundQueueProvider);
Mockito.when(features.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
+ Mockito.when(getFeaturesOutput.getDatapathId()).thenReturn(BigInteger.valueOf(123L));
+ Mockito.when(getFeaturesOutput.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
setUp();
private OpendaylightFlowStatisticsServiceImpl flowStatisticsService;
public void setUp() {
- flowStatisticsService = new OpendaylightFlowStatisticsServiceImpl(rqContextStack, deviceContext);
+ flowStatisticsService = OpendaylightFlowStatisticsServiceImpl.createWithOook(rqContextStack, deviceContext);
rqContext = new AbstractRequestContext<Object>(42L) {
@Override
public void setUp() {
- flowStatisticsService = new OpendaylightFlowStatisticsServiceImpl(rqContextStack, deviceContext);
+ flowStatisticsService = OpendaylightFlowStatisticsServiceImpl.createWithOook(rqContextStack, deviceContext);
rqContextMp = new AbstractRequestContext<List<MultipartReply>>(42L) {
@Override
}
).when(multiMsgCollector).endCollecting(Matchers.any(EventIdentifier.class));
Mockito.when(translator.translate(
- Matchers.any(MultipartReply.class), Matchers.same(deviceContext), Matchers.isNull())
+ Matchers.any(MultipartReply.class), Matchers.same(deviceState), Matchers.isNull())
).thenReturn(new AggregatedFlowStatisticsBuilder().build());
private OpendaylightFlowStatisticsServiceImpl flowStatisticsService;
public void setUp() {
- flowStatisticsService = new OpendaylightFlowStatisticsServiceImpl(rqContextStack, deviceContext);
+ flowStatisticsService = OpendaylightFlowStatisticsServiceImpl.createWithOook(rqContextStack, deviceContext);
flowStatisticsService.setDelegate(flowStatisticsDelegate);
}
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.get.aggregate.flow.statistics.from.flow.table._for.given.match.output.AggregatedFlowStatisticsBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReplyMessageBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartRequestInput;
private DeviceState deviceState;
@Mock
private MessageTranslator<Object, Object> translator;
+ @Mock
+ private GetFeaturesOutput featuresOutput;
private AbstractRequestContext<Object> rqContext;
}
};
+ Mockito.when(featuresOutput.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
Mockito.when(rqContextStack.<Object>createRequestContext()).thenReturn(rqContext);
Mockito.when(deviceContext.getDeviceState()).thenReturn(deviceState);
Mockito.when(deviceState.getNodeId()).thenReturn(NODE_ID);
Mockito.when(deviceState.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_3);
+ Mockito.when(deviceState.getFeatures()).thenReturn(featuresOutput);
Mockito.doAnswer(closeRequestFutureAnswer).when(multiMsgCollector).endCollecting();
Mockito.doAnswer(closeRequestFutureAnswer).when(multiMsgCollector).endCollecting(Matchers.any(EventIdentifier.class));
Mockito.when(translatorLibrary.lookupTranslator(Matchers.any(TranslatorKey.class))).thenReturn(translator);
- service = new AggregateFlowsInTableService(rqContextStack, deviceContext, new AtomicLong(20L));
+ service = AggregateFlowsInTableService.createWithOook(rqContextStack, deviceContext, new AtomicLong(20L));
}
@Test
.setFlowCount(new Counter32(12L))
.setPacketCount(new Counter64(BigInteger.valueOf(13L)))
.build();
- Mockito.when(translator.translate(Matchers.any(MultipartReply.class), Matchers.eq(deviceContext), Matchers.any()))
+ Mockito.when(translator.translate(Matchers.any(MultipartReply.class), Matchers.eq(deviceState), Matchers.any()))
.thenReturn(aggregatedStats);
.setNode(createNodeRef("unitProt:123"))
.setTableId(new TableId((short) 1));
- Mockito.when(translator.translate(Matchers.any(MultipartReply.class), Matchers.eq(deviceContext), Matchers.any()))
+ Mockito.when(translator.translate(Matchers.any(MultipartReply.class), Matchers.eq(deviceState), Matchers.any()))
.thenReturn(new AggregatedFlowStatisticsBuilder()
.setByteCount(new Counter64(BigInteger.valueOf(50L)))
.setPacketCount(new Counter64(BigInteger.valueOf(51L)))
protected void setup() {
statisticsGatheringService = new StatisticsGatheringOnTheFlyService(mockedRequestContextStack, mockedDeviceContext);
Mockito.doReturn(NODE_ID).when(mockedPrimConnectionContext).getNodeId();
+ Mockito.when(mockedDeviceContext.getDeviceState().getNodeId()).thenReturn(NODE_ID);
}
@Test
--- /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
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.get.aggregate.flow.statistics.from.flow.table._for.given.match.output.AggregatedFlowStatistics;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReplyMessageBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyAggregateCaseBuilder;
private AggregatedFlowStatisticsTranslator translator;
@Mock
- private DeviceContext deviceContext;
+ private DeviceState deviceState;
@Before
public void setUp() throws Exception {
MultipartReplyMessageBuilder mpInputBld = new MultipartReplyMessageBuilder()
.setMultipartReplyBody(inputBld.build());
- final AggregatedFlowStatistics statistics = translator.translate(mpInputBld.build(), deviceContext, null);
+ final AggregatedFlowStatistics statistics = translator.translate(mpInputBld.build(), deviceState, null);
Assert.assertEquals(aggregateStatsValueBld.getByteCount(), statistics.getByteCount().getValue());
Assert.assertEquals(aggregateStatsValueBld.getFlowCount(), statistics.getFlowCount().getValue());
--- /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.translator;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import java.math.BigInteger;
+import java.util.Collections;
+
+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.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+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.openflow.common.types.rev130731.FlowWildcardsV10;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.TableId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.grouping.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.v10.grouping.MatchV10Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemovedMessageBuilder;
+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;
+
+/**
+ * Test of {@link AggregatedFlowStatisticsTranslator}
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FlowRemovedTranslatorTest {
+
+ private FlowRemovedTranslator translator;
+
+ private FlowRemovedV10Translator translatorV10;
+
+ @Mock
+ private DeviceContext deviceContext;
+
+ @Mock
+ private DeviceState deviceState;
+
+ @Mock
+ private GetFeaturesOutput features;
+
+ @Mock
+ private FlowWildcardsV10 flowWildcards;
+
+ private KeyedInstanceIdentifier<Node, NodeKey> nodeId;
+
+ @Before
+ public void setUp() throws Exception {
+ nodeId = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(new NodeId("dummyNodeId")));
+
+ translator = new FlowRemovedTranslator();
+ translatorV10 = new FlowRemovedV10Translator();
+
+ when(deviceContext.getDeviceState()).thenReturn(deviceState);
+ when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodeId);
+ when(deviceState.getFeatures()).thenReturn(features);
+ when(features.getDatapathId()).thenReturn(BigInteger.TEN);
+ }
+
+ @Test
+ public void testTranslate() throws Exception {
+ org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemoved flowRemovedMessage = buildMessage(false);
+ final FlowRemoved flowRemoved = translator.translate(flowRemovedMessage, deviceState, null);
+
+ assertEquals(flowRemovedMessage.getCookie(), flowRemoved.getCookie().getValue());
+ assertEquals(flowRemovedMessage.getPriority(), flowRemoved.getPriority());
+ assertEquals((long)flowRemovedMessage.getTableId().getValue(), (long)flowRemoved.getTableId());
+ }
+
+ @Test
+ public void testTranslateV10() throws Exception {
+ org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemoved flowRemovedMessage = buildMessage(true);
+ final FlowRemoved flowRemoved = translatorV10.translate(flowRemovedMessage, deviceState, null);
+
+ assertEquals(flowRemovedMessage.getCookie(), flowRemoved.getCookie().getValue());
+ assertEquals(flowRemovedMessage.getPriority(), flowRemoved.getPriority());
+ assertEquals((long)flowRemovedMessage.getTableId().getValue(), (long)flowRemoved.getTableId());
+ }
+
+ private org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemoved buildMessage(boolean isV10) {
+ FlowRemovedMessageBuilder builder = new FlowRemovedMessageBuilder()
+ .setCookie(BigInteger.ONE)
+ .setPriority(1)
+ .setTableId(new TableId(42l));
+
+ if (isV10) {
+ builder.setMatchV10(new MatchV10Builder().setWildcards(flowWildcards).build());
+ } else {
+ builder.setMatch(new MatchBuilder().setMatchEntry(Collections.<MatchEntry>emptyList()).build());
+ }
+
+ return builder.build();
+ }
+}
\ No newline at end of file
final PacketInMessage packetInMessage = createPacketInMessage(DATA.getBytes(), PORT_NO);
Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodePath);
- final PacketReceived packetReceived = packetReceivedTranslator.translate(packetInMessage, deviceContext, null);
+ final PacketReceived packetReceived = packetReceivedTranslator.translate(packetInMessage, deviceState, null);
Assert.assertArrayEquals(packetInMessage.getData(), packetReceived.getPayload());
Assert.assertEquals("org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.SendToController",
.getDefaultInstance("hundredGbFd");
- final FlowCapableNodeConnector nodeConnector = portUpdateTranslator.translate(portBld.build(), deviceContext, null);
+ final FlowCapableNodeConnector nodeConnector = portUpdateTranslator.translate(portBld.build(), deviceState, null);
commonCheck(nodeConnector);
null, false, false, null, null, null, null, null
);
- final FlowCapableNodeConnector nodeConnector = portUpdateTranslator.translate(portBld.build(), deviceContext, null);
+ final FlowCapableNodeConnector nodeConnector = portUpdateTranslator.translate(portBld.build(), deviceState, null);
commonCheck(nodeConnector);
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import com.google.common.collect.Lists;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
+
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.device.MessageTranslator;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
import org.opendaylight.openflowplugin.api.openflow.device.TranslatorLibrary;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.MultiMsgCollector;
import org.opendaylight.openflowplugin.api.openflow.md.core.TranslatorKey;
import org.opendaylight.openflowplugin.impl.device.DeviceContextImpl;
import org.opendaylight.openflowplugin.openflow.md.util.OpenflowPortsUtil;
@RunWith(MockitoJUnitRunner.class)
public class DeviceInitializationUtilsTest {
- private static final boolean TEST_VALUE_SWITCH_FEATURE_MANDATORY = true;
- private static final long TEST_VALUE_GLOBAL_NOTIFICATION_QUOTA = 2000l;
+ public static final String DUMMY_NODE_ID = "dummyNodeId";
private static final KeyedInstanceIdentifier<Node, NodeKey> DUMMY_NODE_II = InstanceIdentifier.create(Nodes.class)
- .child(Node.class, new NodeKey(new NodeId("dummyNodeId")));
+ .child(Node.class, new NodeKey(new NodeId(DUMMY_NODE_ID)));
private static final Short DUMMY_TABLE_ID = 1;
private static final Long DUMMY_MAX_METER = 544L;
private static final String DUMMY_DATAPATH_ID = "44";
public void setUp() throws Exception {
OpenflowPortsUtil.init();
- when(mockConnectionContext.getNodeId()).thenReturn(new NodeId("dummyNodeId"));
+ when(mockConnectionContext.getNodeId()).thenReturn(new NodeId(DUMMY_NODE_ID));
when(mockConnectionContext.getFeatures()).thenReturn(mockFeatures);
when(mockConnectionContext.getConnectionAdapter()).thenReturn(mockedConnectionAdapter);
when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockConnectionContext);
- final Capabilities capabilitiesV13 = Mockito.mock(Capabilities.class);
- final CapabilitiesV10 capabilitiesV10 = Mockito.mock(CapabilitiesV10.class);
+ final Capabilities capabilitiesV13 = mock(Capabilities.class);
+ final CapabilitiesV10 capabilitiesV10 = mock(CapabilitiesV10.class);
when(mockFeatures.getCapabilities()).thenReturn(capabilitiesV13);
when(mockFeatures.getCapabilitiesV10()).thenReturn(capabilitiesV10);
when(mockFeatures.getDatapathId()).thenReturn(BigInteger.valueOf(21L));
}
+
+ @Test
+ public void initializeNodeInformationTest() throws Exception {
+ DeviceState mockedDeviceState = mock(DeviceState.class);
+ MultiMsgCollector msgCollector = mock(MultiMsgCollector.class);
+ TranslatorLibrary tLibrary = mock(TranslatorLibrary.class);
+
+ GetFeaturesOutput mockedFeatures = mock(GetFeaturesOutput.class);
+ when(mockedFeatures.getTables()).thenReturn((short) 2);
+ when(mockedDeviceState.getFeatures()).thenReturn(mockedFeatures);
+ when(mockedDeviceState.getVersion()).thenReturn(OFConstants.OFP_VERSION_1_0);
+
+ when(mockedDeviceState.getNodeInstanceIdentifier()).thenReturn(DUMMY_NODE_II);
+ when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
+ when(mockedDeviceContext.getMultiMsgCollector(Mockito.any(RequestContext.class))).thenReturn(msgCollector);
+ when(mockedDeviceContext.oook()).thenReturn(tLibrary);
+
+ final ConnectionContext connectionContext = buildMockConnectionContext(OFConstants.OFP_VERSION_1_0);
+ when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
+
+ DeviceInitializationUtils.initializeNodeInformation(mockedDeviceContext, true);
+
+ verify(mockFeatures, atLeastOnce()).getPhyPort();
+ verify(tLibrary, atLeastOnce()).lookupTranslator(any(TranslatorKey.class));
+ }
+
@Test
public void chainTableTrunkWriteOF10Test() throws Exception {
DeviceState mockedDeviceState = mock(DeviceState.class);
@Test
public void testTranslateAndWriteReplyTypeDesc() throws Exception {
final ConnectionContext connectionContext = buildMockConnectionContext(OFConstants.OFP_VERSION_1_3);
- Mockito.when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
- final DeviceState deviceState = Mockito.mock(DeviceState.class);
- Mockito.when(mockedDeviceContext.getDeviceState()).thenReturn(deviceState);
+ when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
+ final DeviceState deviceState = mock(DeviceState.class);
+ when(mockedDeviceContext.getDeviceState()).thenReturn(deviceState);
final Collection<MultipartReply> multipartReplyMessages = prepareDataforTypeDesc(mockedDeviceContext);
when(mockConnectionContext.getOutboundQueueProvider()).thenReturn(outboundQueueProvider);
return mockConnectionContext;
}
-
-
-
}
import org.mockito.Matchers;
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.rpc.RpcContext;
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.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
import org.opendaylight.yangtools.yang.binding.RpcService;
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() {
final DeviceContext mockedDeviceContext = mock(DeviceContext.class);
final ConnectionContext mockedConnectionContext = mock(ConnectionContext.class);
+ final DeviceState mockedDeviceState = mock(DeviceState.class);
+ when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
+
final FeaturesReply mockedFeatures = mock(FeaturesReply.class);
when(mockedConnectionContext.getFeatures()).thenReturn(mockedFeatures);
+ final GetFeaturesOutput mockedFeaturesOutput = mock(GetFeaturesOutput.class);
+ when(mockedDeviceState.getFeatures()).thenReturn(mockedFeaturesOutput);
+
final BigInteger mockedDataPathId = mock(BigInteger.class);
when(mockedFeatures.getDatapathId()).thenReturn(mockedDataPathId);
+ when(mockedFeaturesOutput.getDatapathId()).thenReturn(mockedDataPathId);
when(mockedDeviceContext.getPrimaryConnectionContext()).thenReturn(mockedConnectionContext);
- MdSalRegistrationUtils.registerMasterServices(mockedRpcContext,mockedDeviceContext, OfpRole.BECOMEMASTER);
+ MdSalRegistrationUtils.registerMasterServices(mockedRpcContext, mockedDeviceContext, OfpRole.BECOMEMASTER);
verify(mockedRpcContext, times(NUMBER_OF_RPC_SERVICE_REGISTRATION)).registerRpcServiceImplementation(
Matchers.<Class<RpcService>> any(), any(RpcService.class));
}
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.openflowplugin.api.openflow.md.ModelDrivenSwitch;
import org.opendaylight.openflowplugin.api.openflow.md.core.NotificationQueueWrapper;
+import org.opendaylight.openflowplugin.openflow.md.core.sal.OpenflowPluginConfig;
import org.opendaylight.yangtools.concepts.CompositeObjectRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.FutureCallback;
-import java.util.concurrent.ArrayBlockingQueue;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.CheckedFuture;
import org.slf4j.Logger;
private final ListeningExecutorService pool;
- public OfEntityManager( EntityOwnershipService entityOwnershipService ) {
+ private final OpenflowPluginConfig openflowPluginConfig;
+
+ public OfEntityManager(EntityOwnershipService entityOwnershipService, OpenflowPluginConfig ofPluginConfig) {
this.entityOwnershipService = entityOwnershipService;
+ openflowPluginConfig = ofPluginConfig;
ownershipListener = new OpenflowOwnershipListener(this);
entsession = new ConcurrentHashMap<>();
entRegistrationMap = new ConcurrentHashMap<>();
this.dataBroker = dbBroker;
}
+ public void init(){
+ registerEntityOwnershipChangeListener();
+ }
+
+ public void registerEntityOwnershipChangeListener() {
+ if(entityOwnershipService!=null) {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("registerEntityOwnershipChangeListener: Registering entity ownership change listener for entitier of type {}", DEVICE_TYPE);
+ }
+ entityOwnershipService.registerListener(DEVICE_TYPE, ownershipListener);
+ }
+ }
+
public void requestOpenflowEntityOwnership(final ModelDrivenSwitch ofSwitch,
final SessionContext context,
final NotificationQueueWrapper wrappedNotification,
MDSwitchMetaData entityMetaData =
new MDSwitchMetaData(ofSwitch,context,wrappedNotification,rpcProviderRegistry);
- if (registeredListener.compareAndSet(false, true)) {
- entityOwnershipService.registerListener(DEVICE_TYPE, ownershipListener);
- }
final Entity entity = new Entity(DEVICE_TYPE, ofSwitch.getNodeId().getValue());
entsession.put(entity, entityMetaData);
final String targetSwitchDPId = sessionContext.getFeatures().getDatapathId().toString();
RolePushTask task = new RolePushTask(newRole, sessionContext);
ListenableFuture<Boolean> rolePushResult = pool.submit(task);
+
final CheckedFuture<Boolean, RolePushException> rolePushResultChecked =
RoleUtil.makeCheckedRuleRequestFxResult(rolePushResult);
Futures.addCallback(rolePushResult, new FutureCallback<Boolean>(){
public void onSuccess(Boolean result){
LOG.info("onDeviceOwnershipChanged: Controller is successfully set as a " +
"MASTER controller for {}", targetSwitchDPId);
- entsession.get(entity).getOfSwitch().sendEmptyTableFeatureRequest();
+ if(!openflowPluginConfig.skipTableFeatures()) {
+ if(LOG.isDebugEnabled()){
+ LOG.debug("Send table feature request for entity {}",entity.getId());
+ }
+ entsession.get(entity).getOfSwitch().sendEmptyTableFeatureRequest();
+ }
sendNodeAddedNotification(entsession.get(entity));
}
--- /dev/null
+/*
+ * Copyright (c) 2016 Ericsson 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.openflow.md.core.sal;
+
+/**
+ * Created by eshuvka on 5/16/2016.
+ */
+public class OpenflowPluginConfig {
+
+ private final boolean skipTableFeatures;
+
+ private OpenflowPluginConfig (OpenflowPluginConfigBuilder builder){
+ skipTableFeatures = builder.skipTableFeatures();
+ }
+
+ public boolean skipTableFeatures(){
+ return skipTableFeatures;
+ }
+
+ public static OpenflowPluginConfigBuilder builder(){
+ return new OpenflowPluginConfigBuilder();
+ }
+
+ public static class OpenflowPluginConfigBuilder{
+ private boolean skipTableFeatures;
+
+ public boolean skipTableFeatures(){
+ return skipTableFeatures;
+ }
+
+ public void setSkipTableFeatures(boolean skip){
+ skipTableFeatures = skip;
+ }
+
+ public OpenflowPluginConfig build() {return new OpenflowPluginConfig(this);}
+ }
+}
private RpcProviderRegistry rpcRegistry;
private EntityOwnershipService entityOwnershipService;
+ private OpenflowPluginConfig openflowPluginConfig;
+
/**
* Initialization of services and msgSpy counter
*/
messageCountProvider = new MessageSpyCounterImpl();
extensionConverterManager = new ExtensionConverterManagerImpl();
roleManager = new OFRoleManager(OFSessionUtil.getSessionManager());
- entManager = new OfEntityManager(entityOwnershipService);
+ entManager = new OfEntityManager(entityOwnershipService,getOpenflowPluginConfig());
entManager.setDataBroker(dataBroker);
+ entManager.init();
LOG.debug("dependencies gathered..");
registrationManager = new SalRegistrationManager();
this.entityOwnershipService = entityOwnershipService;
}
+ public void setOpenflowPluginConfig(OpenflowPluginConfig openflowPluginConfig) {
+ this.openflowPluginConfig = openflowPluginConfig;
+ }
+
+ @VisibleForTesting
+ public OpenflowPluginConfig getOpenflowPluginConfig() {
+ return openflowPluginConfig;
+ }
+
@VisibleForTesting
protected RpcProviderRegistry getRpcRegistry() {
return rpcRegistry;
import com.google.common.base.MoreObjects;
import javax.management.ObjectName;
+
+import org.opendaylight.openflowplugin.openflow.md.core.sal.OpenflowPluginConfig;
import org.opendaylight.openflowplugin.openflow.md.core.sal.OpenflowPluginProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
*
*/
public final class ConfigurableOpenFlowProviderModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.common.config.impl.rev140326.AbstractConfigurableOpenFlowProviderModule {
+ private static final Logger LOG = LoggerFactory.getLogger(ConfigurableOpenFlowProviderModule.class);
+
private OpenflowPluginProvider pluginProvider;
+ private static final boolean SKIP_TABLE_FEATURES = false;
+
/**
* @param identifier module identifier
* @param dependencyResolver dependency resolver
pluginProvider.setSwitchConnectionProviders(getOpenflowSwitchConnectionProviderDependency());
pluginProvider.setEntityOwnershipService(getOwnershipServiceDependency());
pluginProvider.setRole(getRole());
+ pluginProvider.setOpenflowPluginConfig(readConfig());
pluginProvider.initialization();
return pluginProvider;
}
return recycled;
}
+
+ private OpenflowPluginConfig readConfig(){
+
+ final OpenflowPluginConfig.OpenflowPluginConfigBuilder openflowCfgBuilder = OpenflowPluginConfig.builder();
+
+ if(getSkipTableFeatures()!=null){
+ openflowCfgBuilder.setSkipTableFeatures(getSkipTableFeatures().booleanValue());
+ } else{
+ LOG.warn("Could not load XML configuration file via ConfigSubsystem! Fallback to default config value(s)");
+ openflowCfgBuilder.setSkipTableFeatures(SKIP_TABLE_FEATURES);
+ }
+
+ return openflowCfgBuilder.build();
+ }
}
type ofp-role;
default "NOCHANGE";
}
+
+ leaf skip-table-features {
+ description "Ability to skip pulling and storing of large table features. These features are still
+ available via rpc, so if use set it to true, it won't store table feature data in DataStore.";
+ type boolean;
+ default "false";
+ }
}
case msg-spy-service-impl {
private ModelDrivenSwitch mdSwitchOF13;
-
CompositeObjectRegistration<ModelDrivenSwitch> registration;
context.setFeatures(features);
context.setNotificationEnqueuer(notificationEnqueuer);
- OfEntityManager entManager = new OfEntityManager(entityOwnershipService);
+ OfEntityManager entManager = new OfEntityManager(entityOwnershipService,getConfig());
mdSwitchOF13 = new ModelDrivenSwitchImpl(null, null, context);
registration = new CompositeObjectRegistration<>(mdSwitchOF13, Collections.<Registration>emptyList());
context.setProviderRegistration(registration);
SwitchSessionKeyOF switchSessionKeyOF = new SwitchSessionKeyOF();
salRegistrationManager.onSessionAdded(switchSessionKeyOF, context);
}
+
+ public OpenflowPluginConfig getConfig(){
+ OpenflowPluginConfig.OpenflowPluginConfigBuilder cfgBuilder =
+ new OpenflowPluginConfig.OpenflowPluginConfigBuilder();
+ cfgBuilder.setSkipTableFeatures(true);
+ return cfgBuilder.build();
+
+ }
}
portNumber = new PortNumberUni(OutputPortValues.FLOOD.toString());
Assert.assertEquals("FLOOD", OpenflowPortsUtil.portNumberToString(portNumber));
- try {
- portNumber = new PortNumberUni((String) null);
- Assert.fail("NPE was expected - due to value type");
- } catch (Exception e) {
- // expected
- Assert.assertEquals(NullPointerException.class, e.getClass());
- }
+
+ portNumber = new PortNumberUni((String) null);
+ Assert.assertNotNull(portNumber);
+
}
}