Merge "BUG-2188 :To report (TCP) port number (for the OpenFlow connection) for switch...
authorAbhijit Kumbhare <abhijit.kumbhare@ericsson.com>
Thu, 2 Jun 2016 16:51:08 +0000 (16:51 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 2 Jun 2016 16:51:08 +0000 (16:51 +0000)
216 files changed:
applications/forwardingrules-sync/pom.xml [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesAddCommitter.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesCommitter.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesRemoveCommitter.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesUpdateCommitter.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/NodeListener.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SemaphoreKeeper.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SyncPlanPushStrategy.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SyncReactor.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeCachedDao.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeDao.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeOdlDao.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeSnapshotDao.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/AbstractFrmSyncListener.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/FlowForwarder.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/ForwardingRulesSyncProvider.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/FrmExecutors.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/GroupForwarder.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/MeterForwarder.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedConfigListener.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedOperationalListener.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorFutureDecorator.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorFutureWithCompressionDecorator.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorGuardDecorator.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorImpl.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/TableForwarder.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyFlatBatchImpl.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyIncrementalImpl.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SynchronizationDiffInput.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/markandsweep/SwitchFlowId.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/CrudCounts.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/FlowCapableNodeLookups.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/FxChainUtil.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/ItemSyncBox.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/PathUtil.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/ReconcileUtil.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/SemaphoreKeeperGuavaImpl.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/SyncCrudCounters.java [new file with mode: 0644]
applications/forwardingrules-sync/src/main/resources/features.xml [new file with mode: 0644]
applications/forwardingrules-sync/src/main/resources/org/opendaylight/blueprint/forwardingrules-sync.xml [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/DSInputFactory.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/FlowForwarderTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/ForwardingRulesSyncProviderTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/GroupForwarderTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/MeterForwarderTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedConfigListenerTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedOperationalListenerTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorImplTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/TableForwarderTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/DiffInputFactory.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyFlatBatchImplTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyIncrementalImplTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/util/ReconcileUtilTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/util/SemaphoreKeeperTest.java [new file with mode: 0644]
applications/forwardingrules-sync/src/test/resources/log4j.xml [new file with mode: 0644]
applications/notification-supplier/pom.xml [moved from applications/old-notification-supplier/pom.xml with 96% similarity]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationProvider.java [moved from applications/old-notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/old/notification/supplier/OldNotifProvider.java with 75% similarity]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationProviderImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierDefinition.java [moved from applications/old-notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/old/notification/supplier/OldNotifSupplierDefinition.java with 78% similarity]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItem.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItemRoot.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItemStat.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/AbstractNotificationSupplierBase.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/AbstractNotificationSupplierForItemRoot.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeConnectorNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/AbstractNotificationSupplierForItem.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/FlowNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/GroupNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/MeterNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/package-info.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/AbstractNotificationSupplierForItemStat.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowStatNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowTableStatNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/GroupStatNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/MeterStatNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/NodeConnectorStatNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/QueueStatNotificationSupplierImpl.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/package-info.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/package-info.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/package-info.java [moved from applications/old-notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/old/notification/supplier/package-info.java with 83% similarity]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/tools/NotificationProviderConfig.java [moved from applications/old-notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/old/notification/supplier/tools/OldNotifProviderConfig.java with 90% similarity]
applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/tools/package-info.java [moved from applications/old-notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/old/notification/supplier/tools/package-info.java with 80% similarity]
applications/notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/notification/supplier/rev150820/NotifModule.java [new file with mode: 0644]
applications/notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/notification/supplier/rev150820/NotifModuleFactory.java [new file with mode: 0644]
applications/notification-supplier/src/main/resources/initial/73-notification-supplier.xml [moved from applications/old-notification-supplier/src/main/resources/initial/73-old-notification-supplier.xml with 86% similarity]
applications/notification-supplier/src/main/yang/notification-supplier.yang [moved from applications/old-notification-supplier/src/main/yang/old-notification-supplier.yang with 85% similarity]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationProviderImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeConnectorNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/helper/TestChangeEventBuildHelper.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/helper/TestSupplierVerifyHelper.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/FlowNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/GroupNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/MeterNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowStatNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowTableStatNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/GroupStatNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/MeterStatNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/NodeConnectorStatNotificationSupplierImplTest.java [new file with mode: 0644]
applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/QueueStatNotificationSupplierImplTest.java [new file with mode: 0644]
applications/old-notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/old/notification/supplier/rev150820/OldNotifModule.java [deleted file]
applications/old-notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/old/notification/supplier/rev150820/OldNotifModuleFactory.java [deleted file]
applications/pom.xml
applications/statistics-manager/src/main/java/org/opendaylight/openflowplugin/applications/statistics/manager/impl/helper/MatchComparatorHelper.java
applications/statistics-manager/src/test/java/org/opendaylight/openflowplugin/applications/statistics/manager/impl/helper/MatchComparatorHelperTest.java
artifacts/pom.xml
features-li/pom.xml
features-li/src/main/features/features.xml
model/model-flow-statistics/src/main/yang/opendaylight-direct-statistics.yang [new file with mode: 0644]
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/device/DeviceContext.java
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/device/DeviceState.java
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/device/MessageTranslator.java
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/device/TxFacade.java [new file with mode: 0644]
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/lifecycle/LifecycleConductor.java
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/statistics/StatisticsContext.java
openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/statistics/StatisticsManager.java
openflowplugin-controller-config/src/main/resources/initial/42-openflowplugin.xml
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/LifecycleConductorImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionContextImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/ConnectionManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/HandshakeContextImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/OutboundQueueProviderImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/ConnectionReadyListenerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/HandshakeListenerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/connection/listener/OpenflowProtocolListenerInitialImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/DeviceStateImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/TransactionChainManager.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/listener/OpenflowProtocolListenerFullImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/rpc/RpcContextImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/rpc/RpcManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/AbstractMultipartOnTheFlyService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/AbstractService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/MultipartRequestOnTheFlyCallback.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/RoleService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/SalRoleServiceImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/SalTableServiceImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/SinglePurposeMultipartReplyTranslator.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsContextImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsGatheringUtils.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsManagerImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/AggregateFlowsInTableService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/FlowsInTableService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/MatchingFlowsInTableService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/OpendaylightFlowStatisticsServiceImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/compatibility/AbstractCompatibleStatService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/compatibility/OpendaylightFlowStatisticsServiceDelegateImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/dedicated/StatisticsGatheringOnTheFlyService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/dedicated/StatisticsGatheringService.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/AbstractDirectStatisticsService.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/FlowDirectStatisticsService.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/GroupDirectStatisticsService.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/MeterDirectStatisticsService.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/NodeConnectorDirectStatisticsService.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceImpl.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceProvider.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/QueueDirectStatisticsService.java [new file with mode: 0644]
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/translator/AggregatedFlowStatisticsTranslator.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/translator/FlowRemovedTranslator.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/translator/FlowRemovedV10Translator.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/translator/PacketReceivedTranslator.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/translator/PortUpdateTranslator.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/util/DeviceInitializationUtils.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/util/MdSalRegistrationUtils.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/LifecycleConductorImplTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImplTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceContextImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceManagerImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/DeviceStateImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/device/TransactionChainManagerTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/role/RoleContextImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/role/RoleManagerImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/rpc/ItemLifecycleListenerImplTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/rpc/RpcContextImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/rpc/RpcManagerImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/MultipartRequestOnTheFlyCallbackTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalFlowServiceImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalRoleServiceImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/SalTableServiceImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/services/ServiceMocking.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/SinglePurposeMultipartReplyTranslatorTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsContextImpMockInitiation.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsContextImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsGatheringUtilsTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsManagerImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/AbstractStatsServiceTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/OpendaylightFlowStatisticsServiceImpl1Test.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/OpendaylightFlowStatisticsServiceImpl2Test.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/OpendaylightFlowStatisticsServiceImpl3Test.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/compatibility/AbstractCompatibleStatServiceTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/compatibility/OpendaylightFlowStatisticsServiceDelegateImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/dedicated/StatisticsGatheringOnTheFlyServiceTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/AbstractDirectStatisticsServiceTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/FlowDirectStatisticsServiceTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/GroupDirectStatisticsServiceTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/MeterDirectStatisticsServiceTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/NodeConnectorDirectStatisticsServiceTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceImplTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/QueueDirectStatisticsServiceTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/translator/AggregatedFlowStatisticsTranslatorTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/translator/FlowRemovedTranslatorTest.java [new file with mode: 0644]
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/translator/PacketReceivedTranslatorTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/translator/PortUpdateTranslatorTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/util/DeviceInitializationUtilsTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/util/MdSalRegistrationUtilsTest.java
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/role/OfEntityManager.java
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/sal/OpenflowPluginConfig.java [new file with mode: 0644]
openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/sal/OpenflowPluginProvider.java
openflowplugin/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/common/config/impl/rev140326/ConfigurableOpenFlowProviderModule.java
openflowplugin/src/main/yang/openflow-plugin-cfg-impl.yang
openflowplugin/src/test/java/org/opendaylight/openflowplugin/openflow/md/core/sal/SalRegistrationManagerTest.java
openflowplugin/src/test/java/org/opendaylight/openflowplugin/openflow/md/util/OpenflowPortsUtilTest.java

diff --git a/applications/forwardingrules-sync/pom.xml b/applications/forwardingrules-sync/pom.xml
new file mode 100644 (file)
index 0000000..e9fad3b
--- /dev/null
@@ -0,0 +1,144 @@
+<?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>
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesAddCommitter.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesAddCommitter.java
new file mode 100644 (file)
index 0000000..7b227fd
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * 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);
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesCommitter.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesCommitter.java
new file mode 100644 (file)
index 0000000..9ad8fab
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * 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> {
+}
+
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesRemoveCommitter.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesRemoveCommitter.java
new file mode 100644 (file)
index 0000000..f21bf98
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * 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);
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesUpdateCommitter.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/ForwardingRulesUpdateCommitter.java
new file mode 100644 (file)
index 0000000..d1b3eea
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * 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);
+
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/NodeListener.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/NodeListener.java
new file mode 100644 (file)
index 0000000..df1bab0
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * 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> {
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SemaphoreKeeper.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SemaphoreKeeper.java
new file mode 100644 (file)
index 0000000..8185bb0
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * 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);
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SyncPlanPushStrategy.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SyncPlanPushStrategy.java
new file mode 100644 (file)
index 0000000..9a856cc
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * 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);
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SyncReactor.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/SyncReactor.java
new file mode 100644 (file)
index 0000000..12cffd1
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * 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;
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeCachedDao.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeCachedDao.java
new file mode 100644 (file)
index 0000000..0aebb06
--- /dev/null
@@ -0,0 +1,42 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeDao.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeDao.java
new file mode 100644 (file)
index 0000000..0bc6b99
--- /dev/null
@@ -0,0 +1,23 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeOdlDao.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeOdlDao.java
new file mode 100644 (file)
index 0000000..724c43a
--- /dev/null
@@ -0,0 +1,58 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeSnapshotDao.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/dao/FlowCapableNodeSnapshotDao.java
new file mode 100644 (file)
index 0000000..2c40af7
--- /dev/null
@@ -0,0 +1,40 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/AbstractFrmSyncListener.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/AbstractFrmSyncListener.java
new file mode 100644 (file)
index 0000000..4d1e53a
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * 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();
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/FlowForwarder.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/FlowForwarder.java
new file mode 100644 (file)
index 0000000..f0babcb
--- /dev/null
@@ -0,0 +1,139 @@
+/**
+ * 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;
+    }
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/ForwardingRulesSyncProvider.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/ForwardingRulesSyncProvider.java
new file mode 100644 (file)
index 0000000..57c404b
--- /dev/null
@@ -0,0 +1,203 @@
+/**
+ * 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();
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/FrmExecutors.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/FrmExecutors.java
new file mode 100644 (file)
index 0000000..44e625d
--- /dev/null
@@ -0,0 +1,44 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/GroupForwarder.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/GroupForwarder.java
new file mode 100644 (file)
index 0000000..bb0aec9
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * 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());
+    }
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/MeterForwarder.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/MeterForwarder.java
new file mode 100644 (file)
index 0000000..9e69f2c
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * 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());
+    }
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedConfigListener.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedConfigListener.java
new file mode 100644 (file)
index 0000000..94265ec
--- /dev/null
@@ -0,0 +1,141 @@
+/**
+ * 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;
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedOperationalListener.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedOperationalListener.java
new file mode 100644 (file)
index 0000000..e66bf3f
--- /dev/null
@@ -0,0 +1,224 @@
+/**
+ * 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;
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorFutureDecorator.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorFutureDecorator.java
new file mode 100644 (file)
index 0000000..fb0b3e9
--- /dev/null
@@ -0,0 +1,112 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorFutureWithCompressionDecorator.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorFutureWithCompressionDecorator.java
new file mode 100644 (file)
index 0000000..44f3ee7
--- /dev/null
@@ -0,0 +1,107 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorGuardDecorator.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorGuardDecorator.java
new file mode 100644 (file)
index 0000000..ea019e5
--- /dev/null
@@ -0,0 +1,148 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorImpl.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorImpl.java
new file mode 100644 (file)
index 0000000..de4819e
--- /dev/null
@@ -0,0 +1,209 @@
+/**
+ * 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);
+    }
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/TableForwarder.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/TableForwarder.java
new file mode 100644 (file)
index 0000000..f2dc532
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * 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());
+    }
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyFlatBatchImpl.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyFlatBatchImpl.java
new file mode 100644 (file)
index 0000000..48560a4
--- /dev/null
@@ -0,0 +1,483 @@
+/**
+ * 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;
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyIncrementalImpl.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyIncrementalImpl.java
new file mode 100644 (file)
index 0000000..4a56acb
--- /dev/null
@@ -0,0 +1,546 @@
+/**
+ * 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;
+    }
+
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SynchronizationDiffInput.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SynchronizationDiffInput.java
new file mode 100644 (file)
index 0000000..a0b9fde
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * 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;
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/markandsweep/SwitchFlowId.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/markandsweep/SwitchFlowId.java
new file mode 100644 (file)
index 0000000..86c8cad
--- /dev/null
@@ -0,0 +1,68 @@
+/**\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
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/CrudCounts.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/CrudCounts.java
new file mode 100644 (file)
index 0000000..23a7be8
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * 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--;
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/FlowCapableNodeLookups.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/FlowCapableNodeLookups.java
new file mode 100644 (file)
index 0000000..df3a3db
--- /dev/null
@@ -0,0 +1,111 @@
+/**
+ * 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;
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/FxChainUtil.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/FxChainUtil.java
new file mode 100644 (file)
index 0000000..7eddebb
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * 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);
+            }
+        };
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/ItemSyncBox.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/ItemSyncBox.java
new file mode 100644 (file)
index 0000000..6635d0a
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * 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;
+        }
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/PathUtil.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/PathUtil.java
new file mode 100644 (file)
index 0000000..f4a91c2
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * 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);
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/ReconcileUtil.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/ReconcileUtil.java
new file mode 100644 (file)
index 0000000..e339ccd
--- /dev/null
@@ -0,0 +1,368 @@
+/**
+ * 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());
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/SemaphoreKeeperGuavaImpl.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/SemaphoreKeeperGuavaImpl.java
new file mode 100644 (file)
index 0000000..1a11c9f
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * 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;
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/SyncCrudCounters.java b/applications/forwardingrules-sync/src/main/java/org/opendaylight/openflowplugin/applications/frsync/util/SyncCrudCounters.java
new file mode 100644 (file)
index 0000000..487f065
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * 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);
+    }
+}
diff --git a/applications/forwardingrules-sync/src/main/resources/features.xml b/applications/forwardingrules-sync/src/main/resources/features.xml
new file mode 100644 (file)
index 0000000..8dd8c50
--- /dev/null
@@ -0,0 +1,26 @@
+<?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>
diff --git a/applications/forwardingrules-sync/src/main/resources/org/opendaylight/blueprint/forwardingrules-sync.xml b/applications/forwardingrules-sync/src/main/resources/org/opendaylight/blueprint/forwardingrules-sync.xml
new file mode 100644 (file)
index 0000000..c60e960
--- /dev/null
@@ -0,0 +1,24 @@
+<?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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/DSInputFactory.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/DSInputFactory.java
new file mode 100644 (file)
index 0000000..f41f7d5
--- /dev/null
@@ -0,0 +1,159 @@
+/**
+ * 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();
+    }
+}
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/FlowForwarderTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/FlowForwarderTest.java
new file mode 100644 (file)
index 0000000..1effafd
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * 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());
+    }
+}
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/ForwardingRulesSyncProviderTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/ForwardingRulesSyncProviderTest.java
new file mode 100644 (file)
index 0000000..1b1fac2
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/GroupForwarderTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/GroupForwarderTest.java
new file mode 100644 (file)
index 0000000..2cf2eec
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/MeterForwarderTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/MeterForwarderTest.java
new file mode 100644 (file)
index 0000000..7c82074
--- /dev/null
@@ -0,0 +1,167 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedConfigListenerTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedConfigListenerTest.java
new file mode 100644 (file)
index 0000000..3b5caea
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedOperationalListenerTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SimplifiedOperationalListenerTest.java
new file mode 100644 (file)
index 0000000..4be870f
--- /dev/null
@@ -0,0 +1,113 @@
+/**
+ * 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();
+    }
+}
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorImplTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/SyncReactorImplTest.java
new file mode 100644 (file)
index 0000000..17f470b
--- /dev/null
@@ -0,0 +1,139 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/TableForwarderTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/TableForwarderTest.java
new file mode 100644 (file)
index 0000000..7e88950
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/DiffInputFactory.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/DiffInputFactory.java
new file mode 100644 (file)
index 0000000..a30d600
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * 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();
+    }
+}
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyFlatBatchImplTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyFlatBatchImplTest.java
new file mode 100644 (file)
index 0000000..e83d9b0
--- /dev/null
@@ -0,0 +1,303 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyIncrementalImplTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/impl/strategy/SyncPlanPushStrategyIncrementalImplTest.java
new file mode 100644 (file)
index 0000000..75a34b8
--- /dev/null
@@ -0,0 +1,651 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/util/ReconcileUtilTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/util/ReconcileUtilTest.java
new file mode 100644 (file)
index 0000000..b5d6893
--- /dev/null
@@ -0,0 +1,352 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/util/SemaphoreKeeperTest.java b/applications/forwardingrules-sync/src/test/java/org/opendaylight/openflowplugin/applications/frsync/util/SemaphoreKeeperTest.java
new file mode 100644 (file)
index 0000000..f874eb7
--- /dev/null
@@ -0,0 +1,143 @@
+/**
+ * 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
diff --git a/applications/forwardingrules-sync/src/test/resources/log4j.xml b/applications/forwardingrules-sync/src/test/resources/log4j.xml
new file mode 100644 (file)
index 0000000..f17195f
--- /dev/null
@@ -0,0 +1,20 @@
+<?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
similarity index 96%
rename from applications/old-notification-supplier/pom.xml
rename to applications/notification-supplier/pom.xml
index 6dd663f973ce9ab7d06a0f155a186bc26dc6df5d..4f8cf5cf32f11d32172dd881bd4fa4cca12f193b 100644 (file)
@@ -9,7 +9,7 @@
         <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>
@@ -74,7 +74,7 @@
                         <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>
@@ -6,15 +6,15 @@
  * 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();
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationProviderImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationProviderImpl.java
new file mode 100644 (file)
index 0000000..de2a34f
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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;
+    }
+}
+
@@ -6,20 +6,20 @@
  * 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
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItem.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItem.java
new file mode 100644 (file)
index 0000000..52347e5
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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);
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItemRoot.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItemRoot.java
new file mode 100644 (file)
index 0000000..b2c4020
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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);
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItemStat.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationSupplierForItemStat.java
new file mode 100644 (file)
index 0000000..781b160
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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);
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/AbstractNotificationSupplierBase.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/AbstractNotificationSupplierBase.java
new file mode 100644 (file)
index 0000000..2659f24
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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();
+    }
+
+}
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/AbstractNotificationSupplierForItemRoot.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/AbstractNotificationSupplierForItemRoot.java
new file mode 100644 (file)
index 0000000..a8e8d21
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * 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);
+                    }
+                }
+            }
+        }
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeConnectorNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeConnectorNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..f87a584
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..19ea605
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/AbstractNotificationSupplierForItem.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/AbstractNotificationSupplierForItem.java
new file mode 100644 (file)
index 0000000..1dd8758
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/FlowNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/FlowNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..e982146
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/GroupNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/GroupNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..4f2250f
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/MeterNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/MeterNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..badd371
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/package-info.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/package-info.java
new file mode 100644 (file)
index 0000000..9e50233
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/AbstractNotificationSupplierForItemStat.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/AbstractNotificationSupplierForItemStat.java
new file mode 100644 (file)
index 0000000..2898b35
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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);
+                    }
+                }
+            }
+        }
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowStatNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowStatNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..7e51e44
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowTableStatNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowTableStatNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..9bcd21c
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/GroupStatNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/GroupStatNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..9de8cdb
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/MeterStatNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/MeterStatNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..db1c4f4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/NodeConnectorStatNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/NodeConnectorStatNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..00580dc
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/QueueStatNotificationSupplierImpl.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/QueueStatNotificationSupplierImpl.java
new file mode 100644 (file)
index 0000000..b27e418
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/package-info.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/package-info.java
new file mode 100644 (file)
index 0000000..f71f623
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/package-info.java b/applications/notification-supplier/src/main/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/package-info.java
new file mode 100644 (file)
index 0000000..8c73ccc
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * 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
@@ -6,12 +6,12 @@
  * 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;
@@ -23,7 +23,7 @@ public class OldNotifProviderConfig {
     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();
@@ -71,11 +71,11 @@ public class OldNotifProviderConfig {
         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;
@@ -158,8 +158,8 @@ public class OldNotifProviderConfig {
             this.flowStatSupport = flowStatSupport;
         }
 
-        public OldNotifProviderConfig build() {
-            return new OldNotifProviderConfig(this);
+        public NotificationProviderConfig build() {
+            return new NotificationProviderConfig(this);
         }
     }
 }
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/notification/supplier/rev150820/NotifModule.java b/applications/notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/notification/supplier/rev150820/NotifModule.java
new file mode 100644 (file)
index 0000000..00c774c
--- /dev/null
@@ -0,0 +1,22 @@
+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();
+    }
+
+}
diff --git a/applications/notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/notification/supplier/rev150820/NotifModuleFactory.java b/applications/notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/notification/supplier/rev150820/NotifModuleFactory.java
new file mode 100644 (file)
index 0000000..fcb8094
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+* 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 {
+
+}
similarity index 86%
rename from applications/old-notification-supplier/src/main/resources/initial/73-old-notification-supplier.xml
rename to applications/notification-supplier/src/main/resources/initial/73-notification-supplier.xml
index 663bd1040a11c901f05eb4314a26b0fef26ebc5f..5b47dc9381a3f8c59ae55b13f3bc80fd47a90065 100644 (file)
@@ -13,8 +13,8 @@
             <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>
@@ -26,7 +26,7 @@
                         <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>
@@ -37,7 +37,7 @@
                         <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>
 
@@ -46,7 +46,7 @@
     </configuration>
 
     <required-capabilities>
-        <capability>urn:opendaylight:params:xml:ns:yang:openflow:applications:old-notification-supplier?module=old-notification-supplier&amp;revision=2015-08-20</capability>
+        <capability>urn:opendaylight:params:xml:ns:yang:openflow:applications:notification-supplier?module=notification-supplier&amp;revision=2015-08-20</capability>
         <capability>urn:opendaylight:flow:inventory?module=flow-node-inventory&amp;revision=2013-08-19</capability>
         <capability>urn:opendaylight:inventory?module=opendaylight-inventory&amp;revision=2013-08-19</capability>
         <capability>urn:opendaylight:flow:types?module=opendaylight-flow-types&amp;revision=2013-10-26</capability>
similarity index 85%
rename from applications/old-notification-supplier/src/main/yang/old-notification-supplier.yang
rename to applications/notification-supplier/src/main/yang/notification-supplier.yang
index 76a45845b8aff9bae5716d0e68760b97babb86fd..3b089f9b12b543e6957b8ad9536ea3ba6b45f941 100644 (file)
@@ -1,26 +1,26 @@
-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 {
@@ -40,7 +40,7 @@ module old-notification-supplier {
                 }
             }
 
-            container old-notification-supplier-settings {
+            container notification-supplier-settings {
                 leaf flow-support {
                     type boolean;
                     default true;
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationProviderImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/NotificationProviderImplTest.java
new file mode 100644 (file)
index 0000000..0325190
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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();
+    }
+}
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeConnectorNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeConnectorNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..3c6b038
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/NodeNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..771b6dc
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/helper/TestChangeEventBuildHelper.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/helper/TestChangeEventBuildHelper.java
new file mode 100644 (file)
index 0000000..13c1682
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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;
+            }
+        };
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/helper/TestSupplierVerifyHelper.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/helper/TestSupplierVerifyHelper.java
new file mode 100644 (file)
index 0000000..8c41ab1
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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));
+    }
+}
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/FlowNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/FlowNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..5f765f6
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/GroupNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/GroupNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..fc890c7
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/MeterNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/MeterNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..e9f9b28
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowStatNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowStatNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..cb41a07
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowTableStatNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/FlowTableStatNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..13ecb40
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/GroupStatNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/GroupStatNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..47fa810
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/MeterStatNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/MeterStatNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..c7974ee
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/NodeConnectorStatNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/NodeConnectorStatNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..a94c4db
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/QueueStatNotificationSupplierImplTest.java b/applications/notification-supplier/src/test/java/org/opendaylight/openflowplugin/applications/notification/supplier/impl/item/stat/QueueStatNotificationSupplierImplTest.java
new file mode 100644 (file)
index 0000000..e488b64
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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();
+    }
+}
+
diff --git a/applications/old-notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/old/notification/supplier/rev150820/OldNotifModule.java b/applications/old-notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/old/notification/supplier/rev150820/OldNotifModule.java
deleted file mode 100644 (file)
index d892283..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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();
-    }
-}
diff --git a/applications/old-notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/old/notification/supplier/rev150820/OldNotifModuleFactory.java b/applications/old-notification-supplier/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/applications/old/notification/supplier/rev150820/OldNotifModuleFactory.java
deleted file mode 100644 (file)
index 961ca9b..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
-* 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 {
-
-}
index 3ec52403bf38829a46462d64544e583164f31ef9..c7e776a7f6f135f08cc1e42fa4c099134366fefe 100644 (file)
@@ -88,7 +88,7 @@
         <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>
@@ -96,6 +96,7 @@
         <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 -->
index aa4f756007781a97cab86cd9ff2476056ee37e9a..ee6e912a6374ba8f7f1c625d36129a8992d860d5 100644 (file)
@@ -57,6 +57,7 @@ public class MatchComparatorHelper {
     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
@@ -302,13 +303,22 @@ public class MatchComparatorHelper {
         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) {
index db4dfecfc2389a80093ed32edd2712fe0412ed51..d31a24b97e490a46795fb43748809b59b1196299 100644 (file)
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertTrue;
 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;
 
@@ -18,6 +19,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.
 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)
@@ -209,6 +211,33 @@ public class MatchComparatorHelperTest {
                 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();
index 504fc1f9f3e33f27517befa0c2d7b9afeaf0e6bd..82ba5748cd3fd274858de4c74b926e872d333e22 100644 (file)
                 <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>
index 6d8a8853a6ddd303186c7ee8377f24eb258833d2..62d49173bbe057832a356a4773a5c3ef93028174 100644 (file)
             <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>
index 000a3d46fe58541fa076bac1ca32a8591f88f7ba..9872cf47ca44d1960827f6417618694cbe9fc379 100644 (file)
         <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>
diff --git a/model/model-flow-statistics/src/main/yang/opendaylight-direct-statistics.yang b/model/model-flow-statistics/src/main/yang/opendaylight-direct-statistics.yang
new file mode 100644 (file)
index 0000000..e4037a4
--- /dev/null
@@ -0,0 +1,123 @@
+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;
+        }
+    }
+}
index 62188f8fdf66c988d62f4d5b3530e5fc417e4c01..b2c70f70535cd40d58c6e22fb07a6199f927c3af 100644 (file)
@@ -15,10 +15,6 @@ import java.util.List;
 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;
@@ -31,8 +27,6 @@ import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext
 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>
@@ -54,6 +48,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 public interface DeviceContext extends AutoCloseable,
         DeviceReplyProcessor,
         PortNumberCache,
+        TxFacade,
         XidSequencer {
 
     void setStatisticsRpcEnabled(boolean isStatisticsRpcEnabled);
@@ -138,38 +133,12 @@ public interface DeviceContext extends AutoCloseable,
      */
     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.
      *
