Merge "BUG-5636: making table features configurable for the He plugin. DEFAULT will...
authorAbhijit Kumbhare <abhijit.kumbhare@ericsson.com>
Thu, 26 May 2016 05:57:52 +0000 (05:57 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 26 May 2016 05:57:52 +0000 (05:57 +0000)
148 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/TxFacade.java
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-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/device/DeviceContextImpl.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/statistics/StatisticsContextImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsManagerImpl.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/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/util/MdSalRegistrationUtils.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/LifecycleConductorImplTest.java
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/rpc/RpcManagerImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/statistics/StatisticsManagerImplTest.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/util/MdSalRegistrationUtilsTest.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 35125b54214b35279ede89e928717b82adc9fb9c..b2c70f70535cd40d58c6e22fb07a6199f927c3af 100644 (file)
@@ -138,6 +138,7 @@ public interface DeviceContext extends AutoCloseable,
      * @return sync. future for Slave and MD-SAL completition for Master
      */
     ListenableFuture<Void> shuttingDownDataStoreTransactions();
+
     /**
      * 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 6a982b1e6814174984140fe211beef174ff78adc..e8a8ccb90449b48aa753e4654db84dc91823cdc2 100644 (file)
@@ -24,6 +24,13 @@ public interface TxFacade {
     <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.
      */
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 ff4d1b32be4f4d63137325387833e6749d4f215a..ddf73a75cc468af369bcfa7fa373c5f024d6c1b2 100644 (file)
@@ -24,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;
@@ -48,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);
@@ -60,6 +62,12 @@ 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);
@@ -115,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);
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 d8e924a953230b09037610f6b4ca2714e8381111..441725723740f439ac10f78b1f03bc1e5d2c5028 100644 (file)
@@ -333,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
@@ -407,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());
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 34b766f880278681534fdd97c56569bfc5897c8b..d85dab8f59632457938b55b0344792df1767e0c4 100644 (file)
@@ -215,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.");
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 38eaab0c3640145ea659bd12e7b649c79be6c448..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,13 +94,6 @@ 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);
     }