index d5fcf47ce94b0beb337487d2ed15af5d988afe11..1cbc3c28039dbaaba0b9cb5ca884e4a12b81f494 100644 (file)
@@ -123,10 +123,6 @@ public interface DeviceState {
 
     void setDeviceSynchronized(boolean deviceSynchronized);
 
-    void setRole(OfpRole ofpRole);
-
-    OfpRole getRole();
-
     boolean isStatisticsPollingEnabled();
 
     void setStatisticsPollingEnabledProp(boolean statPollEnabled);
index 8addb4d8dcaf215bb1cae5f0e125ed89bd333413..4738218827153ce24a9390523a25dc4cee0a0d1d 100644 (file)
@@ -18,10 +18,10 @@ public interface MessageTranslator<I, O> {
     /**
      * 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);
 
 }
diff --git a/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/device/TxFacade.java b/openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/device/TxFacade.java
new file mode 100644 (file)
index 0000000..e8a8ccb
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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();
+}
index 63042e0b742f6a449634b1905a10eeb584fe66ff..879bd63acd20635160c810159ac55ed850263225 100644 (file)
@@ -12,6 +12,7 @@ import io.netty.util.Timeout;
 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;
 
@@ -72,6 +73,12 @@ public interface LifecycleConductor {
      */
     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
index 7380e5656e387ff56ea8058a6022400b01781266..9bc2cda8293d1d403459345922d1de590057ec22 100644 (file)
@@ -55,4 +55,7 @@ public interface StatisticsContext extends RequestContextStack, AutoCloseable {
 
     @Override
     void close();
+
+    void setSchedulingEnabled(boolean schedulingEnabled);
+    boolean isSchedulingEnabled();
 }
index 827479297193c4f9e23dfeb32fb420981cd84deb..e1588be274db166ca8e3adf085dd9f251afd417e 100644 (file)
@@ -11,6 +11,7 @@ package org.opendaylight.openflowplugin.api.openflow.statistics;
 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 &lt;mbobak@cisco.com&gt; on 26.2.2015.
@@ -18,6 +19,9 @@ import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceLifecy
 public interface StatisticsManager extends DeviceLifecycleSupervisor, DeviceInitializationPhaseHandler,
         DeviceTerminationPhaseHandler, AutoCloseable {
 
+    void startScheduling(NodeId nodeId);
+    void stopScheduling(NodeId nodeId);
+
     @Override
     void close();
 }
index f6c9453c26fc2b636e329011379d542376de1f48..70831eb720f6ad1ce5e52f106045456b707a98ba 100644 (file)
@@ -117,11 +117,13 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
                 <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>
index f0ff123e24d364e4cf58a60720df96e14dcf7bfc..ddf73a75cc468af369bcfa7fa373c5f024d6c1b2 100644 (file)
@@ -8,6 +8,7 @@
 
 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;
@@ -23,6 +24,7 @@ import org.opendaylight.openflowplugin.api.openflow.lifecycle.DeviceContextChang
 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;
@@ -47,6 +49,7 @@ public final class LifecycleConductorImpl implements LifecycleConductor, RoleCha
     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);
@@ -59,12 +62,19 @@ public final class LifecycleConductorImpl implements LifecycleConductor, RoleCha
         }
     }
 
+    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;
         }
@@ -113,16 +123,13 @@ public final class LifecycleConductorImpl implements LifecycleConductor, RoleCha
                 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);
@@ -186,4 +193,10 @@ public final class LifecycleConductorImpl implements LifecycleConductor, RoleCha
             LOG.info("initialization phase for node {} in device context was successful. All phases initialized OK.", nodeId);
         }
     }
+
+    @VisibleForTesting
+    public boolean isServiceChangeListenersEmpty() {
+        return this.serviceChangeListeners.isEmpty();
+    }
+
 }
index 84120ddc383c8da20a3bcb3d2e7bb42a4a2aa28d..ab64a090c49b745e31b2bd889fe93d98afbe428b 100644 (file)
@@ -204,6 +204,7 @@ public class OpenFlowPluginProviderImpl implements OpenFlowPluginProvider, OpenF
 
         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);
index a0a20a9a00d3f308310642f7542f8dee828067e3..191ea623ff3315e0dfc397a9cb1f1c03008bcbe1 100644 (file)
@@ -148,7 +148,7 @@ public class ConnectionContextImpl implements ConnectionContext {
             try {
                 handshakeContext.close();
             } catch (Exception e) {
-                LOG.info("handshake context closing failed: ", e);
+                LOG.error("handshake context closing failed:{} ", e);
             } finally {
                 handshakeContext = null;
             }
index 544e7124ba44bb6627efc050820b2803a5715545..31d986c2fc224046827df46db140fd8910986c37 100644 (file)
@@ -9,7 +9,7 @@
 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;
@@ -92,7 +92,7 @@ public class ConnectionManagerImpl implements ConnectionManager {
             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);
     }
 
     /**
index 285b3cd954b381aea116e44421e883f0709ad71e..e7142642b85c6010eaac4735bbb8de3e379c771f 100644 (file)
@@ -54,7 +54,7 @@ public class HandshakeContextImpl implements HandshakeContext {
         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()) {
index cc2aaa508f00c4e2882454b48079ffe3308f3080..49d0c2d463623a2e0d21ad8832ec7dcecb0205a0 100644 (file)
@@ -68,7 +68,7 @@ public class OutboundQueueProviderImpl implements OutboundQueueProvider {
                     try {
                         wait();
                     } catch (InterruptedException e) {
-                        LOG.info("Interrupted while waiting for entry", e);
+                        LOG.error("Interrupted while waiting for entry", e);
                         return null;
                     }
                 }
index 41610e7822b33eb738865f79d1f2c88996a22165..0047deccac2984e737b886273ad611fa7e07fee3 100644 (file)
@@ -37,8 +37,10 @@ public class ConnectionReadyListenerImpl implements ConnectionReadyListener {
 
     @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) {
@@ -52,25 +54,27 @@ public class ConnectionReadyListenerImpl implements ConnectionReadyListener {
                         // 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());
         }
     }
 
index 037dc200f9f37e092d10aecdb4da309b145fb94a..f843f59194289cc08368722d94636621fe8d68c0 100644 (file)
@@ -68,7 +68,7 @@ public class HandshakeListenerImpl implements HandshakeListener {
                     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);
@@ -77,7 +77,7 @@ public class HandshakeListenerImpl implements HandshakeListener {
 
             @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);
             }
         });
@@ -103,8 +103,8 @@ public class HandshakeListenerImpl implements HandshakeListener {
         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);
         }
     }
 
index 585c58caec75d27eda1f3c57b83725346a61ea03..ff8eb07bd4559cc63325c75cb7d56a9c038fb8d0 100644 (file)
@@ -46,7 +46,9 @@ public class OpenflowProtocolListenerInitialImpl implements OpenflowProtocolList
 
     @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());
@@ -57,22 +59,23 @@ public class OpenflowProtocolListenerInitialImpl implements OpenflowProtocolList
 
     @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)) {
@@ -88,29 +91,31 @@ public class OpenflowProtocolListenerInitialImpl implements OpenflowProtocolList
                     // 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);
     }
 
     /**
index 17a86f0c12e18016638ab67f8e102ae37a1ad1ff..441725723740f439ac10f78b1f03bc1e5d2c5028 100644 (file)
@@ -40,6 +40,7 @@ import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
 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;
@@ -155,7 +156,7 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
     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) {
@@ -163,13 +164,14 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
         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);
@@ -331,7 +333,12 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
     @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
@@ -391,7 +398,7 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
         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
@@ -405,6 +412,8 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
                         .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());
@@ -415,7 +424,7 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
     @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 {
@@ -445,7 +454,7 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
     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());
@@ -512,7 +521,7 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
             // 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);
         }
     }
 
@@ -659,4 +668,9 @@ public class DeviceContextImpl implements DeviceContext, ExtensionConverterProvi
         deviceState.setValid(false);
         return transactionChainManager.shuttingDown();
     }
+
+    @VisibleForTesting
+    TransactionChainManager getTransactionChainManager() {
+        return this.transactionChainManager;
+    }
 }
index 4eb33295a200f781d5366fd3d7e1975ae8df5447..31df880261d5dd668047c2b57febe1142531ca44 100644 (file)
@@ -27,7 +27,6 @@ import javax.annotation.CheckForNull;
 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;
@@ -161,7 +160,7 @@ public class DeviceManagerImpl implements DeviceManager, ExtensionConverterProvi
         final DeviceContext deviceContext = new DeviceContextImpl(connectionContext,
                 deviceState,
                 dataBroker,
-                conductor.getMessageIntelligenceAgency(),
+                conductor,
                 outboundQueueProvider,
                 translatorLibrary,
                 switchFeaturesMandatory);
index efbeefdbc7ea41eca83ca37ad0ca8b4f34fff330..16864cd33978183ee249314dbfa55eca8800c892 100644 (file)
@@ -48,7 +48,6 @@ class DeviceStateImpl implements DeviceState {
     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);
@@ -58,7 +57,6 @@ class DeviceStateImpl implements DeviceState {
         version = featuresReply.getVersion();
         statPollEnabled = false;
         deviceSynchronized = false;
-        role = OfpRole.BECOMESLAVE;
     }
 
     @Override
@@ -162,16 +160,6 @@ class DeviceStateImpl implements DeviceState {
         deviceSynchronized = _deviceSynchronized;
     }
 
-    @Override
-    public OfpRole getRole() {
-        return role;
-    }
-
-    @Override
-    public void setRole(OfpRole role) {
-        this.role = role;
-    }
-
     @Override
     public boolean isStatisticsPollingEnabled() {
         return statPollEnabled;
index 4276bfe54bcf484267d3ed05a24da5b9c7b2b8a3..d85dab8f59632457938b55b0344792df1767e0c4 100644 (file)
@@ -26,6 +26,7 @@ import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 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;
@@ -55,6 +56,7 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
     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;
@@ -65,6 +67,8 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
     @GuardedBy("txLock")
     private ListenableFuture<Void> lastSubmittedFuture;
 
+    private boolean initCommit;
+
     public TransactionChainManagerStatus getTransactionChainManagerStatus() {
         return transactionChainManagerStatus;
     }
@@ -72,8 +76,11 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
     @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);
@@ -111,6 +118,7 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
                 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());
@@ -128,7 +136,7 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
         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.");
@@ -170,7 +178,9 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
             Futures.addCallback(submitFuture, new FutureCallback<Void>() {
                 @Override
                 public void onSuccess(final Void result) {
-                    //no action required
+                    if (initCommit) {
+                        initCommit = false;
+                    }
                 }
 
                 @Override
@@ -180,6 +190,10 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
                     } else {
                         LOG.error("Exception during transaction submitting. {}", t);
                     }
+                    if (initCommit) {
+                        LOG.error("Initial commit failed. {}", t);
+                        conductor.closeConnection(nodeId());
+                    }
                 }
             });
             lastSubmittedFuture = submitFuture;
@@ -201,11 +215,13 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
     }
 
     <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.");
@@ -216,7 +232,7 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
     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();
         }
     }
@@ -285,7 +301,8 @@ class TransactionChainManager implements TransactionChainListener, AutoCloseable
 
     @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) {
index 8569d45ba0af2653ce72515779e5a16bb1948f42..eb7bf9e5f659a7780c681db013838cd4c04b3fae 100644 (file)
@@ -43,7 +43,9 @@ public class OpenflowProtocolListenerFullImpl implements OpenflowMessageListener
 
     @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());
@@ -76,7 +78,9 @@ public class OpenflowProtocolListenerFullImpl implements OpenflowMessageListener
 
     @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);
     }
 
index 9e8d940babe3245f63ae61414196a70b45148e13..ce2040ce5e58c0a754bfe8c2209dc25b7ecab71c 100644 (file)
@@ -275,8 +275,8 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
         });
     }
 
-
-    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());
@@ -356,6 +356,16 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
         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);
index bc3545bb9ac178f5f605bd73f3a3381f00054fd1..91feab60f6580be9199c1d9046cb845fde4c3780 100644 (file)
@@ -7,6 +7,7 @@
  */
 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;