@@ -119,23 +102,26 @@ public class StatisticsManagerImpl implements StatisticsManager, StatisticsManag
     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>() {
@@ -168,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
@@ -218,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);
@@ -259,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;
@@ -274,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 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());
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 708410e69cc14cb198332e5d2da154f38a3ca261..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;
@@ -102,6 +110,15 @@ public class MdSalRegistrationUtils {
         rpcContext.registerRpcServiceImplementation(NodeConfigService.class, new NodeConfigServiceImpl(rpcContext, deviceContext));
         rpcContext.registerRpcServiceImplementation(OpendaylightFlowStatisticsService.class, OpendaylightFlowStatisticsServiceImpl.createWithOook(rpcContext, deviceContext));
 
+        // Direct statistics gathering
+        final OpendaylightDirectStatisticsServiceProvider statisticsProvider = new OpendaylightDirectStatisticsServiceProvider();
+        statisticsProvider.register(FlowDirectStatisticsService.class, new FlowDirectStatisticsService(rpcContext, deviceContext));
+        statisticsProvider.register(GroupDirectStatisticsService.class, new GroupDirectStatisticsService(rpcContext, deviceContext));
+        statisticsProvider.register(MeterDirectStatisticsService.class, new MeterDirectStatisticsService(rpcContext, deviceContext));
+        statisticsProvider.register(NodeConnectorDirectStatisticsService.class, new NodeConnectorDirectStatisticsService(rpcContext, deviceContext));
+        statisticsProvider.register(QueueDirectStatisticsService.class, new QueueDirectStatisticsService(rpcContext, deviceContext));
+        rpcContext.registerRpcServiceImplementation(OpendaylightDirectStatisticsService.class, new OpendaylightDirectStatisticsServiceImpl(statisticsProvider));
+
         final SalFlatBatchServiceImpl salFlatBatchService = new SalFlatBatchServiceImpl(
                 new SalFlowsBatchServiceImpl(salFlowService, flowCapableTransactionService),
                 new SalGroupsBatchServiceImpl(salGroupService, flowCapableTransactionService),
@@ -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);
     }
 
     /**
index 0553355aeb8d96c4d4250a7e2fcce3ded411f148..63c28cdb6349a2d60513a7602fe119c8f39cfda4 100644 (file)
@@ -11,6 +11,7 @@ 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;
@@ -26,6 +27,7 @@ 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;
@@ -63,6 +65,8 @@ public class LifecycleConductorImplTest {
     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;
@@ -75,6 +79,7 @@ public class LifecycleConductorImplTest {
 
         lifecycleConductor = new LifecycleConductorImpl(messageIntelligenceAgency);
         lifecycleConductor.setSafelyDeviceManager(deviceManager);
+        lifecycleConductor.setSafelyStatisticsManager(statisticsManager);
 
         when(connectionContext.getFeatures()).thenReturn(featuresReply);
     }
@@ -172,8 +177,7 @@ public class LifecycleConductorImplTest {
         when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
         when(deviceContext.onClusterRoleChange(null, OfpRole.BECOMEMASTER)).thenReturn(listenableFuture);
         lifecycleConductor.roleChangeOnDevice(nodeId,true,OfpRole.BECOMEMASTER,false);
-        verify(deviceState,times(1)).setRole(OfpRole.BECOMEMASTER);
-        verify(deviceState,times(0)).setRole(OfpRole.BECOMESLAVE);
+        verify(statisticsManager).startScheduling(nodeId);
     }
 
     /**
@@ -185,8 +189,7 @@ public class LifecycleConductorImplTest {
         when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
         when(deviceContext.onClusterRoleChange(null, OfpRole.BECOMESLAVE)).thenReturn(listenableFuture);
         lifecycleConductor.roleChangeOnDevice(nodeId,true,OfpRole.BECOMESLAVE,false);
-        verify(deviceState,times(1)).setRole(OfpRole.BECOMESLAVE);
-        verify(deviceState,times(0)).setRole(OfpRole.BECOMEMASTER);
+        verify(statisticsManager).stopScheduling(nodeId);
     }
 
     /**
index 6c18618612521c412f1903d6f5ecc7e6a32a8da3..07e345168a52e4d77b538a2b47bf6a9f0e84cdda 100644 (file)
@@ -494,6 +494,7 @@ 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(DeviceState.class), any(Object.class)))
                 .thenReturn(flowRemovedMdsalBld.build());
@@ -518,6 +519,7 @@ 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);
     }
index cafda406373407d0bb74a8807a8ac852af4e4c36..0d61ca0c7bb3633579f80899ac5b4720fdd153bd 100644 (file)
@@ -160,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) {
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 2c7d0c263a8fd99cd06020915734e3c6dcfb7f67..cd3a89bc3836509af4441a2a8788753d7c51576b 100644 (file)
@@ -104,10 +104,10 @@ 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);
     }
 
     /**
@@ -118,11 +118,11 @@ public class TransactionChainManagerTest {
     public void testSubmitTransaction() throws Exception {
         final Node data = new NodeBuilder().setId(nodeId).build();
         txChainManager.initialSubmitWriteTransaction();
-        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
         txChainManager.submitWriteTransaction();
 
         Mockito.verify(txChain).newWriteOnlyTransaction();
-        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
         Mockito.verify(writeTx).submit();
     }
 
@@ -133,11 +133,11 @@ public class TransactionChainManagerTest {
     @Test
     public void testSubmitTransaction1() throws Exception {
         final Node data = new NodeBuilder().setId(nodeId).build();
-        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
         txChainManager.submitWriteTransaction();
 
         Mockito.verify(txChain).newWriteOnlyTransaction();
-        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
         Mockito.verify(writeTx, Mockito.never()).submit();
     }
 
@@ -149,11 +149,11 @@ public class TransactionChainManagerTest {
         Mockito.when(writeTx.submit()).thenReturn(Futures.<Void, TransactionCommitFailedException>immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")));
         final Node data = new NodeBuilder().setId(nodeId).build();
         txChainManager.initialSubmitWriteTransaction();
-        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
         txChainManager.submitWriteTransaction();
 
         Mockito.verify(txChain).newWriteOnlyTransaction();
-        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
         Mockito.verify(writeTx).submit();
     }
 
@@ -165,11 +165,11 @@ 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();
     }
 
@@ -212,12 +212,12 @@ public class TransactionChainManagerTest {
     public void testDeactivateTransactionChainManagerFailed() throws Exception {
         Mockito.when(writeTx.submit()).thenReturn(Futures.<Void, TransactionCommitFailedException>immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")));
         final Node data = new NodeBuilder().setId(nodeId).build();
-        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
 
         txChainManager.deactivateTransactionManager();
 
         Mockito.verify(txChain).newWriteOnlyTransaction();
-        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
         Mockito.verify(writeTx).submit();
         Mockito.verify(txChain).close();
     }
@@ -225,12 +225,12 @@ public class TransactionChainManagerTest {
     @Test
     public void testShuttingDown() throws Exception{
         final Node data = new NodeBuilder().setId(nodeId).build();
-        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+        txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
 
         txChainManager.shuttingDown();
 
         Mockito.verify(txChain).newWriteOnlyTransaction();
-        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+        Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
         Mockito.verify(writeTx).submit();
     }
 
index 3a6ed63c5731d3b86ee95f4cd6c873ba38900a6a..fe01124d6c94ab26c7fd4ccd7e398da7e40eccb3 100644 (file)
@@ -102,7 +102,6 @@ public class RpcManagerImplTest {
         Mockito.when(connectionContext.getFeatures()).thenReturn(features);
         Mockito.when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
         Mockito.when(deviceContext.getDeviceState()).thenReturn(deviceState);
-        Mockito.when(deviceContext.getDeviceState().getRole()).thenReturn(OfpRole.BECOMEMASTER);
         Mockito.when(deviceContext.getItemLifeCycleSourceRegistry()).thenReturn(itemLifeCycleRegistry);
         Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodePath);
         Mockito.when(deviceState.getFeatures()).thenReturn(featuresOutput);
@@ -136,28 +135,24 @@ public class RpcManagerImplTest {
 
     @Test
     public void testOnDeviceContextLevelUpMaster() throws Exception {
-        Mockito.when(deviceState.getRole()).thenReturn(OfpRole.BECOMEMASTER);
         rpcManager.onDeviceContextLevelUp(nodeId);
         verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
     }
 
     @Test
     public void testOnDeviceContextLevelUpSlave() throws Exception {
-        Mockito.when(deviceState.getRole()).thenReturn(OfpRole.BECOMESLAVE);
         rpcManager.onDeviceContextLevelUp(nodeId);
         verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
     }
 
     @Test
     public void testOnDeviceContextLevelUpOther() throws Exception {
-        Mockito.when(deviceState.getRole()).thenReturn(OfpRole.NOCHANGE);
         rpcManager.onDeviceContextLevelUp(nodeId);
         verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
     }
 
     @Test
     public void testOnDeviceContextLevelDown() throws Exception {
-        Mockito.when(deviceState.getRole()).thenReturn(OfpRole.NOCHANGE);
         rpcManager.onDeviceContextLevelDown(deviceContext);
         verify(deviceTerminationPhaseHandler).onDeviceContextLevelDown(deviceContext);
     }
index 24b823b38d744e567871f6a5ef1434164c6cfd2e..f203cfbbc113236b86298b8d3fd77ea37249645b 100644 (file)
@@ -127,6 +127,8 @@ public class StatisticsManagerImplTest {
     private LifecycleConductor conductor;
     @Mock
     private GetFeaturesOutput featuresOutput;
+    @Mock
+    private DeviceInitializationPhaseHandler deviceInitializationPhaseHandler;
 
     private RequestContext<List<MultipartReply>> currentRequestContext;
     private StatisticsManagerImpl statisticsManager;
@@ -180,6 +182,7 @@ 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);
     }
@@ -367,14 +370,12 @@ public class StatisticsManagerImplTest {
     }
 
     @Test
-    public void testPollStatistics() {
-        StatisticsManager statisticsManagerSpy = Mockito.spy(statisticsManager);
-
+    public void testPollStatistics() throws Exception {
         final StatisticsContext statisticsContext = Mockito.mock(StatisticsContext.class);
         final TimeCounter mockTimerCounter = Mockito.mock(TimeCounter.class);
 
         statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
-        verify(mockedDeviceContext, times(2)).getDeviceState();
+        verify(mockedDeviceContext).getDeviceState();
 
         when(mockedDeviceContext.getDeviceState().isValid()).thenReturn(true);
         statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
@@ -384,8 +385,8 @@ public class StatisticsManagerImplTest {
         statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
         // TODO Make scheduleNextPolling visible for tests?
 
-        when(mockedDeviceContext.getDeviceState().getRole()).thenReturn(OfpRole.BECOMEMASTER);
         when(statisticsContext.gatherDynamicData()).thenReturn(Futures.immediateCheckedFuture(Boolean.TRUE));
+        when(statisticsContext.isSchedulingEnabled()).thenReturn(Boolean.TRUE);
         statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
         Mockito.verify(mockTimerCounter).markStart();
         Mockito.verify(mockTimerCounter).addTimeMark();
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 12842a34036ac96733d1484c81e46e9790123717..cf0149cd79ffea230d337755f76834bdb7e5be30 100644 (file)
@@ -32,10 +32,10 @@ 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() {