@@ -72,7 +73,8 @@ public class RpcContextImpl implements RpcContext {
 
     @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;
     }
 
@@ -130,4 +132,11 @@ public class RpcContextImpl implements RpcContext {
             LOG.debug("Unregistration serviceClass {} for Node {}", serviceClass, nodeInstanceIdentifier.getKey().getId());
         }
     }
+
+    @VisibleForTesting
+    public boolean isEmptyRpcRegistrations() {
+        return this.rpcRegistrations.isEmpty();
+    }
+
+
 }
index 063ea26ef08ea1bd94244572241a863b9e820133..3619e66e914d0f15eeaf0ba0940fc4b27ddac12f 100644 (file)
@@ -7,6 +7,7 @@
  */
 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;
@@ -91,4 +92,14 @@ public class RpcManagerImpl implements RpcManager {
     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);
+        }
+    }
 }
index b665258ebcb6a66c8ca957b082bfbb6058e6c839..0d7020030500355cfdb59ed37a07cae73f2b83b7 100644 (file)
@@ -23,7 +23,9 @@ public abstract class AbstractMultipartOnTheFlyService<I> extends AbstractServic
 
     @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());
     }
 
 
index 5888d2b443ac3da370303f9619842874221aea05..f1bdbf116da899234e9f5a02b5c49e90875194a6 100644 (file)
@@ -14,15 +14,16 @@ import com.google.common.util.concurrent.Futures;
 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;
@@ -40,18 +41,20 @@ abstract class AbstractService<I, O> {
     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() {
@@ -70,6 +73,10 @@ abstract class AbstractService<I, O> {
         return datapathId;
     }
 
+    public NodeId getNodeId() {
+        return nodeId;
+    }
+
     public RequestContextStack getRequestContextStack() {
         return requestContextStack;
     }
@@ -101,16 +108,16 @@ abstract class AbstractService<I, O> {
         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;
index aacfa703c5744cec709595871d1e90d65deb577e..c00d092f96273335af1233e70141f3213a3a2ce5 100644 (file)
@@ -12,8 +12,11 @@ import com.google.common.util.concurrent.Futures;
 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;
@@ -32,21 +35,30 @@ import org.slf4j.LoggerFactory;
 
 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() {
@@ -77,13 +89,14 @@ final class MultipartRequestOnTheFlyCallback extends AbstractRequestCallback<Lis
             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);
@@ -93,7 +106,8 @@ final class MultipartRequestOnTheFlyCallback extends AbstractRequestCallback<Lis
 
                 @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();
@@ -110,7 +124,7 @@ final class MultipartRequestOnTheFlyCallback extends AbstractRequestCallback<Lis
         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;
     }
 }
index 0bf66fa960a12eaaf04c6279edf3097d677412cb..c677c53a68247079504cb9a8664125eb798020f6 100644 (file)
@@ -54,8 +54,7 @@ public class RoleService extends AbstractSimpleService<RoleRequestInputBuilder,
     }
 
     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();
@@ -75,7 +74,7 @@ public class RoleService extends AbstractSimpleService<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 {
@@ -98,7 +97,7 @@ public class RoleService extends AbstractSimpleService<RoleRequestInputBuilder,
 
     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);
@@ -111,7 +110,7 @@ public class RoleService extends AbstractSimpleService<RoleRequestInputBuilder,
             @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) {
@@ -133,7 +132,7 @@ public class RoleService extends AbstractSimpleService<RoleRequestInputBuilder,
             @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);
             }
         });
index cb2227769fb5b5e415a6f8395e2a6c66f04c236d..32aa20c843413c61f63c9c9ac28f11d45c71c877 100644 (file)
@@ -46,8 +46,6 @@ public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput
 
     private final DeviceContext deviceContext;
     private final RoleService roleService;
-    private final NodeId nodeId;
-    private final Short version;
 
     private final Semaphore currentRoleGuard = new Semaphore(1, true);
 
@@ -58,8 +56,6 @@ public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput
         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
@@ -72,9 +68,9 @@ public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput
         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.
@@ -99,7 +95,7 @@ public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput
 
             @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();
             }
@@ -129,6 +125,7 @@ public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput
             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);
@@ -147,29 +144,29 @@ public final class SalRoleServiceImpl extends AbstractSimpleService<SetRoleInput
                     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);
         });
     }
index 1c218b37b2c4a56eec0b2c1ae91fc2ec9449c117..90d5dc62cf24ad8573559705d5f579023a6355b2 100644 (file)
@@ -18,6 +18,7 @@ import java.util.concurrent.Future;
 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;
@@ -52,9 +53,14 @@ import org.slf4j.Logger;
 
 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
@@ -95,7 +101,7 @@ public final class SalTableServiceImpl extends AbstractMultipartService<UpdateTa
 
             @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());
             }
@@ -113,19 +119,18 @@ public final class SalTableServiceImpl extends AbstractMultipartService<UpdateTa
 
         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(
index ad111387be91dcc2e326b044ec456ff8557af0bd..6c8d587854bb42d827dccaa50427e3a449201445 100644 (file)
@@ -111,16 +111,15 @@ public class SinglePurposeMultipartReplyTranslator {
     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();
@@ -129,7 +128,7 @@ public class SinglePurposeMultipartReplyTranslator {
                     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;
@@ -167,7 +166,7 @@ public class SinglePurposeMultipartReplyTranslator {
                         NodeConnectorStatisticsAndPortNumberMapBuilder statsBuilder =
                                 new NodeConnectorStatisticsAndPortNumberMapBuilder();
                         statsBuilder.setNodeConnectorId(
-                                InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(features.getDatapathId(),
+                                InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(datapathId,
                                         portStats.getPortNo(), ofVersion));
 
                         BytesBuilder bytesBuilder = new BytesBuilder();
@@ -396,7 +395,7 @@ public class SinglePurposeMultipartReplyTranslator {
                         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()));
@@ -408,7 +407,7 @@ public class SinglePurposeMultipartReplyTranslator {
                         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());
index e02633ae77197d71059ce18afd5033e1ff6302e3..eafdc7a4aaf53c144ca115520458f5b870accd3f 100644 (file)
@@ -64,6 +64,7 @@ public class StatisticsContextImpl implements StatisticsContext {
     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;
@@ -179,14 +180,25 @@ public class StatisticsContextImpl implements StatisticsContext {
 
     @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) {
index cc97c8fcc6bab1c55863c43ce6ab049126d1344b..4933f28374008e089049c67978ba84ae55fe0756 100644 (file)
@@ -26,6 +26,9 @@ import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.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;
@@ -157,7 +160,9 @@ public final class StatisticsGatheringUtils {
 
                         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);
                             }
@@ -214,7 +219,7 @@ public final class StatisticsGatheringUtils {
     }
 
     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()) {
@@ -233,12 +238,14 @@ public final class StatisticsGatheringUtils {
 
     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;
@@ -246,8 +253,11 @@ public final class StatisticsGatheringUtils {
         });
     }
 
-    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()) {
@@ -256,13 +266,13 @@ public final class StatisticsGatheringUtils {
 
                     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) {
@@ -282,11 +292,13 @@ public final class StatisticsGatheringUtils {
         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);
 
@@ -311,10 +323,10 @@ public final class StatisticsGatheringUtils {
                         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);
                 }
@@ -353,7 +365,7 @@ public final class StatisticsGatheringUtils {
     }
 
     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()) {
@@ -385,7 +397,7 @@ public final class StatisticsGatheringUtils {
 
     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();
@@ -439,7 +451,7 @@ public final class StatisticsGatheringUtils {
     }
 
     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()) {
 
@@ -455,8 +467,8 @@ public final class StatisticsGatheringUtils {
         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);
     }
 
     /**
index 07e4b6df929c12f80a0b3e32fb8e780930cc75f6..ca217555cf0e4699935c1bbdcad9433b4e0c000d 100644 (file)
@@ -8,15 +8,6 @@
 
 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;
@@ -27,16 +18,6 @@ import com.google.common.util.concurrent.Futures;
 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;
@@ -52,13 +33,22 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow
 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);
@@ -72,7 +62,7 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
 
     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);
@@ -104,37 +94,34 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
         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>() {
@@ -167,19 +154,19 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
             @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
@@ -217,7 +204,7 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
     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);
@@ -258,7 +245,7 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
                             }
                             break;
                         default:
-                            LOG.warn("statistics work mode not supported: {}", targetWorkMode);
+                            LOG.warn("Statistics work mode not supported: {}", targetWorkMode);
                     }
                 }
                 workMode = targetWorkMode;
@@ -273,6 +260,50 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
         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) {
index e71017b4780b049c2b5ed0dba7c91a0e96966476..af49302a778ed1c4fe1d69aae8d01050ec8fcfb3 100644 (file)
@@ -38,8 +38,19 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731
 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
@@ -76,12 +87,11 @@ public final class AggregateFlowsInTableService extends AbstractCompatibleStatSe
         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)
index 1d812694de4ccb8ebe3cedc62fd80764d75e7ef8..f063b2ef51d69511fc26f61ba722e84eabb3e2a6 100644 (file)
@@ -40,7 +40,12 @@ public final class FlowsInTableService extends AbstractCompatibleStatService<Get
     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());
index e15c0166b8373699f03ccbc28c23ad560521d1af..3de2abdba19144abd0289e9ca620cd206f7191d5 100644 (file)
@@ -61,8 +61,7 @@ final class MatchingFlowsInTableService extends AbstractMultipartService<GetAggr
             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);
 
index cd127d6e16f68a03b3ba742049742e70c15a17e1..cabe1cffa40ced08d7025e5d7264d39d285a590f 100644 (file)
@@ -50,7 +50,6 @@ public class OpendaylightFlowStatisticsServiceImpl implements OpendaylightFlowSt
                 @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);
@@ -59,7 +58,7 @@ public class OpendaylightFlowStatisticsServiceImpl implements OpendaylightFlowSt
                         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 =
@@ -82,10 +81,18 @@ public class OpendaylightFlowStatisticsServiceImpl implements OpendaylightFlowSt
     };
 
     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
index 47eb56819a2c58edf5beee18953b67483c2e8e3f..1a5c5a1d5f3473d87f372a5c50b55885e46c2c4b 100644 (file)
@@ -42,7 +42,7 @@ public abstract class AbstractCompatibleStatService<I, O, N extends Notification
     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() {
index 05fc1ba2a95c3a31e62744d6b00abf4141ab604c..199afcaccbf4f04ef4a2c6a817ddafbc43b1b9de 100644 (file)
@@ -49,7 +49,7 @@ public class OpendaylightFlowStatisticsServiceDelegateImpl implements Opendaylig
                                                          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);
index a38c4b480a44d13a687fd933e7638a3ec358c7c4..6dfcfa05405cccad2982993b662de6a5a6777185 100644 (file)
@@ -38,7 +38,7 @@ public class StatisticsGatheringOnTheFlyService extends AbstractMultipartOnTheFl
 
     @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);
index 9a2eb17de6d38b62b479bea658d4923b969b3af6..c43ae604fc8af3fee422d8b2e9ef5d61055285ba 100644 (file)
@@ -38,7 +38,7 @@ public class StatisticsGatheringService extends AbstractMultipartService<Multipa
 
     @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);
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/AbstractDirectStatisticsService.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/AbstractDirectStatisticsService.java
new file mode 100644 (file)
index 0000000..e809ebf
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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;
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/FlowDirectStatisticsService.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/FlowDirectStatisticsService.java
new file mode 100644 (file)
index 0000000..72066b0
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * 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);
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/GroupDirectStatisticsService.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/GroupDirectStatisticsService.java
new file mode 100644 (file)
index 0000000..b7c650a
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/MeterDirectStatisticsService.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/MeterDirectStatisticsService.java
new file mode 100644 (file)
index 0000000..da6475e
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/NodeConnectorDirectStatisticsService.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/NodeConnectorDirectStatisticsService.java
new file mode 100644 (file)
index 0000000..6f6f496
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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);
+        }
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceImpl.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceImpl.java
new file mode 100644 (file)
index 0000000..b96ae21
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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();
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceProvider.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceProvider.java
new file mode 100644 (file)
index 0000000..daecefb
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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)));
+    }
+}
diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/QueueDirectStatisticsService.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/QueueDirectStatisticsService.java
new file mode 100644 (file)
index 0000000..88f97dc
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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
index 7a486725448000616e47b9a1ad102f59171232bf..1381bf9a2d57a5a934483c62291261de8ad76b8d 100644 (file)
@@ -9,6 +9,7 @@
 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;
@@ -23,7 +24,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731
  */
 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();
index d11c8b6ad2ddb41c56affd31b7978357bc707911..956389a10362294c029f8d2901c5b707538d7b35 100644 (file)
@@ -8,6 +8,7 @@
 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;
@@ -23,19 +24,19 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731
 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);
     }
 }
index b09b8fe13564d5388239f11dd3332ed5ee2d51f6..2436faeaebd3908b79a123a378fb478100f9e753 100644 (file)
@@ -9,6 +9,7 @@
 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;
@@ -19,8 +20,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731
  */
 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);
     }
 }
index 7a1a2caee5ccd12000f3968ec82dd70c99bc4ae8..9e9793d66ca62f02a4e624f7bcf0549b7fff268e 100644 (file)
@@ -12,6 +12,7 @@ import com.google.common.annotations.VisibleForTesting;
 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;
@@ -36,10 +37,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.Table
  */
 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()));
@@ -52,7 +53,7 @@ public class PacketReceivedTranslator implements MessageTranslator<PacketInMessa
         }
 
         // 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
index 643d6aed831a7458e793f68b91815ea1eea3f607..6c35a2a50ec6692c994ef4fe15d72e3329549289 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.openflowplugin.impl.translator;
 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;
@@ -24,17 +25,17 @@ public class PortUpdateTranslator implements MessageTranslator<PortGrouping, Flo
 
     @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()));
index 00ed2fb06af5d760a3978b1328250ec8b2fb64cb..d4da3cc131c277265c5705d961d0e16dcd444c47 100644 (file)
@@ -122,7 +122,7 @@ public class DeviceInitializationUtils {
             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);
@@ -318,7 +318,7 @@ public class DeviceInitializationUtils {
                             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();
index 49cc546dcb3f9d1a05ebcf942b39812e357486a9..05806e9b9a120077b26bfd59ab8a2572e43e2602 100644 (file)
@@ -37,6 +37,14 @@ import org.opendaylight.openflowplugin.impl.statistics.services.OpendaylightMete
 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;
@@ -96,11 +104,20 @@ public class MdSalRegistrationUtils {
         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),
@@ -152,6 +169,7 @@ public class MdSalRegistrationUtils {
         rpcContext.unregisterRpcServiceImplementation(SalFlatBatchService.class);
         // TODO: experimenter symmetric and multipart message services
         rpcContext.unregisterRpcServiceImplementation(SalExperimenterMessageService.class);
+        rpcContext.unregisterRpcServiceImplementation(OpendaylightDirectStatisticsService.class);
     }
 
     /**
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/LifecycleConductorImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/LifecycleConductorImplTest.java
new file mode 100644 (file)
index 0000000..63c28cd
--- /dev/null
@@ -0,0 +1,269 @@
+/**
+ * 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();
+    }
+}
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImplTest.java
new file mode 100644 (file)
index 0000000..3b6f008
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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
index 58e85ca002a09479ca5771ae4b9c6e0ee13a2ed9..07e345168a52e4d77b538a2b47bf6a9f0e84cdda 100644 (file)
@@ -10,11 +10,14 @@ package org.opendaylight.openflowplugin.impl.device;
 
 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;
@@ -28,7 +31,6 @@ import java.net.InetSocketAddress;
 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;
@@ -46,6 +48,7 @@ import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 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;
@@ -64,13 +67,18 @@ import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowRegistryKe
 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;
@@ -86,19 +94,13 @@ 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.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;
@@ -130,6 +132,8 @@ public class DeviceContextImplTest {
     @Mock
     DeviceState deviceState;
     @Mock
+    GetFeaturesOutput featuresOutput;
+    @Mock
     DataBroker dataBroker;
     @Mock
     WriteTransaction wTx;
@@ -164,6 +168,8 @@ public class DeviceContextImplTest {
 
     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());
@@ -198,17 +204,26 @@ public class DeviceContextImplTest {
         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());
@@ -216,42 +231,45 @@ public class DeviceContextImplTest {
 
     @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
@@ -266,6 +284,17 @@ public class DeviceContextImplTest {
         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();
@@ -282,26 +311,25 @@ public class DeviceContextImplTest {
     }
 
     /**
-     * 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
@@ -328,6 +356,13 @@ public class DeviceContextImplTest {
         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);
@@ -459,8 +494,9 @@ public class DeviceContextImplTest {
                 .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
@@ -483,20 +519,61 @@ public class DeviceContextImplTest {
                 .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();
+    }
 }
index 29c111e12ccd995f982603b68b2352694351b866..0d61ca0c7bb3633579f80899ac5b4720fdd153bd 100644 (file)
@@ -17,12 +17,17 @@ import static org.mockito.Mockito.inOrder;
 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;
@@ -55,12 +60,8 @@ import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceTermin
 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;
@@ -68,20 +69,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731
 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;
 
@@ -105,6 +97,10 @@ public class DeviceManagerImplTest {
     private DeviceContextImpl mockedDeviceContext;
     @Mock
     private NodeId mockedNodeId;
+    @Mock
+    private LifecycleConductor lifecycleConductor;
+    @Mock
+    private MessageIntelligenceAgency messageIntelligenceAgency;
 
     @Before
     public void setUp() throws Exception {
@@ -115,11 +111,13 @@ public class DeviceManagerImplTest {
         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)
@@ -149,8 +147,6 @@ public class DeviceManagerImplTest {
 
         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);
 
@@ -164,7 +160,6 @@ public class DeviceManagerImplTest {
         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) {
@@ -194,7 +189,7 @@ public class DeviceManagerImplTest {
         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
@@ -206,8 +201,8 @@ public class DeviceManagerImplTest {
         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);
 
@@ -219,7 +214,30 @@ public class DeviceManagerImplTest {
         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) {
@@ -256,7 +274,7 @@ public class DeviceManagerImplTest {
 
     @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);
@@ -264,8 +282,8 @@ public class DeviceManagerImplTest {
 
         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 {
index 8affe98015a81387ca3d0f0879da0f8e0b1ec42e..fad7eb67927a5713a1d170aa4ea7a7c1b2166765 100644 (file)
@@ -110,12 +110,6 @@ public class DeviceStateImplTest {
         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());
index e43a6f5c2dea70332243d0d8251d487eece77e68..cd3a89bc3836509af4441a2a8788753d7c51576b 100644 (file)
@@ -33,6 +33,7 @@ import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListen
 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;
@@ -65,6 +66,8 @@ public class TransactionChainManagerTest {
     Registration registration;
     @Mock
     DeviceState deviceState;
+    @Mock
+    LifecycleConductor conductor;
 
     @Mock
     private KeyedInstanceIdentifier<Node, NodeKey> nodeKeyIdent;
@@ -85,7 +88,7 @@ public class TransactionChainManagerTest {
         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));
@@ -101,28 +104,56 @@ public class TransactionChainManagerTest {
     @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();
     }
 
@@ -134,22 +165,21 @@ public class TransactionChainManagerTest {
     @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);
     }
 
@@ -167,4 +197,47 @@ public class TransactionChainManagerTest {
         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
index e88ce24a3e52378dc3d3d3f61ca826bcc726ff35..a427fbbe0bb3ee90cc72309599f3ad20e74a3b36 100644 (file)
@@ -28,11 +28,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * @author Jozef Bacigal
- * Date: 4/19/16
- * Time: 12:56
- */
 @RunWith(MockitoJUnitRunner.class)
 public class RoleContextImplTest {
 
@@ -59,8 +54,8 @@ 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*/
@@ -112,7 +107,8 @@ public class RoleContextImplTest {
 
     @Test
     public void testCreateRequestContext() throws Exception {
-
+        roleContext.createRequestContext();
+        Mockito.verify(conductor).reserveXidForDeviceMessage(nodeId);
     }
 
     @Test(expected = NullPointerException.class)
index bba0245d9a75e345d8aba960745ea93dc9c3ed42..3febe5466ccd088dd1f7727ba3abffa370f0536e 100644 (file)
@@ -36,6 +36,7 @@ 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;
@@ -43,13 +44,14 @@ import org.opendaylight.openflowplugin.api.openflow.lifecycle.RoleChangeListener
 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 {
 
@@ -92,6 +94,12 @@ public class RoleManagerImplTest {
     @Mock
     LifecycleConductor conductor;
 
+    @Mock
+    DeviceState deviceState;
+
+    @Mock
+    GetFeaturesOutput featuresOutput;
+
     private RoleManagerImpl roleManager;
     private RoleManagerImpl roleManagerSpy;
     private RoleContext roleContextSpy;
@@ -101,15 +109,18 @@ public class RoleManagerImplTest {
     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);
@@ -125,7 +136,6 @@ public class RoleManagerImplTest {
         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);
@@ -177,6 +187,7 @@ public class RoleManagerImplTest {
 
     @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());
@@ -218,6 +229,23 @@ public class RoleManagerImplTest {
         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() {
@@ -239,5 +267,26 @@ public class RoleManagerImplTest {
         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
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/rpc/ItemLifecycleListenerImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/rpc/ItemLifecycleListenerImplTest.java
new file mode 100644 (file)
index 0000000..18a7942
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * 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();
+    }
+}
index 75b20cb7e578d7745da7692ee6aae09b014658df..854d4d3269ac9fdb7317802680025cc123f28189 100644 (file)
@@ -9,33 +9,41 @@ package org.opendaylight.openflowplugin.impl.rpc;
 
 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
@@ -44,6 +52,14 @@ public class RpcContextImplTest {
     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;
 
@@ -51,6 +67,15 @@ public class RpcContextImplTest {
     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
@@ -70,4 +95,71 @@ public class RpcContextImplTest {
             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 {}
 }
index 3d4a67d8c575141c5224b848e06b9d7b2a79e24c..fe01124d6c94ab26c7fd4ccd7e398da7e40eccb3 100644 (file)
@@ -22,23 +22,31 @@ import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderCo
 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 {
 
@@ -52,11 +60,25 @@ 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();
@@ -70,16 +92,38 @@ public class RpcManagerImplTest {
         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
@@ -88,4 +132,58 @@ public class RpcManagerImplTest {
         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();
+    }
 }
index 8d41070cbddac264106e1c63822d298ffd5cbd83..756b1ee4371b6cb5a0a85521d5b9823f6100199a 100644 (file)
@@ -109,11 +109,15 @@ public class MultipartRequestOnTheFlyCallbackTest {
         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);
 
@@ -132,7 +136,9 @@ public class MultipartRequestOnTheFlyCallbackTest {
                 //NOOP
             }
         };
-        multipartRequestOnTheFlyCallback = new MultipartRequestOnTheFlyCallback(dummyRequestContext, String.class, mockedDeviceContext, dummyEventIdentifier);
+        multipartRequestOnTheFlyCallback = new MultipartRequestOnTheFlyCallback(dummyRequestContext, String.class,
+                mockedDeviceContext.getMessageSpy(),dummyEventIdentifier, mockedDeviceContext.getDeviceState(),
+                mockedDeviceContext.getDeviceFlowRegistry(), mockedDeviceContext);
     }
 
 
index 76c7ac43ceb44ad5b5aab777153120f88417c09d..3cf72599f8d30a8a62c97fbb72f70289e10788f5 100644 (file)
@@ -50,6 +50,7 @@ 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.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
@@ -92,14 +93,18 @@ public class SalFlowServiceImplTest extends TestCase {
     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);
@@ -114,11 +119,11 @@ public class SalFlowServiceImplTest extends TestCase {
         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
index 2de3711cf94d194dbaaa7a96134b3b5b3803fd76..35c702a5126ee290bd3d5c40c46c890fc29ed9a3 100644 (file)
@@ -23,6 +23,7 @@ import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
 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;
@@ -34,6 +35,7 @@ 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.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;
@@ -71,6 +73,12 @@ public class SalRoleServiceImplTest {
     @Mock
     private RequestContext<RoleRequestOutput> mockRequestContext;
 
+    @Mock
+    private DeviceState mockDeviceState;
+
+    @Mock
+    private GetFeaturesOutput mockFeaturesOutput;
+
     @Mock
     private OutboundQueue mockOutboundQueue;
 
@@ -85,6 +93,10 @@ public class SalRoleServiceImplTest {
     @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);
index 188cb7816dfe532ffa697e034b6d8377f7ad5879..0e821d484ccc9450776108c137206b7435742c2e 100644 (file)
@@ -63,7 +63,8 @@ public class SalTableServiceImplTest extends ServiceMocking {
                 .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
index 011387a6d01e7f6f8ad733987805267d6512eb7b..d6edde72994d8f0a5e04ff2bc29c229e9926b131 100644 (file)
@@ -31,6 +31,7 @@ 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.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
@@ -56,6 +57,8 @@ public abstract class ServiceMocking {
     @Mock
     protected FeaturesReply mockedFeatures;
     @Mock
+    protected GetFeaturesOutput mockedFeaturesOutput;
+    @Mock
     protected ConnectionAdapter mockedConnectionAdapter;
     @Mock
     protected MessageSpy mockedMessagSpy;
@@ -80,13 +83,16 @@ public abstract class ServiceMocking {
         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);
index 370af566666534b94e1df80a8ba11716c1c4b29c..46414d6c822c6315dee8f751c6a87a207e890bf5 100644 (file)
@@ -94,7 +94,10 @@ public class SinglePurposeMultipartReplyTranslatorTest {
 
         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);
@@ -106,7 +109,10 @@ public class SinglePurposeMultipartReplyTranslatorTest {
 
         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);
@@ -123,7 +129,10 @@ public class SinglePurposeMultipartReplyTranslatorTest {
         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);
@@ -152,7 +161,10 @@ public class SinglePurposeMultipartReplyTranslatorTest {
 
         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);
@@ -175,7 +187,10 @@ public class SinglePurposeMultipartReplyTranslatorTest {
 
         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);
index 7c50f076e4b105381913c866473dff89468cf6df..bce92039e282e2d7942734dcb955d89693989bd7 100644 (file)
@@ -30,6 +30,7 @@ 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;
 
@@ -43,15 +44,15 @@ class StatisticsContextImpMockInitiation {
     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
@@ -60,16 +61,16 @@ class StatisticsContextImpMockInitiation {
         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);
@@ -77,6 +78,11 @@ class StatisticsContextImpMockInitiation {
         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);
index 91533ae80099edb90feb4e5172852ebbe2cbf0f6..05cd073993a75c3e41d917ab122b48574183a81c 100644 (file)
@@ -13,9 +13,11 @@ package org.opendaylight.openflowplugin.impl.statistics;
 
 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;
@@ -32,6 +34,7 @@ import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
 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;
@@ -97,6 +100,7 @@ public class StatisticsContextImplTest extends StatisticsContextImpMockInitiatio
     @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);
index 283d0d0b87038c5dbfa448ec5857e7d96654556a..b1c389eca913baf3743cfd57f16c42968dfcbf64 100644 (file)
@@ -193,7 +193,8 @@ public class StatisticsGatheringUtilsTest {
         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());
@@ -493,7 +494,8 @@ public class StatisticsGatheringUtilsTest {
     @Test
     public void testDeleteAllKnownFlowsNotSync() throws Exception {
         when(deviceState.deviceSynchronized()).thenReturn(false);
-        StatisticsGatheringUtils.deleteAllKnownFlows(deviceContext);
+        StatisticsGatheringUtils.deleteAllKnownFlows(deviceContext.getDeviceState(),
+                deviceContext.getDeviceFlowRegistry(), deviceContext);
         Mockito.verifyNoMoreInteractions(deviceFlowRegistry);
     }
 
@@ -512,7 +514,8 @@ public class StatisticsGatheringUtilsTest {
         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,
index df14afe627374f37d6911f95074618a0d51e5e0b..f203cfbbc113236b86298b8d3fd77ea37249645b 100644 (file)
@@ -1,20 +1,29 @@
+/**
+ * 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;
@@ -46,10 +55,15 @@ import org.opendaylight.openflowplugin.api.openflow.registry.ItemLifeCycleRegist
 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;
@@ -57,6 +71,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow
 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;
@@ -78,6 +94,8 @@ public class StatisticsManagerImplTest {
     @Mock
     FeaturesReply mockedFeatures;
     @Mock
+    GetFeaturesOutput mockedFeaturesOutput;
+    @Mock
     ConnectionAdapter mockedConnectionAdapter;
     @Mock
     MessageSpy mockedMessagSpy;
@@ -107,14 +125,25 @@ public class StatisticsManagerImplTest {
     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);
@@ -128,6 +157,8 @@ public class StatisticsManagerImplTest {
         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"));
 
@@ -151,16 +182,16 @@ public class StatisticsManagerImplTest {
                 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);
@@ -172,11 +203,7 @@ public class StatisticsManagerImplTest {
 
         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
@@ -341,4 +368,31 @@ public class StatisticsManagerImplTest {
         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
index db225e3594c32833e24007d57819def8a5315bee..a3480ed9ad9775bdc4890d643e19b9a7b4c59bb3 100644 (file)
@@ -94,10 +94,11 @@ public abstract class AbstractStatsServiceTest {
         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();
index 5fb40eac6ec5eceaf8df9ebb5c6b2c57ef578d58..49a16e38c00cd4a4a89a535d639023453ca40e86 100644 (file)
@@ -36,7 +36,7 @@ public class OpendaylightFlowStatisticsServiceImpl1Test extends AbstractStatsSer
     private OpendaylightFlowStatisticsServiceImpl flowStatisticsService;
 
     public void setUp() {
-        flowStatisticsService = new OpendaylightFlowStatisticsServiceImpl(rqContextStack, deviceContext);
+        flowStatisticsService = OpendaylightFlowStatisticsServiceImpl.createWithOook(rqContextStack, deviceContext);
 
         rqContext = new AbstractRequestContext<Object>(42L) {
             @Override
index 43945f1d3d7b8e2dd38644031305d634a7dc40e1..236e50577a1d81e77995b223aac88199de7c48bf 100644 (file)
@@ -53,7 +53,7 @@ public class OpendaylightFlowStatisticsServiceImpl2Test extends AbstractStatsSer
 
 
     public void setUp() {
-        flowStatisticsService = new OpendaylightFlowStatisticsServiceImpl(rqContextStack, deviceContext);
+        flowStatisticsService = OpendaylightFlowStatisticsServiceImpl.createWithOook(rqContextStack, deviceContext);
 
         rqContextMp = new AbstractRequestContext<List<MultipartReply>>(42L) {
             @Override
@@ -82,7 +82,7 @@ public class OpendaylightFlowStatisticsServiceImpl2Test extends AbstractStatsSer
                          }
         ).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());
 
 
index 897c872a98d524e6dba4048fc22dfbad57e2179d..329c670886197ef70f61c5d55ae8da33f6a28ad7 100644 (file)
@@ -33,7 +33,7 @@ public class OpendaylightFlowStatisticsServiceImpl3Test extends AbstractStatsSer
     private OpendaylightFlowStatisticsServiceImpl flowStatisticsService;
 
     public void setUp() {
-        flowStatisticsService = new OpendaylightFlowStatisticsServiceImpl(rqContextStack, deviceContext);
+        flowStatisticsService = OpendaylightFlowStatisticsServiceImpl.createWithOook(rqContextStack, deviceContext);
         flowStatisticsService.setDelegate(flowStatisticsDelegate);
     }
 
index 501020b4389d5b7f20edde01d001ff5a7cb7710d..d6dccf17037816a9bc6fbe6ac31442043455be0a 100644 (file)
@@ -41,6 +41,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.g
 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;
@@ -64,6 +65,8 @@ public class AbstractCompatibleStatServiceTest extends AbstractStatsServiceTest
     private DeviceState deviceState;
     @Mock
     private MessageTranslator<Object, Object> translator;
+    @Mock
+    private GetFeaturesOutput featuresOutput;
 
     private AbstractRequestContext<Object> rqContext;
 
@@ -89,10 +92,12 @@ public class AbstractCompatibleStatServiceTest extends AbstractStatsServiceTest
             }
         };
 
+        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));
 
@@ -101,7 +106,7 @@ public class AbstractCompatibleStatServiceTest extends AbstractStatsServiceTest
 
         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
@@ -135,7 +140,7 @@ public class AbstractCompatibleStatServiceTest extends AbstractStatsServiceTest
                 .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);
 
 
index 01b363cb802cccdac40084f2815201c0c44b370a..835e489101cbd5b2987bd6448445c44a9194b59e 100644 (file)
@@ -95,7 +95,7 @@ public class OpendaylightFlowStatisticsServiceDelegateImplTest extends AbstractS
                 .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)))
index 29626cf4e4e452e22c2c8df0944f31e13d56d95f..ab611e216afbb57f0e163a8732e748d2acccd267 100644 (file)
@@ -31,6 +31,7 @@ public class StatisticsGatheringOnTheFlyServiceTest extends ServiceMocking {
     protected void setup() {
         statisticsGatheringService = new StatisticsGatheringOnTheFlyService(mockedRequestContextStack, mockedDeviceContext);
         Mockito.doReturn(NODE_ID).when(mockedPrimConnectionContext).getNodeId();
+        Mockito.when(mockedDeviceContext.getDeviceState().getNodeId()).thenReturn(NODE_ID);
     }
 
     @Test
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/AbstractDirectStatisticsServiceTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/AbstractDirectStatisticsServiceTest.java
new file mode 100644 (file)
index 0000000..7263b98
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/FlowDirectStatisticsServiceTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/FlowDirectStatisticsServiceTest.java
new file mode 100644 (file)
index 0000000..11b2b63
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/GroupDirectStatisticsServiceTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/GroupDirectStatisticsServiceTest.java
new file mode 100644 (file)
index 0000000..e948929
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/MeterDirectStatisticsServiceTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/MeterDirectStatisticsServiceTest.java
new file mode 100644 (file)
index 0000000..7c83703
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/NodeConnectorDirectStatisticsServiceTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/NodeConnectorDirectStatisticsServiceTest.java
new file mode 100644 (file)
index 0000000..9296347
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceImplTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/OpendaylightDirectStatisticsServiceImplTest.java
new file mode 100644 (file)
index 0000000..c3a2c26
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * 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
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/QueueDirectStatisticsServiceTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/services/direct/QueueDirectStatisticsServiceTest.java
new file mode 100644 (file)
index 0000000..3df2f25
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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
index e130049c19c1b2aee24318b6587e5a83066d1735..a840fa9a4ae386fee01e45b4237938827ffd5cf6 100644 (file)
@@ -16,6 +16,7 @@ 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.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;
@@ -29,7 +30,7 @@ public class AggregatedFlowStatisticsTranslatorTest {
 
     private AggregatedFlowStatisticsTranslator translator;
     @Mock
-    private DeviceContext deviceContext;
+    private DeviceState deviceState;
 
     @Before
     public void setUp() throws Exception {
@@ -47,7 +48,7 @@ public class AggregatedFlowStatisticsTranslatorTest {
         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());
diff --git a/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/translator/FlowRemovedTranslatorTest.java b/openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/translator/FlowRemovedTranslatorTest.java
new file mode 100644 (file)
index 0000000..a695ef0
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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
index 8df7184a4db121753bb618b03b027dca74428bf6..f6358fad05462f83e37d6ae8eaf8eebcd4c374cf 100644 (file)
@@ -100,7 +100,7 @@ public class PacketReceivedTranslatorTest {
         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",
index 7e933d627b5b2170d24e7eae006e27ec87fe63e6..c4a1f2a285ff76ddac21c96ec5126f12e32a81b7 100644 (file)
@@ -65,7 +65,7 @@ public class PortUpdateTranslatorTest {
                         .getDefaultInstance("hundredGbFd");
 
 
-        final FlowCapableNodeConnector nodeConnector = portUpdateTranslator.translate(portBld.build(), deviceContext, null);
+        final FlowCapableNodeConnector nodeConnector = portUpdateTranslator.translate(portBld.build(), deviceState, null);
 
         commonCheck(nodeConnector);
 
@@ -95,7 +95,7 @@ public class PortUpdateTranslatorTest {
                         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);
 
index 4fd14992fb5ecf71c5a79b979dd5c663e8e911ee..e20beab46b23393a8b9df81efbe0586376b5b0a7 100644 (file)
@@ -10,21 +10,24 @@ package org.opendaylight.openflowplugin.impl.util;
 
 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;
@@ -45,8 +48,10 @@ 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.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;
@@ -100,10 +105,9 @@ import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 @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";
@@ -132,17 +136,43 @@ public class DeviceInitializationUtilsTest {
     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);
@@ -168,9 +198,9 @@ public class DeviceInitializationUtilsTest {
     @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);
 
@@ -333,7 +363,4 @@ public class DeviceInitializationUtilsTest {
         when(mockConnectionContext.getOutboundQueueProvider()).thenReturn(outboundQueueProvider);
         return mockConnectionContext;
     }
-
-
-
 }
index bed59f638f7993788ab745e4c17b411019f62035..cf0149cd79ffea230d337755f76834bdb7e5be30 100644 (file)
@@ -22,18 +22,20 @@ import org.junit.Test;
 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() {
@@ -42,14 +44,21 @@ public class MdSalRegistrationUtilsTest {
         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));
     }
index 26d92ec8cdb1812b4007e6c5065c2111cbfcdf15..910f930f6527df59be3648fb9de6df67568c92d0 100644 (file)
@@ -24,6 +24,7 @@ import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipC
 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;
@@ -40,7 +41,6 @@ import com.google.common.util.concurrent.ListeningExecutorService;
 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;
@@ -63,8 +63,11 @@ public class OfEntityManager implements TransactionChainListener{
 
     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<>();
@@ -77,6 +80,19 @@ public class OfEntityManager implements TransactionChainListener{
         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,
@@ -84,9 +100,6 @@ public class OfEntityManager implements TransactionChainListener{
         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);
 
@@ -243,6 +256,7 @@ public class OfEntityManager implements TransactionChainListener{
             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>(){
@@ -250,7 +264,12 @@ public class OfEntityManager implements TransactionChainListener{
                 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));
 
                 }
diff --git a/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/sal/OpenflowPluginConfig.java b/openflowplugin/src/main/java/org/opendaylight/openflowplugin/openflow/md/core/sal/OpenflowPluginConfig.java
new file mode 100644 (file)
index 0000000..1cda22e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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);}
+    }
+}
index c151c04faa30ad41317fe1e2a9386d8145a56169..dac95eb86b21ec91473eea8db875f9a43e37aa0c 100644 (file)
@@ -56,6 +56,8 @@ public class OpenflowPluginProvider implements AutoCloseable, OpenFlowPluginExte
     private RpcProviderRegistry rpcRegistry;
     private EntityOwnershipService entityOwnershipService;
 
+    private OpenflowPluginConfig openflowPluginConfig;
+
     /**
      * Initialization of services and msgSpy counter
      */
@@ -63,8 +65,9 @@ public class OpenflowPluginProvider implements AutoCloseable, OpenFlowPluginExte
         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();
@@ -160,6 +163,15 @@ public class OpenflowPluginProvider implements AutoCloseable, OpenFlowPluginExte
         this.entityOwnershipService = entityOwnershipService;
     }
 
+    public void setOpenflowPluginConfig(OpenflowPluginConfig openflowPluginConfig) {
+        this.openflowPluginConfig = openflowPluginConfig;
+    }
+
+    @VisibleForTesting
+    public OpenflowPluginConfig getOpenflowPluginConfig() {
+        return openflowPluginConfig;
+    }
+
     @VisibleForTesting
     protected RpcProviderRegistry getRpcRegistry() {
         return rpcRegistry;
index 0c31a5821a0b1aa0c354ccede34f711f4a51a1b7..1542d5444bacf717e81ed154717b3f9987528975 100644 (file)
@@ -11,15 +11,23 @@ package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflo
 
 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
@@ -54,6 +62,7 @@ public final class ConfigurableOpenFlowProviderModule extends org.opendaylight.y
         pluginProvider.setSwitchConnectionProviders(getOpenflowSwitchConnectionProviderDependency());
         pluginProvider.setEntityOwnershipService(getOwnershipServiceDependency());
         pluginProvider.setRole(getRole());
+        pluginProvider.setOpenflowPluginConfig(readConfig());
         pluginProvider.initialization();
         return pluginProvider;
     }
@@ -85,4 +94,18 @@ public final class ConfigurableOpenFlowProviderModule extends org.opendaylight.y
 
         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();
+    }
 }
index 0aa1eef2bc5b12c3eb2a3d3346fabe39a278fcf5..0f031c00e01d67ae2e2fd8e566739d35f1439f86 100644 (file)
@@ -107,6 +107,13 @@ module openflow-provider-impl {
                 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 {
index 4bf473ac6c4d62b678750935a73f13660861660e..367f0a0917be5f2df0da62a7364dbb962cec5e77 100644 (file)
@@ -90,7 +90,6 @@ public class SalRegistrationManagerTest {
 
     private ModelDrivenSwitch mdSwitchOF13;
 
-
     CompositeObjectRegistration<ModelDrivenSwitch> registration;
 
 
@@ -106,7 +105,7 @@ public class SalRegistrationManagerTest {
         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);
@@ -193,5 +192,13 @@ public class SalRegistrationManagerTest {
         SwitchSessionKeyOF switchSessionKeyOF = new SwitchSessionKeyOF();
         salRegistrationManager.onSessionAdded(switchSessionKeyOF, context);
     }
+
+    public OpenflowPluginConfig getConfig(){
+        OpenflowPluginConfig.OpenflowPluginConfigBuilder cfgBuilder =
+                new OpenflowPluginConfig.OpenflowPluginConfigBuilder();
+        cfgBuilder.setSkipTableFeatures(true);
+        return cfgBuilder.build();
+
+    }
 }
 
index 1737475ab175dd8026bba2de52ded078bca4c2bd..6aff6c3f8324332ce145024bd9145b1a68a0e3b4 100644 (file)
@@ -194,13 +194,10 @@ public class OpenflowPortsUtilTest {
         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);
+
     }
 
 }