summaryrefslogtreecommitdiffstats
path: root/infra
diff options
context:
space:
mode:
authorMaros Marsalek <mmarsale@cisco.com>2016-07-27 11:05:51 +0200
committerMaros Marsalek <mmarsale@cisco.com>2016-07-29 16:32:07 +0200
commit0578156b721fa01c8c645b8f9625ecebdb6449e4 (patch)
tree49d36f24e5d984a8c9f151b1440de88619f8b7de /infra
parent007d4542388ca89be409ce1a4a4c7a36ddcb538f (diff)
HONEYCOMB-130: Separate v3po plugin from HC infra
Creating folders: - common/ - infra/ - v3po/ - vpp-common/ Change-Id: I2c39e1b17e39e7c0f0628f44aa5fe08563fa06e4 Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'infra')
-rw-r--r--infra/Readme.adoc67
-rw-r--r--infra/artifacts/pom.xml96
-rw-r--r--infra/cfg-init/pom.xml55
-rw-r--r--infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/AbstractDataTreeConverter.java114
-rw-r--r--infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/DataTreeInitializer.java51
-rw-r--r--infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistry.java33
-rw-r--r--infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImpl.java52
-rw-r--r--infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java112
-rw-r--r--infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModule.java54
-rw-r--r--infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java13
-rw-r--r--infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModule.java38
-rw-r--r--infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModuleFactory.java13
-rw-r--r--infra/cfg-init/src/main/yang/vpp-cfg-init.yang117
-rw-r--r--infra/cfg-init/src/test/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImplTest.java66
-rw-r--r--infra/data-api/Readme.adoc10
-rw-r--r--infra/data-api/pom.xml55
-rw-r--r--infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java70
-rw-r--r--infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java33
-rw-r--r--infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java40
-rw-r--r--infra/data-api/src/main/yang/data-api.yang38
-rw-r--r--infra/data-impl/Readme.adoc43
-rw-r--r--infra/data-impl/pom.xml106
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java198
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java236
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java122
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java278
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java151
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java119
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransaction.java101
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java242
-rw-r--r--infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java177
-rw-r--r--infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java71
-rw-r--r--infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModuleFactory.java13
-rw-r--r--infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModule.java79
-rw-r--r--infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModuleFactory.java13
-rw-r--r--infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java65
-rw-r--r--infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModuleFactory.java13
-rw-r--r--infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModule.java38
-rw-r--r--infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModuleFactory.java13
-rw-r--r--infra/data-impl/src/main/yang/data-impl.yang179
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java111
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java269
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java427
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java69
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java68
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java111
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java192
-rw-r--r--infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java128
-rw-r--r--infra/data-impl/src/test/resources/test-diff.yang54
-rw-r--r--infra/features/pom.xml217
-rw-r--r--infra/features/src/main/features/features.xml70
-rw-r--r--infra/impl/pom.xml130
-rw-r--r--infra/impl/src/main/config/context-datatree-config.xml144
-rw-r--r--infra/impl/src/main/config/default-config.xml253
-rw-r--r--infra/impl/src/main/config/initializer-config.xml188
-rw-r--r--infra/impl/src/main/config/netconf-north-config.xml496
-rw-r--r--infra/impl/src/main/config/restconf-north-config.xml37
-rw-r--r--infra/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java206
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java26
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java13
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java34
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModuleFactory.java13
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModule.java117
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModuleFactory.java13
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModule.java46
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModuleFactory.java13
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java60
-rw-r--r--infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java28
-rw-r--r--infra/impl/src/main/yang/v3po-impl.yang155
-rw-r--r--infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java26
-rw-r--r--infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java70
-rw-r--r--infra/it/it-test/pom.xml66
-rw-r--r--infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java106
-rw-r--r--infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java460
-rw-r--r--infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java150
-rw-r--r--infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java521
-rw-r--r--infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java62
-rw-r--r--infra/it/pom.xml51
-rw-r--r--infra/it/test-model/pom.xml36
-rw-r--r--infra/it/test-model/src/main/yang/hc-test.yang123
-rw-r--r--infra/karaf/pom.xml74
-rw-r--r--infra/notification/api/pom.xml40
-rw-r--r--infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java38
-rw-r--r--infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java35
-rw-r--r--infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java37
-rw-r--r--infra/notification/api/src/main/yang/notification-api.yang31
-rw-r--r--infra/notification/impl/pom.xml92
-rw-r--r--infra/notification/impl/src/main/config/default-config.xml71
-rw-r--r--infra/notification/impl/src/main/config/notification-to-netconf-config.xml58
-rw-r--r--infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java66
-rw-r--r--infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java112
-rw-r--r--infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java109
-rw-r--r--infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java27
-rw-r--r--infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java13
-rw-r--r--infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java93
-rw-r--r--infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java13
-rw-r--r--infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java157
-rw-r--r--infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java13
-rw-r--r--infra/notification/impl/src/main/yang/notification-impl.yang141
-rw-r--r--infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java63
-rw-r--r--infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java98
-rw-r--r--infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java67
-rw-r--r--infra/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java81
-rw-r--r--infra/notification/pom.xml56
-rw-r--r--infra/pom.xml68
-rw-r--r--infra/postman_rest_collection.json1386
-rw-r--r--infra/translate-api/Readme.adoc9
-rw-r--r--infra/translate-api/pom.xml67
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java66
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModifiableSubtreeManagerRegistryBuilder.java77
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java49
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java44
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManager.java39
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManagerRegistryBuilder.java22
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/TranslationException.java38
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ListReader.java66
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java26
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadFailedException.java61
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/Reader.java76
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReaderFactory.java38
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ModifiableReaderRegistryBuilder.java41
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistry.java62
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistryBuilder.java27
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java114
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ListWriter.java33
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java50
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteFailedException.java166
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/Writer.java48
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java38
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/ModifiableWriterRegistryBuilder.java31
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistry.java213
-rw-r--r--infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistryBuilder.java27
-rw-r--r--infra/translate-api/src/main/yang/translate-api.yang52
-rw-r--r--infra/translate-api/src/test/java/io/fd/honeycomb/v3po/translate/ReadFailedExceptionTest.java50
-rw-r--r--infra/translate-impl/Readme.adoc9
-rw-r--r--infra/translate-impl/pom.xml82
-rw-r--r--infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericListReader.java117
-rw-r--r--infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericReader.java70
-rw-r--r--infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java106
-rw-r--r--infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java61
-rw-r--r--infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java83
-rw-r--r--infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java64
-rw-r--r--infra/translate-spi/Readme.adoc4
-rw-r--r--infra/translate-spi/pom.xml72
-rw-r--r--infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java63
-rw-r--r--infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ReaderCustomizer.java63
-rw-r--r--infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/ListWriterCustomizer.java34
-rw-r--r--infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/WriterCustomizer.java75
-rw-r--r--infra/translate-utils/Readme.adoc3
-rw-r--r--infra/translate-utils/pom.xml93
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java213
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java102
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java186
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java79
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java75
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java90
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java96
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java173
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java34
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveListReaderCustomizer.java65
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java57
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java103
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java202
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java117
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java109
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java250
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java101
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java137
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java40
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java121
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java315
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java71
-rw-r--r--infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java85
-rw-r--r--infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java25
-rw-r--r--infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java20
-rw-r--r--infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java26
-rw-r--r--infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModuleFactory.java13
-rw-r--r--infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java38
-rw-r--r--infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModuleFactory.java13
-rw-r--r--infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java84
-rw-r--r--infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModuleFactory.java13
-rw-r--r--infra/translate-utils/src/main/yang/translate-utils.yang95
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java131
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/DataObjects.java52
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/JsonUtilsTest.java109
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java114
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReaderTest.java124
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchyTest.java69
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java131
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java264
-rw-r--r--infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java84
-rw-r--r--infra/translate-utils/src/test/resources/expected-persisted-output.txt8
-rw-r--r--infra/translate-utils/src/test/resources/test-persistence.yang22
193 files changed, 18667 insertions, 0 deletions
diff --git a/infra/Readme.adoc b/infra/Readme.adoc
new file mode 100644
index 000000000..33e882f8c
--- /dev/null
+++ b/infra/Readme.adoc
@@ -0,0 +1,67 @@
+= Honeycomb
+
+Honeycomb is an VPP agent that runs the same host as a VPP instance
+and exposes YANG models via NETCONF and RESTCONF to allow management of that VPP instance.
+Honeycomb uses jAPI to communicate with the VPP.
+
+[ditaa, "hc-architecture"]
+....
+ /------------------\
+ ODL | RESTCONF/NETCONF |
+ \-+--------------+-/
+ | ^
+---------------------|--------------|---------------------
+ v |
+ /------------------\
+ | Data layer |
+ \-+--------------+-/
+ | ^
+ Honeycomb v |
+ /-----+--------------+-----\
+ | Translation layer |
+ \----+----------------+----+
+ | VPP SPI impl |
+ +---+------+-----+
+ | ^
+------------------------|------|--------------------------
+ v |
+ ++------++
+ | jAPI |
+ VPP /----+--------+------\
+ | VPP |
+ \--------------------/
+
+....
+
+== NETCONF/RESTCONF layer
+
+NETCONF and RESTCONF support is provided by ODL (Honeycomb is an ODL application).
+In the future we plan to minimize ODL dependencies or completely remove karaf.
+
+Transaction functionality is provided by the data layer.
+
+== Data layer
+
+Models CONFIG data store as a DataTree.
+
+OPERATIONAL data store reads are passed directly to the translation layer.
+
+Provides transaction functionality for NETCONF/RESTCONF layer.
+
+
+== Translation layer
+
+Extensible API for translation between Binding Aware data and actual device data.
+Consists of readers and writers responsible for communication with the device.
+
+Provides registry of readers and writers for the data layer.
+
+== Supported features
+
+List of supported requests for RESTCONF northbound interface can be found in
+postman_rest_collection.json within the codebase.
+It is a POSTMAN compatible collection and can be imported into POSTMAN application.
+
+
+
+
diff --git a/infra/artifacts/pom.xml b/infra/artifacts/pom.xml
new file mode 100644
index 000000000..c2bbe16ab
--- /dev/null
+++ b/infra/artifacts/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<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>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>honeycomb-artifacts</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cfg-init</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-features</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+</project>
diff --git a/infra/cfg-init/pom.xml b/infra/cfg-init/pom.xml
new file mode 100644
index 000000000..ddacb632a
--- /dev/null
+++ b/infra/cfg-init/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>impl-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>cfg-init</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/AbstractDataTreeConverter.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/AbstractDataTreeConverter.java
new file mode 100644
index 000000000..f0058264e
--- /dev/null
+++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/AbstractDataTreeConverter.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.vpp.data.init;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Base class for initializers which perform conversion between operational and config YANG model.
+ *
+ * @param <C> Config data object
+ * @param <O> Operational data object
+ */
+@Beta
+public abstract class AbstractDataTreeConverter<O extends DataObject, C extends DataObject>
+ implements DataTreeInitializer {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractDataTreeConverter.class);
+
+ private final InstanceIdentifier<O> idOper;
+ private final InstanceIdentifier<C> idConfig;
+ private final DataBroker bindingDataBroker;
+
+ public AbstractDataTreeConverter(final DataBroker bindingDataBroker,
+ final InstanceIdentifier<O> operRootId,
+ final InstanceIdentifier<C> cfgRootId) {
+ this.bindingDataBroker = checkNotNull(bindingDataBroker, "bindingDataBroker should not be null");
+ this.idOper = checkNotNull(operRootId, "operRootId should not be null");
+ this.idConfig = checkNotNull(cfgRootId, "cfgRootId should not be null");
+ }
+
+ @Override
+ public void close() throws Exception {
+ LOG.debug("AbstractDataTreeConverter.close()");
+ // Not removing initialized data, since this works in cooperation with persistence, it could remove
+ // data restored by persistence or remove user configured data when shutting down HC
+ }
+
+ @Override
+ public final void initialize() throws InitializeException {
+ LOG.debug("AbstractDataTreeConverter.initialize() from(oper): {}, to(cfg): {}", idOper, idConfig);
+ final Optional<O> data = readData();
+
+ if (data.isPresent()) {
+ LOG.debug("Config initialization, operational data={}", data);
+
+ final O operationalData = data.get();
+ final C configData = convert(operationalData);
+
+ try {
+ LOG.debug("Initializing config with data={}", configData);
+ writeData(configData);
+ LOG.info("Config initialization successful from(oper): {}, to(cfg): {}", idOper, idConfig);
+ } catch (TransactionCommitFailedException e) {
+ throw new InitializeException("Failed to perform config initialization", e);
+ }
+ } else {
+ LOG.info("Data is not present under: {}, no initial changes to config at: {}", idOper, idConfig);
+ }
+ }
+
+ private Optional<O> readData() {
+ try (ReadOnlyTransaction readTx = bindingDataBroker.newReadOnlyTransaction()) {
+ final CheckedFuture<Optional<O>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException>
+ readFuture = readTx.read(LogicalDatastoreType.OPERATIONAL, idOper);
+ return readFuture.checkedGet();
+ } catch (org.opendaylight.controller.md.sal.common.api.data.ReadFailedException e) {
+ LOG.warn("Failed to read operational state", e);
+ }
+ return Optional.absent();
+ }
+
+ private void writeData(final C configData) throws TransactionCommitFailedException {
+ final WriteTransaction writeTx = bindingDataBroker.newWriteOnlyTransaction();
+ // Merge(instead of put) has to be used due to dynamic start, this might be executed multiple times
+ // and might overwrite config restored from persisted file with the same incomplete config.
+ // Making the entire configuration trigger VPP twice (on second persis ... and VPP does not like that
+ writeTx.merge(LogicalDatastoreType.CONFIGURATION, idConfig, configData);
+ writeTx.submit().checkedGet();
+ }
+
+ // TODO make this class concrete and use function dependency instead of abstract method
+ /**
+ * Converts operational data to config data for given root node
+ * @param operationalData data object representing operational data
+ * @return data object representing config data
+ */
+ protected abstract C convert(final O operationalData);
+}
diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/DataTreeInitializer.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/DataTreeInitializer.java
new file mode 100644
index 000000000..d760401f9
--- /dev/null
+++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/DataTreeInitializer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.vpp.data.init;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Service for config data tree initialization.
+ * Implementation reads operational data and initializes config data tree.
+ * Initialization does not cause any change in VPP state, unlike ordinary writes to config.
+ */
+@Beta
+public interface DataTreeInitializer extends AutoCloseable {
+
+ /**
+ * Initializes config data tree for supported root node.
+ * @throws InitializeException if initialization failed
+ */
+ void initialize() throws InitializeException;
+
+ /**
+ * Removes all data managed by the initializer.
+ */
+ @Override
+ void close() throws Exception;
+
+ class InitializeException extends Exception {
+
+ public InitializeException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+ public InitializeException(final String msg) {
+ super(msg);
+ }
+ }
+}
diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistry.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistry.java
new file mode 100644
index 000000000..8760f0f09
--- /dev/null
+++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistry.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.vpp.data.init;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Data tree initializer suitable as a holder for all other root initializers, providing initializeAll feature.
+ */
+@Beta
+public interface InitializerRegistry extends DataTreeInitializer {
+
+ /**
+ * Performs initialize on all registered root initializers.
+ * @throws if initialization failed
+ */
+ @Override
+ void initialize() throws InitializeException;
+}
diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImpl.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImpl.java
new file mode 100644
index 000000000..e5220f7e8
--- /dev/null
+++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImpl.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.vpp.data.init;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InitializerRegistryImpl implements InitializerRegistry {
+ private static final Logger LOG = LoggerFactory.getLogger(InitializerRegistryImpl.class);
+ private final List<DataTreeInitializer> initializers;
+
+ public InitializerRegistryImpl(@Nonnull List<DataTreeInitializer> initializers) {
+ this.initializers = checkNotNull(initializers, "initializers should not be null");
+ checkArgument(!initializers.contains(null), "initializers should not contain null elements");
+ }
+
+ @Override
+ public void close() throws Exception {
+ LOG.debug("InitializerRegistryImpl.close()");
+ for (DataTreeInitializer initializer : initializers) {
+ initializer.close();
+ }
+ }
+
+ @Override
+ public void initialize() throws InitializeException {
+ // TODO check if readers are there
+ LOG.debug("InitializerRegistryImpl.initialize()");
+ for (DataTreeInitializer initializer : initializers) {
+ initializer.initialize();
+ }
+ }
+}
diff --git a/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java
new file mode 100644
index 000000000..abc3f54c0
--- /dev/null
+++ b/infra/cfg-init/src/main/java/io/fd/honeycomb/v3po/vpp/data/init/RestoringInitializer.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.vpp.data.init;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import io.fd.honeycomb.v3po.translate.util.JsonUtils;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.RestorationType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RestoringInitializer implements DataTreeInitializer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RestoringInitializer.class);
+
+ private final SchemaService schemaService;
+ private final Path path;
+ private final DOMDataBroker dataTree;
+ private final RestorationType restorationType;
+ private final LogicalDatastoreType datastoreType;
+
+ public RestoringInitializer(@Nonnull final SchemaService schemaService,
+ @Nonnull final Path path,
+ @Nonnull final DOMDataBroker dataTree,
+ @Nonnull final RestorationType restorationType,
+ @Nonnull final LogicalDatastoreType datastoreType) {
+ this.schemaService = schemaService;
+ this.datastoreType = datastoreType;
+ this.path = checkStorage(path);
+ this.dataTree = dataTree;
+ this.restorationType = restorationType;
+ }
+
+ private Path checkStorage(final Path path) {
+ if (Files.exists(path)) {
+ checkArgument(!Files.isDirectory(path), "File %s is a directory", path);
+ checkArgument(Files.isReadable(path), "File %s is not readable", path);
+ }
+
+ return path;
+ }
+
+ @Override
+ public void initialize() throws InitializeException {
+ LOG.debug("Starting restoration of {} from {} using {}", dataTree, path, restorationType);
+ if(!Files.exists(path)) {
+ LOG.debug("Persist file {} does not exist. Skipping restoration", path);
+ return;
+ }
+
+ try {
+ final ContainerNode containerNode = JsonUtils
+ .readJsonRoot(schemaService.getGlobalContext(), Files.newInputStream(path, StandardOpenOption.READ));
+
+ final DOMDataWriteTransaction domDataWriteTransaction = dataTree.newWriteOnlyTransaction();
+ for (DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> dataContainerChild : containerNode
+ .getValue()) {
+ final YangInstanceIdentifier iid = YangInstanceIdentifier.create(dataContainerChild.getIdentifier());
+ LOG.trace("Restoring {} from {}", iid, path);
+
+ switch (restorationType) {
+ case Merge:
+ domDataWriteTransaction.merge(datastoreType, iid, dataContainerChild);
+ break;
+ case Put:
+ domDataWriteTransaction.put(datastoreType, iid, dataContainerChild);
+ break;
+ default:
+ throw new InitializeException(
+ "Unable to initialize data using " + restorationType + " restoration strategy. Unsupported");
+ }
+ }
+
+ // Block here to prevent subsequent initializers processing before context is fully restored
+ domDataWriteTransaction.submit().checkedGet();
+ LOG.debug("Data from {} restored successfully", path);
+
+ } catch (IOException | TransactionCommitFailedException e) {
+ throw new InitializeException("Unable to restore data from " + path, e);
+ }
+ }
+
+ @Override
+ public void close() {}
+}
diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModule.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModule.java
new file mode 100644
index 000000000..3fff82e32
--- /dev/null
+++ b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModule.java
@@ -0,0 +1,54 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407;
+
+import io.fd.honeycomb.v3po.vpp.data.init.InitializerRegistry;
+import io.fd.honeycomb.v3po.vpp.data.init.InitializerRegistryImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+* Initializer registry, delegating initialization to a list of initializers
+*/
+public class ConfigurationInitializerRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.AbstractConfigurationInitializerRegistryModule {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ConfigurationInitializerRegistryModule.class);
+
+ public ConfigurationInitializerRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ConfigurationInitializerRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.ConfigurationInitializerRegistryModule 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() {
+ LOG.info("VppConfigurationInitializerModule.createInstance(): initialization started");
+
+ final InitializerRegistry initializer = new InitializerRegistryImpl(getInitializersDependency());
+
+ try {
+ // Initialize contexts first so that other initializers can find any relevant mapping before initializing
+ // configuration to what is already in VPP
+ getPersistedContextInitializerDependency().initialize();
+ LOG.info("Persisted context restored successfully");
+ // Initialize all registered initializers
+ initializer.initialize();
+ LOG.info("VPP configuration initialized successfully from VPP");
+ // Initialize stored configuration on top
+ getPersistedConfigInitializerDependency().initialize();
+ LOG.info("Persisted configuration restored successfully");
+ } catch (Exception e) {
+ LOG.warn("Failed to initialize config", e);
+ }
+
+ LOG.info("Honeycomb initialized");
+
+ return initializer;
+ }
+
+}
diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java
new file mode 100644
index 000000000..f07be953e
--- /dev/null
+++ b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/ConfigurationInitializerRegistryModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: vpp-cfg-init yang module local name: cfg-initializer-registry
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed May 18 14:43:49 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407;
+public class ConfigurationInitializerRegistryModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.AbstractConfigurationInitializerRegistryModuleFactory {
+
+}
diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModule.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModule.java
new file mode 100644
index 000000000..188d1641c
--- /dev/null
+++ b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModule.java
@@ -0,0 +1,38 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407;
+
+import io.fd.honeycomb.v3po.vpp.data.init.RestoringInitializer;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Paths;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.api.rev160411.DatatreeType;
+
+/**
+* Initializer restoring data from a persisted file
+*/
+public class PersistedFileInitializerModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.AbstractPersistedFileInitializerModule {
+ public PersistedFileInitializerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public PersistedFileInitializerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.PersistedFileInitializerModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ try {
+ Paths.get(getPersistFilePath());
+ } catch (InvalidPathException e) {
+ throw new JmxAttributeValidationException("Invalid persist path", e, persistFilePathJmxAttribute);
+ }
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new RestoringInitializer(getSchemaServiceDependency(), Paths.get(getPersistFilePath()),
+ getDomDataBrokerDependency(), getRestorationType(),
+ getDatastoreType() == DatatreeType.Config ? LogicalDatastoreType.CONFIGURATION : LogicalDatastoreType.OPERATIONAL);
+ }
+
+}
diff --git a/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModuleFactory.java b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModuleFactory.java
new file mode 100644
index 000000000..1d7a3c9c6
--- /dev/null
+++ b/infra/cfg-init/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/vpp/data/init/rev160407/PersistedFileInitializerModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: vpp-cfg-init yang module local name: persisted-file-initializer
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed May 18 13:48:52 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407;
+public class PersistedFileInitializerModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.vpp.data.init.rev160407.AbstractPersistedFileInitializerModuleFactory {
+
+}
diff --git a/infra/cfg-init/src/main/yang/vpp-cfg-init.yang b/infra/cfg-init/src/main/yang/vpp-cfg-init.yang
new file mode 100644
index 000000000..52750d926
--- /dev/null
+++ b/infra/cfg-init/src/main/yang/vpp-cfg-init.yang
@@ -0,0 +1,117 @@
+module vpp-cfg-init {
+ yang-version 1;
+ namespace "urn:honeycomb:params:xml:ns:yang:vpp:data:init";
+ prefix "init";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import opendaylight-md-sal-dom {prefix dom;}
+ import translate-api { prefix tapi; revision-date 2016-04-06; }
+ import data-api { prefix dapi; revision-date 2016-04-11; }
+
+ description
+ "This module contains initializers for VPP config data tree";
+
+ revision "2016-04-07" {
+ description
+ "Initial revision";
+ }
+
+ identity cfg-initializer {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.vpp.data.init.DataTreeInitializer;
+ }
+
+ identity cfg-initializer-registry {
+ base config:module-type;
+ config:provided-service cfg-initializer;
+ config:java-name-prefix ConfigurationInitializerRegistry;
+ description "Initializer registry, delegating initialization to a list of initializers";
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case cfg-initializer-registry {
+ when "/config:modules/config:module/config:type = 'cfg-initializer-registry'";
+
+ list initializers {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity cfg-initializer;
+ }
+ }
+ }
+
+ container persisted-context-initializer {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity cfg-initializer;
+ }
+ }
+ }
+
+ container persisted-config-initializer {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity cfg-initializer;
+ }
+ }
+ }
+ }
+ }
+
+ identity persisted-file-initializer {
+ base config:module-type;
+ config:provided-service cfg-initializer;
+ description "Initializer restoring data from a persisted file";
+ }
+
+ typedef restoration-type {
+ type enumeration {
+ enum merge;
+ enum put;
+ }
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case persisted-file-initializer {
+ when "/config:modules/config:module/config:type = 'persisted-file-initializer'";
+
+ container dom-data-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:dom-async-data-broker;
+ }
+ }
+ }
+
+ container schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:schema-service;
+ }
+ }
+ }
+
+ leaf persist-file-path {
+ type string;
+ mandatory true;
+ }
+
+ leaf restoration-type {
+ type restoration-type;
+ default merge;
+ }
+
+
+ leaf datastore-type {
+ type dapi:datatree-type;
+ mandatory true;
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/infra/cfg-init/src/test/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImplTest.java b/infra/cfg-init/src/test/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImplTest.java
new file mode 100644
index 000000000..d562fb60c
--- /dev/null
+++ b/infra/cfg-init/src/test/java/io/fd/honeycomb/v3po/vpp/data/init/InitializerRegistryImplTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.vpp.data.init;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class InitializerRegistryImplTest {
+
+ @Mock(name="dti1")
+ private DataTreeInitializer dti1;
+ @Mock(name="dti2")
+ private DataTreeInitializer dti2;
+ @Mock(name="dti3")
+ private DataTreeInitializer dti3;
+
+ private InitializerRegistryImpl initializerRegistry;
+
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ initializerRegistry = new InitializerRegistryImpl(Arrays.asList(dti1, dti2, dti3));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConstructorFailed() throws Exception {
+ new InitializerRegistryImpl(Arrays.asList(dti1, null));
+ }
+
+ @Test
+ public void testInitialize() throws Exception {
+ initializerRegistry.initialize();
+
+ verify(dti1).initialize();
+ verify(dti2).initialize();
+ verify(dti3).initialize();
+ }
+
+ @Test
+ public void testClose() throws Exception {
+ initializerRegistry.close();
+
+ verify(dti1).close();
+ verify(dti2).close();
+ verify(dti3).close();
+ }
+} \ No newline at end of file
diff --git a/infra/data-api/Readme.adoc b/infra/data-api/Readme.adoc
new file mode 100644
index 000000000..a13c5094f
--- /dev/null
+++ b/infra/data-api/Readme.adoc
@@ -0,0 +1,10 @@
+= Honeycomb Data layer. API
+
+Provides data tree abstraction for data store modeling.
+ModifiableDataTree can be used to model CONFIG data store.
+ReadableDataTree can be used to model OPERATIONAL data store.
+
+
+
+
+
diff --git a/infra/data-api/pom.xml b/infra/data-api/pom.xml
new file mode 100644
index 000000000..eba388ce9
--- /dev/null
+++ b/infra/data-api/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>impl-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>data-api</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ <!-- FIXME use dependency management -->
+ <version>1.3.2-Beryllium-SR2</version>
+ </dependency>
+
+ </dependencies>
+
+</project> \ No newline at end of file
diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java
new file mode 100644
index 000000000..d05c55716
--- /dev/null
+++ b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/DataModification.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+
+/**
+ * Modification of a {@link ModifiableDataManager}.
+ */
+@Beta
+public interface DataModification extends ReadableDataManager {
+
+ /**
+ * Delete the node at specified path.
+ *
+ * @param path Node path
+ */
+ void delete(YangInstanceIdentifier path);
+
+ /**
+ * Merge the specified data with the currently-present data
+ * at specified path.
+ *
+ * @param path Node path
+ * @param data Data to be merged
+ */
+ void merge(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ * Replace the data at specified path with supplied data.
+ *
+ * @param path Node path
+ * @param data New node data
+ */
+ void write(YangInstanceIdentifier path, NormalizedNode<?, ?> data);
+
+ /**
+ * Alters data tree using this modification.
+ *
+ * @throws DataValidationFailedException if modification data is not valid
+ * @throws TranslationException if failed while updating data tree state
+ */
+ void commit() throws DataValidationFailedException, TranslationException;
+
+ /**
+ * Validate and prepare modification before commit. Besides commit, no further operation is expected after validate
+ * and the behaviour is undefined.
+ *
+ * @throws DataValidationFailedException if modification data is not valid
+ */
+ void validate() throws DataValidationFailedException;
+}
diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java
new file mode 100644
index 000000000..11cd513ea
--- /dev/null
+++ b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ModifiableDataManager.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Facade over data tree that allows tree modification.
+ */
+@Beta
+public interface ModifiableDataManager extends ReadableDataManager {
+
+ /**
+ * Creates read-only snapshot of a ModifiableDataTree.
+ *
+ * @return modification
+ */
+ DataModification newModification();
+}
diff --git a/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java
new file mode 100644
index 000000000..0e98c0903
--- /dev/null
+++ b/infra/data-api/src/main/java/io/fd/honeycomb/v3po/data/ReadableDataManager.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Facade over data tree that allows reading tree nodes.
+ */
+@Beta
+public interface ReadableDataManager {
+
+ /**
+ * Reads a particular node from the data tree.
+ *
+ * @param path Path of the node
+ * @return a CheckFuture containing the result of the read.
+ */
+ CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path);
+}
diff --git a/infra/data-api/src/main/yang/data-api.yang b/infra/data-api/src/main/yang/data-api.yang
new file mode 100644
index 000000000..693a73acd
--- /dev/null
+++ b/infra/data-api/src/main/yang/data-api.yang
@@ -0,0 +1,38 @@
+module data-api {
+ yang-version 1;
+ namespace "urn:honeycomb:params:xml:ns:yang:data:api";
+ prefix "dapi";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "This module contains the base YANG data tree definitions
+ for data store modeling";
+
+ revision "2016-04-11" {
+ description
+ "Initial revision.";
+ }
+
+ identity data-tree {
+ base "config:service-type";
+ config:java-class org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+ }
+
+ identity honeycomb-readable-data-tree {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.data.ReadableDataManager;
+ }
+
+ identity honeycomb-modifiable-data-tree {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.data.ModifiableDataManager;
+ }
+
+ typedef datatree-type {
+ type enumeration {
+ enum config;
+ enum oper;
+ }
+ }
+} \ No newline at end of file
diff --git a/infra/data-impl/Readme.adoc b/infra/data-impl/Readme.adoc
new file mode 100644
index 000000000..61b2653b3
--- /dev/null
+++ b/infra/data-impl/Readme.adoc
@@ -0,0 +1,43 @@
+= Honeycomb Data layer. Implementation
+
+Models CONFIG data store as a DataTree.
+
+Every read transaction reads directly from the DataTree.
+Every write transaction is validated, then passed to the translation layer.
+After successful update, config data tree is updated.
+
+OPERATIONAL data store reads are passed directly to the translation layer.
+
+Data transaction functionality for higher layers (i.e NETCONF/RESTCONF layer) is provided by DataBroker.
+
+[ditaa, "data-layer-architecture"]
+....
+ /------------------\
+ | RESTCONF/NETCONF |
+ \--------+---------/
+ |
+----------------------------|------------------------------------
+ v
+ /------------------\
+ | DataBroker |
+ \-+--------------+-/
+ | |
+ | read | read/write
+ Honeycomb v v
+ data layer /------+------\ /--+--------\ /----------\
+ | | | | | |
+ | Operational | | Config +------>+ DataTree +
+ | DataTree | | DataTree | | |
+ | | | | \----------/
+ \------+------/ \--+--------/
+ | |
+---------------------|--------------|----------------------------
+ v v
+ /-----+--------------+-----\
+ | Translation layer |
+ \--------------------------/
+....
+
+
+
+
diff --git a/infra/data-impl/pom.xml b/infra/data-impl/pom.xml
new file mode 100644
index 000000000..b80c796a5
--- /dev/null
+++ b/infra/data-impl/pom.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <artifactId>impl-parent</artifactId>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>data-impl</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-dom-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ <!-- FIXME use dependency management -->
+ <version>1.3.2-Beryllium-SR2</version>
+ </dependency>
+
+ <!-- FIXME workaround for https://git.opendaylight.org/gerrit/#/c/37499/-->
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <version>1.0.2-Beryllium-SR2</version>
+ </dependency>
+ <!-- FIXME workaround for https://git.opendaylight.org/gerrit/#/c/37499/-->
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>ietf-netconf-monitoring-extension</artifactId>
+ <version>1.0.2-Beryllium-SR2</version>
+ </dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Export-Package>
+ io.fd.honeycomb.v3po.data.impl.*
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project> \ No newline at end of file
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java
new file mode 100644
index 000000000..c418ed332
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/DataBroker.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.data.ModifiableDataManager;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Data Broker which provides data transaction functionality for YANG capable data provider using {@link NormalizedNode}
+ * data format.
+ */
+public class DataBroker implements DOMDataBroker, Closeable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DataBroker.class);
+ private final TransactionFactory transactionFactory;
+
+ /**
+ * Creates DataBroker instance.
+ *
+ * @param transactionFactory transaction producing factory
+ */
+ public DataBroker(final TransactionFactory transactionFactory) {
+ this.transactionFactory = transactionFactory;
+ }
+
+ @Override
+ public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ LOG.trace("DataBroker({}).newReadOnlyTransaction()", this);
+ return transactionFactory.newReadOnlyTransaction();
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ LOG.trace("DataBroker({}).newReadWriteTransaction()", this);
+ return transactionFactory.newReadWriteTransaction();
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ LOG.trace("DataBroker({}).newWriteOnlyTransaction()", this);
+ return transactionFactory.newWriteOnlyTransaction();
+ }
+
+ @Override
+ public ListenerRegistration<DOMDataChangeListener> registerDataChangeListener(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path,
+ final DOMDataChangeListener listener,
+ final DataChangeScope triggeringScope) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public DOMTransactionChain createTransactionChain(final TransactionChainListener listener) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Nonnull
+ @Override
+ public Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> getSupportedExtensions() {
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Create DataBroker for a modifiable config DT, but only readable Operational
+ */
+ @Nonnull
+ public static DataBroker create(@Nonnull final ModifiableDataManager configDataTree,
+ @Nonnull final ReadableDataManager operationalDataTree) {
+ checkNotNull(operationalDataTree, "operationalDataTree should not be null");
+ checkNotNull(configDataTree, "configDataTree should not be null");
+ return new DataBroker(new MainPipelineTxFactory(configDataTree, operationalDataTree));
+ }
+
+ /**
+ * Create DataBroker for modifiable operational DT, but no support for config
+ */
+ @Nonnull
+ public static DataBroker create(@Nonnull final ModifiableDataManager operationalDataTree) {
+ checkNotNull(operationalDataTree, "operationalDataTree should not be null");
+ return new DataBroker(new ContextPipelineTxFactory(operationalDataTree));
+ }
+
+ @Override
+ public void close() throws IOException {
+ // NOOP
+ }
+
+ /**
+ * Transaction provider factory to be used by {@link DataBroker}
+ */
+ public interface TransactionFactory {
+
+ DOMDataReadOnlyTransaction newReadOnlyTransaction();
+
+ DOMDataReadWriteTransaction newReadWriteTransaction();
+
+ DOMDataWriteTransaction newWriteOnlyTransaction();
+ }
+
+ /**
+ * Transaction factory specific for Honeycomb's main pipeline (config: read+write, operational: read-only)
+ */
+ private static class MainPipelineTxFactory implements TransactionFactory {
+ private final ReadableDataManager operationalDataTree;
+ private final ModifiableDataManager configDataTree;
+
+ MainPipelineTxFactory(@Nonnull final ModifiableDataManager configDataTree,
+ @Nonnull final ReadableDataManager operationalDataTree) {
+ this.operationalDataTree = operationalDataTree;
+ this.configDataTree = configDataTree;
+ }
+
+ @Override
+ public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ return ReadOnlyTransaction.create(configDataTree.newModification(), operationalDataTree);
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ final DataModification configModification = configDataTree.newModification();
+ return new ReadWriteTransaction(
+ ReadOnlyTransaction.create(configModification, operationalDataTree),
+ WriteTransaction.createConfigOnly(configModification));
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ return WriteTransaction.createConfigOnly(configDataTree.newModification());
+ }
+ }
+
+ /**
+ * Transaction factory specific for Honeycomb's context pipeline (config: none, operational: read+write)
+ */
+ private static class ContextPipelineTxFactory implements TransactionFactory {
+ private final ModifiableDataManager operationalDataTree;
+
+ ContextPipelineTxFactory(@Nonnull final ModifiableDataManager operationalDataTree) {
+ this.operationalDataTree = operationalDataTree;
+ }
+
+ @Override
+ public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
+ return ReadOnlyTransaction.createOperationalOnly(operationalDataTree);
+ }
+
+ @Override
+ public DOMDataReadWriteTransaction newReadWriteTransaction() {
+ final DataModification dataModification = operationalDataTree.newModification();
+ return new ReadWriteTransaction(
+ ReadOnlyTransaction.createOperationalOnly(dataModification),
+ WriteTransaction.createOperationalOnly(dataModification));
+ }
+
+ @Override
+ public DOMDataWriteTransaction newWriteOnlyTransaction() {
+ return WriteTransaction.createOperationalOnly(operationalDataTree.newModification());
+ }
+ }
+}
+
+
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
new file mode 100644
index 000000000..2c2581ec0
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegator.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.immediateCheckedFuture;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.util.TransactionMappingContext;
+import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extension of {@link ModifiableDataTreeManager} that propagates data changes to underlying writer layer before they
+ * are fully committed in the backing data tree. Data changes are propagated in BA format.
+ */
+public final class ModifiableDataTreeDelegator extends ModifiableDataTreeManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeDelegator.class);
+ private static final ReadableDataManager EMPTY_OPERATIONAL = p -> immediateCheckedFuture(Optional.absent());
+
+ private final WriterRegistry writerRegistry;
+ private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker;
+ // TODO what to use instead of deprecated BindingNormalizedNodeSerializer ?
+ private final BindingNormalizedNodeSerializer serializer;
+
+ /**
+ * Creates configuration data tree instance.
+ * @param serializer service for serialization between Java Binding Data representation and NormalizedNode
+ * representation.
+ * @param dataTree data tree for configuration data representation
+ * @param writerRegistry service for translation between Java Binding Data and data provider, capable of performing
+ * @param contextBroker BA broker providing full access to mapping context data
+ */
+ public ModifiableDataTreeDelegator(@Nonnull final BindingNormalizedNodeSerializer serializer,
+ @Nonnull final DataTree dataTree,
+ @Nonnull final WriterRegistry writerRegistry,
+ @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) {
+ super(dataTree);
+ this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null");
+ this.serializer = checkNotNull(serializer, "serializer should not be null");
+ this.writerRegistry = checkNotNull(writerRegistry, "writerRegistry should not be null");
+ }
+
+ @Override
+ public DataModification newModification() {
+ return new ConfigSnapshot(super.newModification());
+ }
+
+ private final class ConfigSnapshot extends ModifiableDataTreeManager.ConfigSnapshot {
+
+ private final DataModification untouchedModification;
+
+ /**
+ * @param untouchedModification DataModification captured while this modification/snapshot was created.
+ * To be used later while invoking writers to provide them with before state
+ * (state without current modifications).
+ * It must be captured as close as possible to when current modification started.
+ */
+ ConfigSnapshot(final DataModification untouchedModification) {
+ this.untouchedModification = untouchedModification;
+ }
+
+ /**
+ * Pass the changes to underlying writer layer.
+ * Transform from BI to BA.
+ * Revert(Write data before to subtrees that have been successfully modified before failure) in case of failure.
+ */
+ @Override
+ protected void processCandidate(final DataTreeCandidate candidate)
+ throws TranslationException {
+
+ final DataTreeCandidateNode rootNode = candidate.getRootNode();
+ final YangInstanceIdentifier rootPath = candidate.getRootPath();
+ LOG.trace("ConfigDataTree.modify() rootPath={}, rootNode={}, dataBefore={}, dataAfter={}",
+ rootPath, rootNode, rootNode.getDataBefore(), rootNode.getDataAfter());
+
+ final ModificationDiff modificationDiff =
+ ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, rootNode);
+ LOG.debug("ConfigDataTree.modify() diff: {}", modificationDiff);
+
+ // Distinguish between updates (create + update) and deletes
+ final WriterRegistry.DataObjectUpdates baUpdates = toBindingAware(modificationDiff.getUpdates());
+ LOG.debug("ConfigDataTree.modify() extracted updates={}", baUpdates);
+
+ try (final WriteContext ctx = getTransactionWriteContext()) {
+ writerRegistry.update(baUpdates, ctx);
+
+ final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult =
+ ((TransactionMappingContext) ctx.getMappingContext()).submit();
+ // Blocking on context data update
+ contextUpdateResult.checkedGet();
+
+ } catch (WriterRegistry.BulkUpdateException e) {
+ LOG.warn("Failed to apply all changes", e);
+ LOG.info("Trying to revert successful changes for current transaction");
+
+ try {
+ e.revertChanges();
+ LOG.info("Changes successfully reverted");
+ } catch (WriterRegistry.Reverter.RevertFailedException revertFailedException) {
+ // fail with failed revert
+ LOG.error("Failed to revert successful changes", revertFailedException);
+ throw revertFailedException;
+ }
+
+ throw e; // fail with success revert
+ } catch (TransactionCommitFailedException e) {
+ // FIXME revert should probably occur when context is not written successfully
+ final String msg = "Error while updating mapping context data";
+ LOG.error(msg, e);
+ throw new TranslationException(msg, e);
+ } catch (TranslationException e) {
+ LOG.error("Error while processing data change (updates={})", baUpdates, e);
+ throw e;
+ }
+ }
+
+ private TransactionWriteContext getTransactionWriteContext() {
+ // Before Tx must use modification
+ final DOMDataReadOnlyTransaction beforeTx = ReadOnlyTransaction.create(untouchedModification, EMPTY_OPERATIONAL);
+ // After Tx must use current modification
+ final DOMDataReadOnlyTransaction afterTx = ReadOnlyTransaction.create(this, EMPTY_OPERATIONAL);
+ final TransactionMappingContext mappingContext = new TransactionMappingContext(
+ contextBroker.newReadWriteTransaction());
+ return new TransactionWriteContext(serializer, beforeTx, afterTx, mappingContext);
+ }
+
+ private WriterRegistry.DataObjectUpdates toBindingAware(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes) {
+ return ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer);
+ }
+ }
+
+ @VisibleForTesting
+ static WriterRegistry.DataObjectUpdates toBindingAware(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes,
+ final BindingNormalizedNodeSerializer serializer) {
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> dataObjectUpdates = HashMultimap.create();
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> dataObjectDeletes =
+ HashMultimap.create();
+
+ for (Map.Entry<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biEntry : biNodes.entrySet()) {
+ final InstanceIdentifier<?> unkeyedIid =
+ RWUtils.makeIidWildcarded(serializer.fromYangInstanceIdentifier(biEntry.getKey()));
+
+ ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate = biEntry.getValue();
+ final DataObjectUpdate dataObjectUpdate = toDataObjectUpdate(normalizedNodeUpdate, serializer);
+ if (dataObjectUpdate != null) {
+ if (dataObjectUpdate instanceof DataObjectUpdate.DataObjectDelete) {
+ dataObjectDeletes.put(unkeyedIid, ((DataObjectUpdate.DataObjectDelete) dataObjectUpdate));
+ } else {
+ dataObjectUpdates.put(unkeyedIid, dataObjectUpdate);
+ }
+ }
+ }
+ return new WriterRegistry.DataObjectUpdates(dataObjectUpdates, dataObjectDeletes);
+ }
+
+ @Nullable
+ private static DataObjectUpdate toDataObjectUpdate(
+ final ModificationDiff.NormalizedNodeUpdate normalizedNodeUpdate,
+ final BindingNormalizedNodeSerializer serializer) {
+
+ InstanceIdentifier<?> baId = serializer.fromYangInstanceIdentifier(normalizedNodeUpdate.getId());
+ checkNotNull(baId, "Unable to transform instance identifier: %s into BA", normalizedNodeUpdate.getId());
+
+ DataObject dataObjectBefore = getDataObject(serializer,
+ normalizedNodeUpdate.getDataBefore(), normalizedNodeUpdate.getId());
+ DataObject dataObjectAfter =
+ getDataObject(serializer, normalizedNodeUpdate.getDataAfter(), normalizedNodeUpdate.getId());
+
+ return dataObjectBefore == null && dataObjectAfter == null
+ ? null
+ : DataObjectUpdate.create(baId, dataObjectBefore, dataObjectAfter);
+ }
+
+ @Nullable
+ private static DataObject getDataObject(@Nonnull final BindingNormalizedNodeSerializer serializer,
+ @Nullable final NormalizedNode<?, ?> data,
+ @Nonnull final YangInstanceIdentifier id) {
+ DataObject dataObject = null;
+ if (data != null) {
+ final Map.Entry<InstanceIdentifier<?>, DataObject> dataObjectEntry =
+ serializer.fromNormalizedNode(id, data);
+ if (dataObjectEntry != null) {
+ dataObject = dataObjectEntry.getValue();
+ }
+ }
+ return dataObject;
+ }
+
+}
+
+
+
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java
new file mode 100644
index 000000000..1082c479b
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeManager.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.util.concurrent.Futures.immediateCheckedFuture;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.data.ModifiableDataManager;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * DataTree backed implementation for modifiable data manager.
+ */
+public class ModifiableDataTreeManager implements ModifiableDataManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ModifiableDataTreeManager.class);
+
+ private final DataTree dataTree;
+
+ public ModifiableDataTreeManager(@Nonnull final DataTree dataTree) {
+ this.dataTree = checkNotNull(dataTree, "dataTree should not be null");
+ }
+
+ @Override
+ public DataModification newModification() {
+ return new ConfigSnapshot();
+ }
+
+ @Override
+ public final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(@Nonnull final YangInstanceIdentifier path) {
+ return newModification().read(path);
+ }
+
+ protected class ConfigSnapshot implements DataModification {
+ private final DataTreeModification modification;
+ private boolean validated = false;
+
+ ConfigSnapshot() {
+ this(dataTree.takeSnapshot().newModification());
+ }
+
+ protected ConfigSnapshot(final DataTreeModification modification) {
+ this.modification = modification;
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
+ @Nonnull final YangInstanceIdentifier path) {
+ final Optional<NormalizedNode<?, ?>> node = modification.readNode(path);
+ if (LOG.isTraceEnabled() && node.isPresent()) {
+ LOG.trace("ConfigSnapshot.read: {}", node.get());
+ }
+ return immediateCheckedFuture(node);
+ }
+
+ @Override
+ public final void delete(final YangInstanceIdentifier path) {
+ modification.delete(path);
+ }
+
+ @Override
+ public final void merge(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ modification.merge(path, data);
+ }
+
+ @Override
+ public final void write(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
+ modification.write(path, data);
+ }
+
+ @Override
+ public final void commit() throws DataValidationFailedException, TranslationException {
+ if(!validated) {
+ validate();
+ }
+ final DataTreeCandidate candidate = dataTree.prepare(modification);
+ processCandidate(candidate);
+ dataTree.commit(candidate);
+ }
+
+ protected void processCandidate(final DataTreeCandidate candidate) throws TranslationException {
+ // NOOP
+ }
+
+ @Override
+ public final void validate() throws DataValidationFailedException {
+ modification.ready();
+ dataTree.validate(modification);
+ validated = true;
+ }
+ }
+}
+
+
+
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java
new file mode 100644
index 000000000..abc0062de
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ModificationDiff.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+
+/**
+ * Recursively collects and provides all unique and non-null modifications (modified normalized nodes).
+ */
+final class ModificationDiff {
+
+ private static final ModificationDiff EMPTY_DIFF = new ModificationDiff(Collections.emptyMap());
+ private static final EnumSet LEAF_MODIFICATIONS = EnumSet.of(ModificationType.WRITE, ModificationType.DELETE);
+
+ private final Map<YangInstanceIdentifier, NormalizedNodeUpdate> updates;
+
+ private ModificationDiff(@Nonnull Map<YangInstanceIdentifier, NormalizedNodeUpdate> updates) {
+ this.updates = updates;
+ }
+
+ /**
+ * Get processed modifications.
+ *
+ * @return mapped modifications, where key is keyed {@link YangInstanceIdentifier}.
+ */
+ Map<YangInstanceIdentifier, NormalizedNodeUpdate> getUpdates() {
+ return updates;
+ }
+
+ private ModificationDiff merge(final ModificationDiff other) {
+ if (this == EMPTY_DIFF) {
+ return other;
+ }
+
+ if (other == EMPTY_DIFF) {
+ return this;
+ }
+
+ return new ModificationDiff(join(updates, other.updates));
+ }
+
+ private static Map<YangInstanceIdentifier, NormalizedNodeUpdate> join(Map<YangInstanceIdentifier, NormalizedNodeUpdate> first,
+ Map<YangInstanceIdentifier, NormalizedNodeUpdate> second) {
+ final Map<YangInstanceIdentifier, NormalizedNodeUpdate> merged = new HashMap<>();
+ merged.putAll(first);
+ merged.putAll(second);
+ return merged;
+ }
+
+ private static ModificationDiff create(YangInstanceIdentifier id, DataTreeCandidateNode candidate) {
+ return new ModificationDiff(ImmutableMap.of(id, NormalizedNodeUpdate.create(id, candidate)));
+ }
+
+ /**
+ * Produce an aggregated diff from a candidate node recursively. MixinNodes are ignored as modifications and so
+ * are complex nodes which direct leaves were not modified.
+ */
+ @Nonnull
+ static ModificationDiff recursivelyFromCandidate(@Nonnull final YangInstanceIdentifier yangIid,
+ @Nonnull final DataTreeCandidateNode currentCandidate) {
+ // recursively process child nodes for exact modifications
+ return recursivelyChildrenFromCandidate(yangIid, currentCandidate)
+ // also add modification on current level, if elligible
+ .merge(isModification(currentCandidate)
+ ? ModificationDiff.create(yangIid, currentCandidate)
+ : EMPTY_DIFF);
+ }
+
+ /**
+ * Check whether current node was modified. {@link MixinNode}s are ignored
+ * and only nodes which direct leaves(or choices) are modified are considered a modification.
+ */
+ private static Boolean isModification(@Nonnull final DataTreeCandidateNode currentCandidate) {
+ // Mixin nodes are not considered modifications
+ if (isMixin(currentCandidate) && !isAugment(currentCandidate)) {
+ return false;
+ } else {
+ return isCurrentModified(currentCandidate);
+ }
+ }
+
+ private static Boolean isCurrentModified(final @Nonnull DataTreeCandidateNode currentCandidate) {
+ // Check if there are any modified leaves and if so, consider current node as modified
+ final Boolean directLeavesModified = currentCandidate.getChildNodes().stream()
+ .filter(ModificationDiff::isLeaf)
+ // For some reason, we get modifications on unmodified list keys TODO debug and report ODL bug
+ // and that messes up our modifications collection here, so we need to skip
+ .filter(ModificationDiff::isBeforeAndAfterDifferent)
+ .filter(child -> LEAF_MODIFICATIONS.contains(child.getModificationType()))
+ .findFirst()
+ .isPresent();
+
+ return directLeavesModified
+ // Also check choices (choices do not exist in BA world and if anything within a choice was modified,
+ // consider its parent as being modified)
+ || currentCandidate.getChildNodes().stream()
+ .filter(ModificationDiff::isChoice)
+ // Recursively check each choice if there was any change to it
+ .filter(ModificationDiff::isCurrentModified)
+ .findFirst()
+ .isPresent();
+ }
+
+ /**
+ * Process all non-leaf child nodes recursively, creating aggregated {@link ModificationDiff}.
+ */
+ private static ModificationDiff recursivelyChildrenFromCandidate(final @Nonnull YangInstanceIdentifier yangIid,
+ final @Nonnull DataTreeCandidateNode currentCandidate) {
+ // recursively process child nodes for specific modifications
+ return currentCandidate.getChildNodes().stream()
+ // not interested in modifications to leaves
+ .filter(child -> !isLeaf(child))
+ .map(candidate -> recursivelyFromCandidate(yangIid.node(candidate.getIdentifier()), candidate))
+ .reduce(ModificationDiff::merge)
+ .orElse(EMPTY_DIFF);
+ }
+
+ /**
+ * Check whether candidate.before and candidate.after is different. If not return false.
+ */
+ private static boolean isBeforeAndAfterDifferent(@Nonnull final DataTreeCandidateNode candidateNode) {
+ if (candidateNode.getDataBefore().isPresent()) {
+ return !candidateNode.getDataBefore().get().equals(candidateNode.getDataAfter().orNull());
+ }
+
+ // considering not a modification if data after is also null
+ return candidateNode.getDataAfter().isPresent();
+ }
+
+ /**
+ * Check whether candidate node is for a leaf type node.
+ */
+ private static boolean isLeaf(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof LeafNode<?>
+ || candidateNode.getDataBefore().orNull() instanceof LeafNode<?>;
+ }
+
+ /**
+ * Check whether candidate node is for a Mixin type node.
+ */
+ private static boolean isMixin(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof MixinNode
+ || candidateNode.getDataBefore().orNull() instanceof MixinNode;
+ }
+
+ /**
+ * Check whether candidate node is for an Augmentation type node.
+ */
+ private static boolean isAugment(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof AugmentationNode
+ || candidateNode.getDataBefore().orNull() instanceof AugmentationNode;
+ }
+
+ /**
+ * Check whether candidate node is for a Choice type node.
+ */
+ private static boolean isChoice(final DataTreeCandidateNode candidateNode) {
+ // orNull intentional, some candidate nodes have both data after and data before null
+ return candidateNode.getDataAfter().orNull() instanceof ChoiceNode
+ || candidateNode.getDataBefore().orNull() instanceof ChoiceNode;
+ }
+
+ @Override
+ public String toString() {
+ return "ModificationDiff{updates=" + updates + '}';
+ }
+
+ /**
+ * Update to a normalized node identifiable by its {@link YangInstanceIdentifier}.
+ */
+ static final class NormalizedNodeUpdate {
+
+ @Nonnull
+ private final YangInstanceIdentifier id;
+ @Nullable
+ private final NormalizedNode<?, ?> dataBefore;
+ @Nullable
+ private final NormalizedNode<?, ?> dataAfter;
+
+ private NormalizedNodeUpdate(@Nonnull final YangInstanceIdentifier id,
+ @Nullable final NormalizedNode<?, ?> dataBefore,
+ @Nullable final NormalizedNode<?, ?> dataAfter) {
+ this.id = checkNotNull(id);
+ this.dataAfter = dataAfter;
+ this.dataBefore = dataBefore;
+ }
+
+ @Nullable
+ public NormalizedNode<?, ?> getDataBefore() {
+ return dataBefore;
+ }
+
+ @Nullable
+ public NormalizedNode<?, ?> getDataAfter() {
+ return dataAfter;
+ }
+
+ @Nonnull
+ public YangInstanceIdentifier getId() {
+ return id;
+ }
+
+ static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id,
+ @Nonnull final DataTreeCandidateNode candidate) {
+ return create(id, candidate.getDataBefore().orNull(), candidate.getDataAfter().orNull());
+ }
+
+ static NormalizedNodeUpdate create(@Nonnull final YangInstanceIdentifier id,
+ @Nullable final NormalizedNode<?, ?> dataBefore,
+ @Nullable final NormalizedNode<?, ?> dataAfter) {
+ checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null");
+ return new NormalizedNodeUpdate(id, dataBefore, dataAfter);
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ final NormalizedNodeUpdate that = (NormalizedNodeUpdate) other;
+
+ return id.equals(that.id);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "NormalizedNodeUpdate{" + "id=" + id
+ + ", dataBefore=" + dataBefore
+ + ", dataAfter=" + dataAfter
+ + '}';
+ }
+ }
+
+}
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java
new file mode 100644
index 000000000..9b71dfd62
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapter.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.util.JsonUtils;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Adapter for a DataTree that stores current state of data in backing DataTree on each successful commit.
+ * Uses JSON format.
+ */
+public class PersistingDataTreeAdapter implements DataTree, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PersistingDataTreeAdapter.class);
+
+ private final DataTree delegateDependency;
+ private final SchemaService schemaServiceDependency;
+ private final Path path;
+
+ /**
+ * Create new Persisting DataTree adapter
+ *
+ * @param delegate backing data tree that actually handles all the operations
+ * @param persistPath path to a file (existing or not) to be used as storage for persistence. Full control over
+ * a file at peristPath is expected
+ * @param schemaService schemaContext provier
+ */
+ public PersistingDataTreeAdapter(@Nonnull final DataTree delegate,
+ @Nonnull final SchemaService schemaService,
+ @Nonnull final Path persistPath) {
+ this.path = testPersistPath(checkNotNull(persistPath, "persistPath is null"));
+ this.delegateDependency = checkNotNull(delegate, "delegate is null");
+ this.schemaServiceDependency = checkNotNull(schemaService, "schemaService is null");
+ }
+
+ /**
+ * Test whether file at persistPath exists and is readable or create it along with its parent structure
+ */
+ private Path testPersistPath(final Path persistPath) {
+ try {
+ checkArgument(!Files.isDirectory(persistPath), "Path %s points to a directory", persistPath);
+ if(Files.exists(persistPath)) {
+ checkArgument(Files.isReadable(persistPath),
+ "Provided path %s points to existing, but non-readable file", persistPath);
+ return persistPath;
+ }
+ Files.createDirectories(persistPath.getParent());
+ Files.write(persistPath, new byte[]{}, StandardOpenOption.CREATE);
+ } catch (IOException | UnsupportedOperationException e) {
+ LOG.warn("Provided path for persistence: {} is not usable", persistPath, e);
+ throw new IllegalArgumentException("Path " + persistPath + " cannot be used as ");
+ }
+
+ return persistPath;
+ }
+
+ @Override
+ public DataTreeSnapshot takeSnapshot() {
+ return delegateDependency.takeSnapshot();
+ }
+
+ @Override
+ public void setSchemaContext(final SchemaContext schemaContext) {
+ delegateDependency.setSchemaContext(schemaContext);
+ }
+
+ @Override
+ public void commit(final DataTreeCandidate dataTreeCandidate) {
+ LOG.trace("Commit detected");
+ delegateDependency.commit(dataTreeCandidate);
+ LOG.debug("Delegate commit successful. Persisting data");
+
+ // FIXME doing full read and full write might not be the fastest way of persisting data here
+ final DataTreeSnapshot dataTreeSnapshot = delegateDependency.takeSnapshot();
+
+ // TODO this can be handled in background by a dedicated thread + a limited blocking queue
+ // TODO enable configurable granularity for persists. Maybe doing write on every modification is too much
+ // and we could do bulk persist
+ persistCurrentData(dataTreeSnapshot.readNode(YangInstanceIdentifier.EMPTY));
+ }
+
+ private void persistCurrentData(final Optional<NormalizedNode<?, ?>> currentRoot) {
+ if(currentRoot.isPresent()) {
+ try {
+ LOG.trace("Persisting current data: {} into: {}", currentRoot.get(), path);
+ JsonUtils.writeJsonRoot(currentRoot.get(), schemaServiceDependency.getGlobalContext(),
+ Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
+ LOG.trace("Data persisted successfully in {}", path);
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to persist current data", e);
+ }
+ } else {
+ LOG.debug("Skipping persistence, since there's no data to persist");
+ }
+ }
+
+ @Override
+ public YangInstanceIdentifier getRootPath() {
+ return delegateDependency.getRootPath();
+ }
+
+ @Override
+ public void validate(final DataTreeModification dataTreeModification) throws DataValidationFailedException {
+ delegateDependency.validate(dataTreeModification);
+ }
+
+ @Override
+ public DataTreeCandidate prepare(
+ final DataTreeModification dataTreeModification) {
+ return delegateDependency.prepare(dataTreeModification);
+ }
+
+ @Override
+ public void close() throws Exception {
+ LOG.trace("Closing {} for {}", getClass().getSimpleName(), path);
+ // NOOP
+ }
+}
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java
new file mode 100644
index 000000000..2850a0d9a
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransaction.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class ReadOnlyTransaction implements DOMDataReadOnlyTransaction {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTransaction.class);
+
+ @Nullable
+ private ReadableDataManager operationalData;
+ @Nullable
+ private ReadableDataManager configSnapshot;
+
+ private boolean closed = false;
+
+ /**
+ * @param configData config data tree manager. Null if config reads are not to be supported
+ * @param operationalData operational data tree manager. Null if operational reads are not to be supported
+ */
+ private ReadOnlyTransaction(@Nullable final ReadableDataManager configData,
+ @Nullable final ReadableDataManager operationalData) {
+ this.configSnapshot = configData;
+ this.operationalData = operationalData;
+ }
+
+ @Override
+ public synchronized void close() {
+ closed = true;
+ configSnapshot = null;
+ operationalData = null;
+ }
+
+ @Override
+ public synchronized CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
+ final LogicalDatastoreType store,
+ final YangInstanceIdentifier path) {
+ LOG.debug("ReadOnlyTransaction.read(), store={}, path={}", store, path);
+ checkState(!closed, "Transaction has been closed");
+
+ if (store == LogicalDatastoreType.OPERATIONAL) {
+ checkArgument(operationalData != null, "{} reads not supported", store);
+ return operationalData.read(path);
+ } else {
+ checkArgument(configSnapshot != null, "{} reads not supported", store);
+ return configSnapshot.read(path);
+ }
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path) {
+ LOG.debug("ReadOnlyTransaction.exists() store={}, path={}", store, path);
+
+ ListenableFuture<Boolean> listenableFuture = Futures.transform(read(store, path), IS_NODE_PRESENT);
+ return Futures.makeChecked(listenableFuture, ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER);
+ }
+
+ @Nonnull
+ @Override
+ public Object getIdentifier() {
+ return this;
+ }
+
+ @Nonnull
+ static ReadOnlyTransaction createOperationalOnly(@Nonnull final ReadableDataManager operationalData) {
+ return new ReadOnlyTransaction(null, requireNonNull(operationalData));
+ }
+
+ @Nonnull
+ static ReadOnlyTransaction createConfigOnly(@Nonnull final ReadableDataManager configData) {
+ return new ReadOnlyTransaction(requireNonNull(configData), null);
+ }
+
+ @Nonnull
+ static ReadOnlyTransaction create(@Nonnull final ReadableDataManager configData,
+ @Nonnull final ReadableDataManager operationalData) {
+ return new ReadOnlyTransaction(requireNonNull(configData), requireNonNull(operationalData));
+ }
+
+ private static final Function<? super Optional<NormalizedNode<?, ?>>, ? extends Boolean> IS_NODE_PRESENT =
+ (Function<Optional<NormalizedNode<?, ?>>, Boolean>) input -> input == null ? Boolean.FALSE : input.isPresent();
+
+ private static final Function<? super Exception, ReadFailedException> ANY_EX_TO_READ_FAILED_EXCEPTION_MAPPER =
+ (Function<Exception, ReadFailedException>) e -> new ReadFailedException("Exists failed", e);
+} \ No newline at end of file
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransaction.java
new file mode 100644
index 000000000..88b46437e
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransaction.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Composite DOM transaction that delegates reads to a {@link DOMDataReadTransaction} delegate and writes to a {@link
+ * DOMDataWriteTransaction} delegate.
+ */
+final class ReadWriteTransaction implements DOMDataReadWriteTransaction {
+
+ private final DOMDataReadOnlyTransaction delegateReadTx;
+ private final DOMDataWriteTransaction delegateWriteTx;
+
+ ReadWriteTransaction(@Nonnull final DOMDataReadOnlyTransaction delegateReadTx,
+ @Nonnull final DOMDataWriteTransaction delegateWriteTx) {
+ this.delegateReadTx = Preconditions.checkNotNull(delegateReadTx, "delegateReadTx should not be null");
+ this.delegateWriteTx = Preconditions.checkNotNull(delegateWriteTx, "delegateWriteTx should not be null");
+ }
+
+ @Override
+ public boolean cancel() {
+ delegateReadTx.close();
+ return delegateWriteTx.cancel();
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+ delegateWriteTx.put(store, path, data);
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+ delegateWriteTx.merge(store, path, data);
+ }
+
+ @Override
+ public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ delegateWriteTx.delete(store, path);
+ }
+
+ @Override
+ public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ return delegateWriteTx.submit();
+ }
+
+ @Override
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ return delegateWriteTx.commit();
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path) {
+ return delegateReadTx.read(store, path);
+ }
+
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(final LogicalDatastoreType store,
+ final YangInstanceIdentifier path) {
+ return delegateReadTx.exists(store, path);
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return this;
+ }
+}
+
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java
new file mode 100644
index 000000000..aff023ebc
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegator.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.ModificationCache;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.util.TransactionMappingContext;
+import java.util.Collection;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ReadableDataTree implementation for operational data.
+ */
+public final class ReadableDataTreeDelegator implements ReadableDataManager {
+ private static final Logger LOG = LoggerFactory.getLogger(ReadableDataTreeDelegator.class);
+
+ private final BindingNormalizedNodeSerializer serializer;
+ private final ReaderRegistry readerRegistry;
+ private final SchemaContext globalContext;
+ private final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker;
+
+ /**
+ * Creates operational data tree instance.
+ * @param serializer service for serialization between Java Binding Data representation and NormalizedNode
+ * representation.
+ * @param globalContext service for obtaining top level context data from all yang modules.
+ * @param readerRegistry service responsible for translation between DataObjects and data provider.
+ * @param contextBroker BA broker for context data
+ */
+ public ReadableDataTreeDelegator(@Nonnull BindingNormalizedNodeSerializer serializer,
+ @Nonnull final SchemaContext globalContext,
+ @Nonnull final ReaderRegistry readerRegistry,
+ @Nonnull final org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker) {
+ this.contextBroker = checkNotNull(contextBroker, "contextBroker should not be null");
+ this.globalContext = checkNotNull(globalContext, "globalContext should not be null");
+ this.serializer = checkNotNull(serializer, "serializer should not be null");
+ this.readerRegistry = checkNotNull(readerRegistry, "reader should not be null");
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>,
+ org.opendaylight.controller.md.sal.common.api.data.ReadFailedException> read(
+ @Nonnull final YangInstanceIdentifier yangInstanceIdentifier) {
+
+ try(TransactionMappingContext mappingContext = new TransactionMappingContext(contextBroker.newReadWriteTransaction());
+ ReadContext ctx = new ReadContextImpl(mappingContext)) {
+
+ final Optional<NormalizedNode<?, ?>> value;
+ if (checkNotNull(yangInstanceIdentifier).equals(YangInstanceIdentifier.EMPTY)) {
+ value = readRoot(ctx);
+ } else {
+ value = readNode(yangInstanceIdentifier, ctx);
+ }
+
+ // Submit context mapping updates
+ final CheckedFuture<Void, TransactionCommitFailedException> contextUpdateResult =
+ ((TransactionMappingContext) ctx.getMappingContext()).submit();
+ // Blocking on context data update
+ contextUpdateResult.checkedGet();
+
+ return Futures.immediateCheckedFuture(value);
+
+ } catch (ReadFailedException e) {
+ return Futures.immediateFailedCheckedFuture(
+ new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(
+ "Failed to read VPP data", e));
+ } catch (TransactionCommitFailedException e) {
+ // FIXME revert should probably occur when context is not written successfully
+ final String msg = "Error while updating mapping context data";
+ LOG.error(msg, e);
+ return Futures.immediateFailedCheckedFuture(
+ new org.opendaylight.controller.md.sal.common.api.data.ReadFailedException(msg, e)
+ );
+ }
+ }
+
+ private Optional<NormalizedNode<?, ?>> readNode(final YangInstanceIdentifier yangInstanceIdentifier,
+ final ReadContext ctx) throws ReadFailedException {
+ LOG.debug("OperationalDataTree.readNode(), yangInstanceIdentifier={}", yangInstanceIdentifier);
+ final InstanceIdentifier<?> path = serializer.fromYangInstanceIdentifier(yangInstanceIdentifier);
+ checkNotNull(path, "Invalid instance identifier %s. Cannot create BA equivalent.", yangInstanceIdentifier);
+ LOG.debug("OperationalDataTree.readNode(), path={}", path);
+
+ final Optional<? extends DataObject> dataObject;
+
+ dataObject = readerRegistry.read(path, ctx);
+ if (dataObject.isPresent()) {
+ final NormalizedNode<?, ?> value = toNormalizedNodeFunction(path).apply(dataObject.get());
+ return Optional.<NormalizedNode<?, ?>>fromNullable(value);
+ } else {
+ return Optional.absent();
+ }
+ }
+
+ private Optional<NormalizedNode<?, ?>> readRoot(final ReadContext ctx) throws ReadFailedException {
+ LOG.debug("OperationalDataTree.readRoot()");
+
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> dataNodeBuilder =
+ Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(SchemaContext.NAME));
+
+ final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> dataObjects =
+ readerRegistry.readAll(ctx);
+
+ for (final InstanceIdentifier<? extends DataObject> instanceIdentifier : dataObjects.keySet()) {
+ final YangInstanceIdentifier rootElementId = serializer.toYangInstanceIdentifier(instanceIdentifier);
+ final NormalizedNode<?, ?> node =
+ wrapDataObjects(rootElementId, instanceIdentifier, dataObjects.get(instanceIdentifier));
+ dataNodeBuilder.withChild((DataContainerChild<?, ?>) node);
+ }
+ return Optional.<NormalizedNode<?, ?>>of(dataNodeBuilder.build());
+ }
+
+ private NormalizedNode<?, ?> wrapDataObjects(final YangInstanceIdentifier yangInstanceIdentifier,
+ final InstanceIdentifier<? extends DataObject> instanceIdentifier,
+ final Collection<? extends DataObject> dataObjects) {
+ final Collection<NormalizedNode<?, ?>> normalizedRootElements = Collections2
+ .transform(dataObjects, toNormalizedNodeFunction(instanceIdentifier));
+
+ final DataSchemaNode schemaNode =
+ globalContext.getDataChildByName(yangInstanceIdentifier.getLastPathArgument().getNodeType());
+ if (schemaNode instanceof ListSchemaNode) {
+ // In case of a list, wrap all the values in a Mixin parent node
+ final ListSchemaNode listSchema = (ListSchemaNode) schemaNode;
+ return wrapListIntoMixinNode(normalizedRootElements, listSchema);
+ } else {
+ Preconditions.checkState(dataObjects.size() == 1, "Singleton list was expected");
+ return getOnlyElement(normalizedRootElements);
+ }
+ }
+
+ private static DataContainerChild<?, ?> wrapListIntoMixinNode(
+ final Collection<NormalizedNode<?, ?>> normalizedRootElements, final ListSchemaNode listSchema) {
+ if (listSchema.getKeyDefinition().isEmpty()) {
+ final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> listBuilder =
+ Builders.unkeyedListBuilder();
+ for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
+ listBuilder.withChild((UnkeyedListEntryNode) normalizedRootElement);
+ }
+ return listBuilder.build();
+ } else {
+ final CollectionNodeBuilder<MapEntryNode, ? extends MapNode> listBuilder =
+ listSchema.isUserOrdered()
+ ? Builders.orderedMapBuilder()
+ : Builders.mapBuilder();
+
+ for (NormalizedNode<?, ?> normalizedRootElement : normalizedRootElements) {
+ listBuilder.withChild((MapEntryNode) normalizedRootElement);
+ }
+ return listBuilder.build();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Function<DataObject, NormalizedNode<?, ?>> toNormalizedNodeFunction(final InstanceIdentifier path) {
+ return dataObject -> {
+ LOG.trace("OperationalDataTree.toNormalizedNode(), path={}, dataObject={}", path, dataObject);
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry =
+ serializer.toNormalizedNode(path, dataObject);
+
+ LOG.trace("OperationalDataTree.toNormalizedNode(), normalizedNodeEntry={}", entry);
+ return entry.getValue();
+ };
+ }
+
+ private static final class ReadContextImpl implements ReadContext {
+
+ private final ModificationCache ctx = new ModificationCache();
+ private final MappingContext mappingContext;
+
+ private ReadContextImpl(final MappingContext mappingContext) {
+ this.mappingContext = mappingContext;
+ }
+
+ @Nonnull
+ @Override
+ public ModificationCache getModificationCache() {
+ return ctx;
+ }
+
+ @Nonnull
+ @Override
+ public MappingContext getMappingContext() {
+ return mappingContext;
+ }
+
+ @Override
+ public void close() {
+ // Make sure to clear the storage in case some customizer stored a reference to it to prevent memory leaks
+ ctx.close();
+ }
+ }
+}
diff --git a/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java
new file mode 100644
index 000000000..c8f9bd3db
--- /dev/null
+++ b/infra/data-impl/src/main/java/io/fd/honeycomb/v3po/data/impl/WriteTransaction.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.CANCELED;
+import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.COMMITED;
+import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.FAILED;
+import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.NEW;
+import static org.opendaylight.controller.md.sal.common.api.TransactionStatus.SUBMITED;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import java.util.function.Consumer;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NotThreadSafe
+final class WriteTransaction implements DOMDataWriteTransaction {
+
+ private static final Logger LOG = LoggerFactory.getLogger(WriteTransaction.class);
+
+ @Nullable
+ private DataModification operationalModification;
+ @Nullable
+ private DataModification configModification;
+ private TransactionStatus status = NEW;
+
+ private WriteTransaction(@Nullable final DataModification configModification,
+ @Nullable final DataModification operationalModification) {
+ this.operationalModification = operationalModification;
+ this.configModification = configModification;
+ }
+
+ private void checkIsNew() {
+ Preconditions.checkState(status == NEW, "Transaction was submitted or canceled");
+ }
+
+ @Override
+ public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+ LOG.debug("WriteTransaction.put() store={}, path={}, data={}", store, path, data);
+ checkIsNew();
+ handleOperation(store, (modification) -> modification.write(path, data));
+ }
+
+ private void handleOperation(final LogicalDatastoreType store,
+ final Consumer<DataModification> r) {
+ switch (store) {
+ case CONFIGURATION:
+ checkArgument(configModification != null, "Modification of %s is not supported", store);
+ r.accept(configModification);
+ break;
+ case OPERATIONAL:
+ checkArgument(operationalModification != null, "Modification of %s is not supported", store);
+ r.accept(operationalModification);
+ break;
+ }
+ }
+
+ @Override
+ public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path,
+ final NormalizedNode<?, ?> data) {
+ LOG.debug("WriteTransaction.merge() store={}, path={}, data={}", store, path, data);
+ checkIsNew();
+ handleOperation(store, (modification) -> modification.merge(path, data));
+ }
+
+ @Override
+ public boolean cancel() {
+ if (status != NEW) {
+ // only NEW transactions can be cancelled
+ return false;
+ } else {
+ status = CANCELED;
+ return true;
+ }
+ }
+
+ @Override
+ public void delete(LogicalDatastoreType store, final YangInstanceIdentifier path) {
+ LOG.debug("WriteTransaction.delete() store={}, path={}", store, path);
+ checkIsNew();
+ handleOperation(store, (modification) -> modification.delete(path));
+ }
+
+ @Override
+ public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ LOG.trace("WriteTransaction.submit()");
+ checkIsNew();
+
+ try {
+ status = SUBMITED;
+
+ // Validate first to catch any issues before attempting commit
+ if (configModification != null) {
+ configModification.validate();
+ }
+ if (operationalModification != null) {
+ operationalModification.validate();
+ }
+
+ if(configModification != null) {
+ configModification.commit();
+ }
+ if(operationalModification != null) {
+ operationalModification.commit();
+ }
+
+ status = COMMITED;
+ } catch (DataValidationFailedException | TranslationException e) {
+ status = FAILED;
+ LOG.error("Failed modify data tree", e);
+ return Futures.immediateFailedCheckedFuture(
+ new TransactionCommitFailedException("Failed to validate DataTreeModification", e));
+ }
+ return Futures.immediateCheckedFuture(null);
+ }
+
+ @Override
+ @Deprecated
+ public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+ throw new UnsupportedOperationException("deprecated");
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return this;
+ }
+
+
+ @Nonnull
+ static WriteTransaction createOperationalOnly(@Nonnull final DataModification operationalData) {
+ return new WriteTransaction(null, requireNonNull(operationalData));
+ }
+
+ @Nonnull
+ static WriteTransaction createConfigOnly(@Nonnull final DataModification configData) {
+ return new WriteTransaction(requireNonNull(configData), null);
+ }
+
+ @Nonnull
+ static WriteTransaction create(@Nonnull final DataModification configData,
+ @Nonnull final DataModification operationalData) {
+ return new WriteTransaction(requireNonNull(configData), requireNonNull(operationalData));
+ }
+}
diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
new file mode 100644
index 000000000..eabcdcbc8
--- /dev/null
+++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModule.java
@@ -0,0 +1,71 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.data.ModifiableDataManager;
+import io.fd.honeycomb.v3po.data.impl.ModifiableDataTreeDelegator;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConfigDataTreeModule extends
+ org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.AbstractConfigDataTreeModule {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ConfigDataTreeModule.class);
+
+ public ConfigDataTreeModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ConfigDataTreeModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.ConfigDataTreeModule 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() {
+ LOG.debug("ConfigDataTreeModule.createInstance()");
+ return new CloseableConfigDataTree(
+ new ModifiableDataTreeDelegator(getSerializerDependency(), getDataTreeDependency(),
+ getWriterRegistryBuilderDependency().build(), getContextBindingBrokerDependency()));
+ }
+
+ private static final class CloseableConfigDataTree implements ModifiableDataManager, AutoCloseable {
+
+ private final ModifiableDataTreeDelegator delegate;
+
+ CloseableConfigDataTree(final ModifiableDataTreeDelegator delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void close() throws Exception {
+ LOG.debug("CloseableConfigDataTree.close()");
+ // NOP
+ }
+
+ @Override
+ public DataModification newModification() {
+ LOG.trace("CloseableConfigDataTree.newModification");
+ return delegate.newModification();
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
+ @Nonnull final YangInstanceIdentifier path) {
+ return delegate.read(path);
+ }
+ }
+}
diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModuleFactory.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModuleFactory.java
new file mode 100644
index 000000000..19baab13a
--- /dev/null
+++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/ConfigDataTreeModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: data-impl yang module local name: config-data-tree
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Sun Apr 10 22:07:29 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
+public class ConfigDataTreeModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.AbstractConfigDataTreeModuleFactory {
+
+}
diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModule.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModule.java
new file mode 100644
index 000000000..99e5b396c
--- /dev/null
+++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModule.java
@@ -0,0 +1,79 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
+
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.api.rev160411.DatatreeType;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.*;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InMemoryDataTreeModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.AbstractInMemoryDataTreeModule {
+
+ private static final Logger LOG = LoggerFactory.getLogger(InMemoryDataTreeModule.class);
+
+ public InMemoryDataTreeModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public InMemoryDataTreeModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.InMemoryDataTreeModule 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() {
+ LOG.debug("InMemoryConfigDataTreeModule.createInstance()");
+ return new CloseableConfigDataTree(getSchemaServiceDependency().getGlobalContext(), getType());
+ }
+
+ private static class CloseableConfigDataTree implements AutoCloseable, DataTree {
+ private final DataTree dataTree;
+
+ CloseableConfigDataTree(final SchemaContext schemaContext, final DatatreeType type) {
+ this.dataTree = InMemoryDataTreeFactory.getInstance().create(
+ type == DatatreeType.Config ? TreeType.CONFIGURATION : TreeType.OPERATIONAL
+ );
+ dataTree.setSchemaContext(schemaContext);
+ }
+
+ @Override
+ public void close() throws Exception {
+ // NOP
+ }
+
+ @Override
+ public DataTreeSnapshot takeSnapshot() {
+ return dataTree.takeSnapshot();
+ }
+
+ @Override
+ public void setSchemaContext(final SchemaContext newSchemaContext) {
+ dataTree.setSchemaContext(newSchemaContext);
+ }
+
+ @Override
+ public void commit(final DataTreeCandidate candidate) {
+ dataTree.commit(candidate);
+ }
+
+ @Override
+ public YangInstanceIdentifier getRootPath() {
+ return dataTree.getRootPath();
+ }
+
+ @Override
+ public void validate(final DataTreeModification modification) throws DataValidationFailedException {
+ dataTree.validate(modification);
+ }
+
+ @Override
+ public DataTreeCandidate prepare(final DataTreeModification modification) {
+ return dataTree.prepare(modification);
+ }
+ }
+}
diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModuleFactory.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModuleFactory.java
new file mode 100644
index 000000000..34a543949
--- /dev/null
+++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/InMemoryDataTreeModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: data-impl yang module local name: inmemory-data-tree
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri May 13 11:10:56 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
+public class InMemoryDataTreeModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.AbstractInMemoryDataTreeModuleFactory {
+
+}
diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java
new file mode 100644
index 000000000..1526fddca
--- /dev/null
+++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModule.java
@@ -0,0 +1,65 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.data.impl.ReadableDataTreeDelegator;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OperationalDataTreeModule extends
+ org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.AbstractOperationalDataTreeModule {
+
+ private static final Logger LOG = LoggerFactory.getLogger(OperationalDataTreeModule.class);
+
+ public OperationalDataTreeModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public OperationalDataTreeModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.OperationalDataTreeModule 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() {
+ LOG.debug("OperationalDataTreeModule.createInstance()");
+ return new CloseableOperationalDataTree(
+ new ReadableDataTreeDelegator(getSerializerDependency(), getSchemaServiceDependency().getGlobalContext(),
+ getReaderRegistryBuilderDependency().build(), getContextBindingBrokerDependency()));
+ }
+
+ private static final class CloseableOperationalDataTree implements ReadableDataManager, AutoCloseable {
+
+ private final ReadableDataTreeDelegator delegate;
+
+ CloseableOperationalDataTree(final ReadableDataTreeDelegator delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void close() throws Exception {
+ LOG.debug("CloseableOperationalDataTree.close()");
+ // NOP
+ }
+
+ @Override
+ public CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read(
+ @Nonnull final YangInstanceIdentifier path) {
+ LOG.trace("CloseableOperationalDataTree.read path={}", path);
+ return delegate.read(path);
+ }
+ }
+}
diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModuleFactory.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModuleFactory.java
new file mode 100644
index 000000000..221eabe5c
--- /dev/null
+++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/OperationalDataTreeModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: data-impl yang module local name: honeycomb-operational-data-tree
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Sun Apr 10 23:52:21 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
+public class OperationalDataTreeModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.AbstractOperationalDataTreeModuleFactory {
+
+}
diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModule.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModule.java
new file mode 100644
index 000000000..145fc4345
--- /dev/null
+++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModule.java
@@ -0,0 +1,38 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
+
+import java.nio.file.InvalidPathException;
+import java.nio.file.Paths;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+
+public class PersistingDataTreeAdapterModule extends
+ org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.AbstractPersistingDataTreeAdapterModule {
+ public PersistingDataTreeAdapterModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public PersistingDataTreeAdapterModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.PersistingDataTreeAdapterModule oldModule,
+ java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ try {
+ Paths.get(getPersistFilePath());
+ } catch (InvalidPathException e) {
+ throw new JmxAttributeValidationException("Invalid persist path", e, persistFilePathJmxAttribute);
+ }
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ return new io.fd.honeycomb.v3po.data.impl.PersistingDataTreeAdapter(
+ getDelegateDependency(),
+ getSchemaServiceDependency(),
+ Paths.get(getPersistFilePath()));
+ }
+
+}
diff --git a/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModuleFactory.java b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModuleFactory.java
new file mode 100644
index 000000000..0b7546c65
--- /dev/null
+++ b/infra/data-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/data/impl/rev160411/PersistingDataTreeAdapterModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: data-impl yang module local name: persisting-data-tree-adapter
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu May 12 14:07:24 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411;
+public class PersistingDataTreeAdapterModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.data.impl.rev160411.AbstractPersistingDataTreeAdapterModuleFactory {
+
+}
diff --git a/infra/data-impl/src/main/yang/data-impl.yang b/infra/data-impl/src/main/yang/data-impl.yang
new file mode 100644
index 000000000..ee485538b
--- /dev/null
+++ b/infra/data-impl/src/main/yang/data-impl.yang
@@ -0,0 +1,179 @@
+module data-impl {
+ yang-version 1;
+ namespace "urn:honeycomb:params:xml:ns:yang:data:impl";
+ prefix "tutils";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;}
+ import opendaylight-md-sal-dom { prefix dom; revision-date 2013-10-28;}
+ import data-api { prefix dapi; revision-date 2016-04-11; }
+ import translate-api { prefix tapi; revision-date 2016-04-06; }
+
+ description
+ "This module contains YANG module definitions
+ for honeycomd data layer";
+
+ revision "2016-04-11" {
+ description
+ "Initial revision.";
+ }
+
+ identity inmemory-data-tree {
+ base config:module-type;
+ config:provided-service dapi:data-tree;
+ config:java-name-prefix InMemoryDataTree;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case inmemory-data-tree {
+ when "/config:modules/config:module/config:type = 'inmemory-data-tree'";
+
+ container schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:schema-service;
+ }
+ }
+ }
+
+ leaf type {
+ type dapi:datatree-type;
+ mandatory true;
+ }
+ }
+ }
+
+ identity persisting-data-tree-adapter {
+ base config:module-type;
+ config:provided-service dapi:data-tree;
+ config:java-name-prefix PersistingDataTreeAdapter;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case persisting-data-tree-adapter {
+ when "/config:modules/config:module/config:type = 'persisting-data-tree-adapter'";
+
+ container delegate {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dapi:data-tree;
+ }
+ }
+ }
+
+ container schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:schema-service;
+ }
+ }
+ }
+
+ leaf persist-file-path {
+ type string;
+ mandatory true;
+ description "Path to a file to be used as data storage";
+ }
+ }
+ }
+
+ identity honeycomb-config-data-tree {
+ base config:module-type;
+ config:provided-service dapi:honeycomb-modifiable-data-tree;
+ config:java-name-prefix ConfigDataTree;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case honeycomb-config-data-tree {
+ when "/config:modules/config:module/config:type = 'honeycomb-config-data-tree'";
+
+ container data-tree {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dapi:data-tree;
+ }
+ }
+ }
+
+ container serializer {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-normalized-node-serializer;
+ }
+ }
+ }
+
+ container writer-registry-builder {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity tapi:honeycomb-writer-registry-builder;
+ }
+ }
+ }
+
+ container context-binding-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-async-data-broker;
+ }
+ }
+ }
+ }
+ }
+
+ identity honeycomb-operational-data-tree {
+ base config:module-type;
+ config:provided-service dapi:honeycomb-readable-data-tree;
+ config:java-name-prefix OperationalDataTree;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case honeycomb-operational-data-tree {
+ when "/config:modules/config:module/config:type = 'honeycomb-operational-data-tree'";
+
+ container schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:schema-service;
+ }
+ }
+ }
+
+ container serializer {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-normalized-node-serializer;
+ }
+ }
+ }
+
+ container reader-registry-builder {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity tapi:honeycomb-reader-registry-builder;
+ }
+ }
+ }
+
+ container context-binding-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-async-data-broker;
+ }
+ }
+ }
+
+ }
+ }
+} \ No newline at end of file
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java
new file mode 100644
index 000000000..55b92b50b
--- /dev/null
+++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/DataBrokerTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.data.ModifiableDataManager;
+import io.fd.honeycomb.v3po.data.DataModification;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+public class DataBrokerTest {
+
+ @Mock
+ private ReadableDataManager operationalData;
+ @Mock
+ private ModifiableDataManager confiDataTree;
+ @Mock
+ private DataModification configSnapshot;
+ private DataBroker broker;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ when(confiDataTree.newModification()).thenReturn(configSnapshot);
+ broker = DataBroker.create(confiDataTree, operationalData);
+ }
+
+ @Test
+ public void testNewReadWriteTransaction() {
+ final DOMDataReadWriteTransaction readWriteTx = broker.newReadWriteTransaction();
+ final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class);
+ readWriteTx.read(LogicalDatastoreType.CONFIGURATION, path);
+
+ // verify that read and write transactions use the same config snapshot
+ verify(configSnapshot).read(path);
+ verify(confiDataTree).newModification();
+ }
+
+ @Test
+ public void testNewWriteOnlyTransaction() {
+ final DOMDataWriteTransaction writeTx = broker.newWriteOnlyTransaction();
+
+ // verify that write transactions use config snapshot
+ verify(confiDataTree).newModification();
+ }
+
+ @Test
+ public void testNewReadOnlyTransaction() {
+ final DOMDataReadOnlyTransaction readTx = broker.newReadOnlyTransaction();
+
+ final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class);
+ readTx.read(LogicalDatastoreType.CONFIGURATION, path);
+
+ // verify that read transactions use config snapshot
+ verify(configSnapshot).read(path);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testRegisterDataChangeListener() {
+ final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class);
+ final DOMDataChangeListener listener = mock(DOMDataChangeListener.class);
+ broker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, path, listener,
+ AsyncDataBroker.DataChangeScope.BASE);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testCreateTransactionChain() {
+ final TransactionChainListener listener = mock(TransactionChainListener.class);
+ broker.createTransactionChain(listener);
+ }
+
+ @Test
+ public void testGetSupportedExtensions() {
+ final Map<Class<? extends DOMDataBrokerExtension>, DOMDataBrokerExtension> supportedExtensions =
+ broker.getSupportedExtensions();
+ assertTrue(supportedExtensions.isEmpty());
+ }
+
+
+} \ No newline at end of file
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java
new file mode 100644
index 000000000..915d738e9
--- /dev/null
+++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModifiableDataTreeDelegatorTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+
+public class ModifiableDataTreeDelegatorTest {
+
+ @Mock
+ private WriterRegistry writer;
+ @Mock
+ private BindingNormalizedNodeSerializer serializer;
+ private DataTree dataTree;
+ @Mock
+ private org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification modification;
+ @Mock
+ private DataBroker contextBroker;
+ @Mock
+ private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction tx;
+
+ private ModifiableDataTreeManager configDataTree;
+
+ static final InstanceIdentifier<?> DEFAULT_ID = InstanceIdentifier.create(DataObject.class);
+ static DataObject DEFAULT_DATA_OBJECT = mockDataObject("serialized", DataObject.class);
+
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ dataTree = ModificationDiffTest.getDataTree();
+ when(contextBroker.newReadWriteTransaction()).thenReturn(tx);
+ when(tx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+
+ when(serializer.fromYangInstanceIdentifier(any(YangInstanceIdentifier.class))).thenReturn(((InstanceIdentifier) DEFAULT_ID));
+ final Map.Entry<InstanceIdentifier<?>, DataObject> parsed = new AbstractMap.SimpleEntry<>(DEFAULT_ID, DEFAULT_DATA_OBJECT);
+ when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class))).thenReturn(parsed);
+
+ configDataTree = new ModifiableDataTreeDelegator(serializer, dataTree, writer, contextBroker);
+ }
+
+ @Test
+ public void testRead() throws Exception {
+ final ContainerNode topContainer = ModificationDiffTest.getTopContainer("topContainer");
+ ModificationDiffTest.addNodeToTree(dataTree, topContainer, ModificationDiffTest.TOP_CONTAINER_ID);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read =
+ configDataTree.read(ModificationDiffTest.TOP_CONTAINER_ID);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read2 =
+ configDataTree.newModification().read(ModificationDiffTest.TOP_CONTAINER_ID);
+ final Optional<NormalizedNode<?, ?>> normalizedNodeOptional = read.get();
+ final Optional<NormalizedNode<?, ?>> normalizedNodeOptional2 = read2.get();
+
+ assertEquals(normalizedNodeOptional, normalizedNodeOptional2);
+ assertTrue(normalizedNodeOptional.isPresent());
+ assertEquals(topContainer, normalizedNodeOptional.get());
+ assertEquals(dataTree.takeSnapshot().readNode(ModificationDiffTest.TOP_CONTAINER_ID), normalizedNodeOptional);
+ }
+
+ @Test
+ public void testCommitSuccessful() throws Exception {
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
+
+ final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
+ dataModification.commit();
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> map = HashMultimap.create();
+ map.put(DEFAULT_ID, DataObjectUpdate.create(DEFAULT_ID, DEFAULT_DATA_OBJECT, DEFAULT_DATA_OBJECT));
+ verify(writer).update(eq(new WriterRegistry.DataObjectUpdates(map, ImmutableMultimap.of())), any(WriteContext.class));
+ assertEquals(nestedList, dataTree.takeSnapshot().readNode(ModificationDiffTest.NESTED_LIST_ID).get());
+ }
+
+ private static DataObject mockDataObject(final String name, final Class<? extends DataObject> classToMock) {
+ final DataObject dataBefore = mock(classToMock, name);
+ doReturn(classToMock).when(dataBefore).getImplementedInterface();
+ return dataBefore;
+ }
+
+ @Test
+ public void testCommitUndoSuccessful() throws Exception {
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
+
+ // Fail on update:
+ final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
+ final TranslationException failedOnUpdateException = new TranslationException("update failed");
+ doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException))
+ .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
+
+ try {
+ // Run the test
+ final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
+ dataModification.commit();
+ fail("WriterRegistry.BulkUpdateException was expected");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
+ assertThat(e.getFailedIds(), hasItem(DEFAULT_ID));
+ verify(reverter).revert();
+ assertEquals(failedOnUpdateException, e.getCause());
+ }
+ }
+
+ @Test
+ public void testCommitUndoFailed() throws Exception {
+ final MapNode nestedList = ModificationDiffTest.getNestedList("listEntry", "listValue");
+
+ // Fail on update:
+ final WriterRegistry.Reverter reverter = mock(WriterRegistry.Reverter.class);
+ final TranslationException failedOnUpdateException = new TranslationException("update failed");
+ doThrow(new WriterRegistry.BulkUpdateException(Collections.singleton(DEFAULT_ID), reverter, failedOnUpdateException))
+ .when(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
+
+ // Fail on revert:
+ final TranslationException failedOnRevertException = new TranslationException("revert failed");
+ doThrow(new WriterRegistry.Reverter.RevertFailedException(Collections.emptySet(), failedOnRevertException))
+ .when(reverter).revert();
+
+ try {
+ // Run the test
+ final DataModification dataModification = configDataTree.newModification();
+ dataModification.write(ModificationDiffTest.NESTED_LIST_ID, nestedList);
+ dataModification.validate();
+ dataModification.commit();
+ fail("WriterRegistry.Reverter.RevertFailedException was expected");
+ } catch (WriterRegistry.Reverter.RevertFailedException e) {
+ verify(writer).update(any(WriterRegistry.DataObjectUpdates.class), any(WriteContext.class));
+ verify(reverter).revert();
+ assertEquals(failedOnRevertException, e.getCause());
+ }
+ }
+
+ private abstract static class DataObject1 implements DataObject {}
+ private abstract static class DataObject2 implements DataObject {}
+ private abstract static class DataObject3 implements DataObject {}
+
+ @Test
+ public void testToBindingAware() throws Exception {
+ when(serializer.fromNormalizedNode(any(YangInstanceIdentifier.class), eq(null))).thenReturn(null);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> biNodes = new HashMap<>();
+ // delete
+ final QName nn1 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid1 = mockYid(nn1);
+ final InstanceIdentifier iid1 = mockIid(yid1, DataObject1.class);
+ final NormalizedNode nn1B = mockNormalizedNode(nn1);
+ final DataObject1 do1B = mockDataObject(yid1, iid1, nn1B, DataObject1.class);
+ biNodes.put(yid1, ModificationDiff.NormalizedNodeUpdate.create(yid1, nn1B, null));
+
+ // create
+ final QName nn2 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid2 = mockYid(nn2);
+ final InstanceIdentifier iid2 = mockIid(yid2, DataObject2.class);;
+ final NormalizedNode nn2A = mockNormalizedNode(nn2);
+ final DataObject2 do2A = mockDataObject(yid2, iid2, nn2A, DataObject2.class);
+ biNodes.put(yid2, ModificationDiff.NormalizedNodeUpdate.create(yid2, null, nn2A));
+
+ // update
+ final QName nn3 = QName.create("namespace", "nn1");
+ final YangInstanceIdentifier yid3 = mockYid(nn3);
+ final InstanceIdentifier iid3 = mockIid(yid3, DataObject3.class);
+ final NormalizedNode nn3B = mockNormalizedNode(nn3);
+ final DataObject3 do3B = mockDataObject(yid3, iid3, nn3B, DataObject3.class);
+ final NormalizedNode nn3A = mockNormalizedNode(nn3);
+ final DataObject3 do3A = mockDataObject(yid3, iid3, nn3A, DataObject3.class);;
+ biNodes.put(yid3, ModificationDiff.NormalizedNodeUpdate.create(yid3, nn3B, nn3A));
+
+ final WriterRegistry.DataObjectUpdates dataObjectUpdates =
+ ModifiableDataTreeDelegator.toBindingAware(biNodes, serializer);
+
+ assertThat(dataObjectUpdates.getDeletes().size(), is(1));
+ assertThat(dataObjectUpdates.getDeletes().keySet(), hasItem(((InstanceIdentifier<?>) iid1)));
+ assertThat(dataObjectUpdates.getDeletes().values(), hasItem(
+ ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid1, do1B, null))));
+
+ assertThat(dataObjectUpdates.getUpdates().size(), is(2));
+ assertThat(dataObjectUpdates.getUpdates().keySet(), hasItems((InstanceIdentifier<?>) iid2, (InstanceIdentifier<?>) iid3));
+ assertThat(dataObjectUpdates.getUpdates().values(), hasItems(
+ DataObjectUpdate.create(iid2, null, do2A),
+ DataObjectUpdate.create(iid3, do3B, do3A)));
+
+ assertThat(dataObjectUpdates.getTypeIntersection().size(), is(3));
+ }
+
+ private <D extends DataObject> D mockDataObject(final YangInstanceIdentifier yid1,
+ final InstanceIdentifier iid1,
+ final NormalizedNode nn1B,
+ final Class<D> type) {
+ final D do1B = mock(type);
+ when(serializer.fromNormalizedNode(yid1, nn1B)).thenReturn(new AbstractMap.SimpleEntry<>(iid1, do1B));
+ return do1B;
+ }
+
+ private NormalizedNode mockNormalizedNode(final QName nn1) {
+ final NormalizedNode nn1B = mock(NormalizedNode.class);
+ when(nn1B.getNodeType()).thenReturn(nn1);
+ return nn1B;
+ }
+
+ private InstanceIdentifier mockIid(final YangInstanceIdentifier yid1,
+ final Class<? extends DataObject> type) {
+ final InstanceIdentifier iid1 = InstanceIdentifier.create(type);
+ when(serializer.fromYangInstanceIdentifier(yid1)).thenReturn(iid1);
+ return iid1;
+ }
+
+ private YangInstanceIdentifier mockYid(final QName nn1) {
+ final YangInstanceIdentifier yid1 = mock(YangInstanceIdentifier.class);
+ when(yid1.getLastPathArgument()).thenReturn(new YangInstanceIdentifier.NodeIdentifier(nn1));
+ return yid1;
+ }
+}
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java
new file mode 100644
index 000000000..bc7582e93
--- /dev/null
+++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ModificationDiffTest.java
@@ -0,0 +1,427 @@
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+
+public class ModificationDiffTest {
+
+ static final QName TOP_CONTAINER_QNAME =
+ QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "top-container");
+ static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "string");
+ static final QName NAME_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "name");
+ static final QName TEXT_LEAF_QNAME = QName.create(TOP_CONTAINER_QNAME, "text");
+ static final QName NESTED_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "nested-list");
+ static final QName DEEP_LIST_QNAME = QName.create(TOP_CONTAINER_QNAME, "deep-list");
+
+ static final QName WITH_CHOICE_CONTAINER_QNAME =
+ QName.create("urn:opendaylight:params:xml:ns:yang:test:diff", "2015-01-05", "with-choice");
+ static final QName CHOICE_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "choice");
+ static final QName IN_CASE1_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case1");
+ static final QName IN_CASE2_LEAF_QNAME = QName.create(WITH_CHOICE_CONTAINER_QNAME, "in-case2");
+
+ static final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);
+ static final YangInstanceIdentifier NESTED_LIST_ID = TOP_CONTAINER_ID.node(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
+
+
+ @Test
+ public void testInitialWrite() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+ final DataTreeModification dataTreeModification = getModification(dataTree);
+ final NormalizedNode<?, ?> topContainer = getTopContainer("string1");
+ final YangInstanceIdentifier TOP_CONTAINER_ID = YangInstanceIdentifier.of(TOP_CONTAINER_QNAME);
+ dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final ModificationDiff modificationDiff = getModificationDiff(prepare);
+
+ assertThat(modificationDiff.getUpdates().size(), is(1));
+ assertThat(modificationDiff.getUpdates().values().size(), is(1));
+ assertUpdate(modificationDiff.getUpdates().values().iterator().next(), TOP_CONTAINER_ID, null, topContainer);
+ }
+
+ @Test
+ public void testInitialWriteForContainerWithChoice() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+ final DataTreeModification dataTreeModification = getModification(dataTree);
+ final ContainerNode containerWithChoice = Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(WITH_CHOICE_CONTAINER_QNAME))
+ .withChild(Builders.choiceBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_QNAME))
+ .withChild(ImmutableNodes.leafNode(IN_CASE1_LEAF_QNAME, "withinCase1"))
+ .build())
+ .build();
+ final YangInstanceIdentifier WITH_CHOICE_CONTAINER_ID = YangInstanceIdentifier.of(WITH_CHOICE_CONTAINER_QNAME);
+ dataTreeModification.write(WITH_CHOICE_CONTAINER_ID, containerWithChoice);
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class),
+ WITH_CHOICE_CONTAINER_ID, null, containerWithChoice);
+ }
+
+ private DataTreeModification getModification(final TipProducingDataTree dataTree) {
+ final DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
+ return dataTreeSnapshot.newModification();
+ }
+
+ @Test
+ public void testWriteNonPresenceEmptyContainer() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+ final DataTreeModification dataTreeModification = getModification(dataTree);
+ final NormalizedNode<?, ?> topContainer = ImmutableNodes.containerNode(TOP_CONTAINER_QNAME);
+ dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final ModificationDiff modificationDiff = getModificationDiff(prepare);
+
+ assertThat(modificationDiff.getUpdates().size(), is(0));
+ }
+
+ private DataTreeCandidateTip prepareModification(final TipProducingDataTree dataTree,
+ final DataTreeModification dataTreeModification)
+ throws DataValidationFailedException {
+ dataTreeModification.ready();
+ dataTree.validate(dataTreeModification);
+ return dataTree.prepare(dataTreeModification);
+ }
+
+ @Test
+ public void testUpdateWrite() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
+
+ final DataTreeModification dataTreeModification = getModification(dataTree);
+ final NormalizedNode<?, ?> topContainerAfter = getTopContainer("string2");
+ dataTreeModification.write(TOP_CONTAINER_ID, topContainerAfter);
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
+ }
+
+ private ModificationDiff getModificationDiff(final DataTreeCandidateTip prepare) {
+ return ModificationDiff.recursivelyFromCandidate(YangInstanceIdentifier.EMPTY, prepare.getRootNode());
+ }
+
+ @Test
+ public void testUpdateMerge() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
+
+ final DataTreeModification dataTreeModification = getModification(dataTree);
+ final NormalizedNode<?, ?> topContainerAfter = getTopContainer("string2");
+ dataTreeModification.merge(TOP_CONTAINER_ID, topContainerAfter);
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, topContainerAfter);
+ }
+
+ @Test
+ public void testUpdateDelete() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+ final ContainerNode topContainer = getTopContainer("string1");
+ addNodeToTree(dataTree, topContainer, TOP_CONTAINER_ID);
+
+ final DataTreeModification dataTreeModification = getModification(dataTree);
+ dataTreeModification.delete(TOP_CONTAINER_ID);
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertThat(updates.values().size(), is(1));
+ assertUpdate(updates.values().iterator().next(), TOP_CONTAINER_ID, topContainer, null);
+ }
+
+ @Test
+ public void testWriteAndUpdateInnerList() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+
+ DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
+ DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
+ final YangInstanceIdentifier listId =
+ YangInstanceIdentifier.create(
+ new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
+ new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
+
+ final MapNode mapNode = getNestedList("name1", "text");
+ final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
+ dataTreeModification.write(listId, mapNode);
+ dataTreeModification.ready();
+ dataTree.validate(dataTreeModification);
+ DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
+
+ Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class),
+ listEntryId, null, mapNode.getValue().iterator().next());
+
+ // Commit so that update can be tested next
+ dataTree.commit(prepare);
+
+ YangInstanceIdentifier listItemId = listId.node(
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"));
+ MapEntryNode mapEntryNode =
+ getNestedList("name1", "text-update").getValue().iterator().next();
+
+ dataTreeSnapshot = dataTree.takeSnapshot();
+ dataTreeModification = dataTreeSnapshot.newModification();
+ dataTreeModification.write(listItemId, mapEntryNode);
+ dataTreeModification.ready();
+ dataTree.validate(dataTreeModification);
+ prepare = dataTree.prepare(dataTreeModification);
+
+ updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1 /*Actual list entry*/));
+ }
+//
+ private void assertUpdate(final ModificationDiff.NormalizedNodeUpdate update,
+ final YangInstanceIdentifier idExpected,
+ final NormalizedNode<?, ?> beforeExpected,
+ final NormalizedNode<?, ?> afterExpected) {
+ assertThat(update.getId(), is(idExpected));
+ assertThat(update.getDataBefore(), is(beforeExpected));
+ assertThat(update.getDataAfter(), is(afterExpected));
+ }
+
+ @Test
+ public void testWriteTopContainerAndInnerList() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+
+ DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
+ DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
+
+ final ContainerNode topContainer = getTopContainer("string1");
+ dataTreeModification.write(TOP_CONTAINER_ID, topContainer);
+
+ final YangInstanceIdentifier listId =
+ YangInstanceIdentifier.create(
+ new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
+ new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
+
+ final MapNode mapNode = getNestedList("name1", "text");
+ final YangInstanceIdentifier listEntryId = listId.node(mapNode.getValue().iterator().next().getIdentifier());
+
+ dataTreeModification.write(listId, mapNode);
+
+ final DataTreeCandidateTip prepare = prepareModification(dataTree, dataTreeModification);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+
+ assertThat(updates.size(), is(2));
+ assertThat(updates.values().size(), is(2));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, ContainerNode.class), TOP_CONTAINER_ID, null,
+ Builders.containerBuilder(topContainer).withChild(mapNode).build());
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), listEntryId, null, mapNode.getValue().iterator().next());
+ // Assert that keys of the updates map are not wildcarded YID
+ assertThat(updates.keySet(), hasItems(
+ TOP_CONTAINER_ID,
+ listEntryId));
+ }
+
+ private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForAfterType(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates,
+ final Class<? extends NormalizedNode<?, ?>> containerNodeClass) {
+ return updates.values().stream()
+ .filter(update -> containerNodeClass.isAssignableFrom(update.getDataAfter().getClass()))
+ .findFirst().get();
+ }
+
+ private ModificationDiff.NormalizedNodeUpdate getNormalizedNodeUpdateForBeforeType(
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates,
+ final Class<? extends NormalizedNode<?, ?>> containerNodeClass) {
+ return updates.values().stream()
+ .filter(update -> containerNodeClass.isAssignableFrom(update.getDataBefore().getClass()))
+ .findFirst().get();
+ }
+
+ @Test
+ public void testWriteDeepList() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+
+ DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
+ DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
+
+ YangInstanceIdentifier listId =
+ YangInstanceIdentifier.create(
+ new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
+ new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
+
+ MapNode mapNode = getNestedList("name1", "text");
+ dataTreeModification.write(listId, mapNode);
+
+ dataTreeModification.ready();
+ dataTree.validate(dataTreeModification);
+ DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
+ dataTree.commit(prepare);
+
+ dataTreeSnapshot = dataTree.takeSnapshot();
+ dataTreeModification = dataTreeSnapshot.newModification();
+
+ final YangInstanceIdentifier.NodeIdentifierWithPredicates nestedListNodeId =
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1");
+ listId = YangInstanceIdentifier.create(
+ new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
+ new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME),
+ nestedListNodeId);
+ final YangInstanceIdentifier deepListId =
+ listId.node(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME));
+ final YangInstanceIdentifier deepListEntryId = deepListId.node(
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME, NAME_LEAF_QNAME, "name1"));
+
+ final MapEntryNode deepListEntry = getDeepList("name1").getValue().iterator().next();
+ // Merge parent list, just to see no modifications on it
+ dataTreeModification.merge(
+ listId,
+ Builders.mapEntryBuilder().withNodeIdentifier(nestedListNodeId)
+ .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, "name1")).build());
+ dataTreeModification.merge(
+ deepListId,
+ Builders.mapBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
+ .build());
+ dataTreeModification.merge(
+ deepListEntryId,
+ deepListEntry);
+
+ dataTreeModification.ready();
+ dataTree.validate(dataTreeModification);
+ prepare = dataTree.prepare(dataTreeModification);
+ dataTree.commit(prepare);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForAfterType(updates, MapEntryNode.class), deepListEntryId, null, deepListEntry);
+ }
+
+ @Test
+ public void testDeleteInnerListItem() throws Exception {
+ final TipProducingDataTree dataTree = getDataTree();
+
+ DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
+ DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
+ final YangInstanceIdentifier listId =
+ YangInstanceIdentifier.create(
+ new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME),
+ new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME));
+
+ final MapNode mapNode = getNestedList("name1", "text");
+ dataTreeModification.write(listId, mapNode);
+ dataTreeModification.ready();
+ dataTree.validate(dataTreeModification);
+ DataTreeCandidateTip prepare = dataTree.prepare(dataTreeModification);
+
+ // Commit so that update can be tested next
+ dataTree.commit(prepare);
+
+ YangInstanceIdentifier listItemId = listId.node(
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME, NAME_LEAF_QNAME, "name1"));
+
+ dataTreeSnapshot = dataTree.takeSnapshot();
+ dataTreeModification = dataTreeSnapshot.newModification();
+ dataTreeModification.delete(listItemId);
+ dataTreeModification.ready();
+ dataTree.validate(dataTreeModification);
+ prepare = dataTree.prepare(dataTreeModification);
+
+ final Map<YangInstanceIdentifier, ModificationDiff.NormalizedNodeUpdate> updates = getModificationDiff(prepare).getUpdates();
+ assertThat(updates.size(), is(1));
+ assertUpdate(getNormalizedNodeUpdateForBeforeType(updates, MapEntryNode.class), listItemId, mapNode.getValue().iterator().next(), null);
+ }
+
+ static void addNodeToTree(final DataTree dataTree, final NormalizedNode<?, ?> node,
+ final YangInstanceIdentifier id)
+ throws DataValidationFailedException {
+ DataTreeSnapshot dataTreeSnapshot = dataTree.takeSnapshot();
+ DataTreeModification dataTreeModification = dataTreeSnapshot.newModification();
+ dataTreeModification.write(id, node);
+ dataTreeModification.ready();
+ dataTree.validate(dataTreeModification);
+ DataTreeCandidate prepare = dataTree.prepare(dataTreeModification);
+ dataTree.commit(prepare);
+ }
+
+ static TipProducingDataTree getDataTree() throws ReactorException {
+ final TipProducingDataTree dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+ dataTree.setSchemaContext(getSchemaCtx());
+ return dataTree;
+ }
+
+ static ContainerNode getTopContainer(final String stringValue) {
+ return Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_QNAME))
+ .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue))
+ .build();
+ }
+
+ static MapNode getNestedList(final String listItemName, final String text) {
+ return Builders.mapBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME))
+ .withChild(
+ Builders.mapEntryBuilder()
+ .withNodeIdentifier(
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(NESTED_LIST_QNAME,
+ NAME_LEAF_QNAME, listItemName))
+ .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName))
+ .withChild(ImmutableNodes.leafNode(TEXT_LEAF_QNAME, text))
+ .build()
+ )
+ .build();
+ }
+
+ private MapNode getDeepList(final String listItemName) {
+ return Builders.mapBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(DEEP_LIST_QNAME))
+ .withChild(
+ Builders.mapEntryBuilder()
+ .withNodeIdentifier(
+ new YangInstanceIdentifier.NodeIdentifierWithPredicates(DEEP_LIST_QNAME,
+ NAME_LEAF_QNAME, listItemName))
+ .withChild(ImmutableNodes.leafNode(NAME_LEAF_QNAME, listItemName))
+ .build()
+ )
+ .build();
+ }
+
+ private static SchemaContext getSchemaCtx() throws ReactorException {
+ final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ buildAction.addSource(new YangStatementSourceImpl(ModificationDiffTest.class.getResourceAsStream("/test-diff.yang")));
+ return buildAction.buildEffective();
+ }
+} \ No newline at end of file
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java
new file mode 100644
index 000000000..523d9dd70
--- /dev/null
+++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/PersistingDataTreeAdapterTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+
+public class PersistingDataTreeAdapterTest {
+
+ @Mock
+ private DataTree delegatingDataTree;
+ @Mock
+ private SchemaService schemaService;
+ @Mock
+ private DataTreeSnapshot snapshot;
+
+ private Path tmpPersistFile;
+
+ private PersistingDataTreeAdapter persistingDataTreeAdapter;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ tmpPersistFile = Files.createTempFile("testing-hc-persistence", "json");
+ persistingDataTreeAdapter = new PersistingDataTreeAdapter(delegatingDataTree, schemaService, tmpPersistFile);
+ }
+
+ @Test
+ public void testNoPersistOnFailure() throws Exception {
+ doThrow(new IllegalStateException("testing errors")).when(delegatingDataTree).commit(any(DataTreeCandidate.class));
+
+ try {
+ persistingDataTreeAdapter.commit(null);
+ fail("Exception expected");
+ } catch (IllegalStateException e) {
+ verify(delegatingDataTree, times(0)).takeSnapshot();
+ verify(delegatingDataTree).commit(any(DataTreeCandidate.class));
+ }
+ }
+
+} \ No newline at end of file
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java
new file mode 100644
index 000000000..a13621725
--- /dev/null
+++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadOnlyTransactionTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.ReadableDataManager;
+import io.fd.honeycomb.v3po.data.DataModification;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ReadOnlyTransactionTest {
+
+ @Mock
+ private ReadableDataManager operationalData;
+ @Mock
+ private DataModification configSnapshot;
+
+ private ReadOnlyTransaction readOnlyTx;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ readOnlyTx = ReadOnlyTransaction.create(configSnapshot, operationalData);
+ }
+
+ @Test
+ public void testExists() {
+ final YangInstanceIdentifier path = mock(YangInstanceIdentifier.class);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException>
+ future = mock(CheckedFuture.class);
+ when(operationalData.read(path)).thenReturn(future);
+
+ readOnlyTx.exists(LogicalDatastoreType.OPERATIONAL, path);
+
+ verify(operationalData).read(path);
+ }
+
+ @Test
+ public void testGetIdentifier() {
+ assertNotNull(readOnlyTx.getIdentifier());
+ }
+} \ No newline at end of file
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java
new file mode 100644
index 000000000..1b67cd967
--- /dev/null
+++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadWriteTransactionTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class ReadWriteTransactionTest {
+
+ @Mock
+ private DOMDataReadOnlyTransaction readTx;
+
+ @Mock
+ private DOMDataWriteTransaction writeTx;
+
+ private LogicalDatastoreType store;
+
+ @Mock
+ private YangInstanceIdentifier path;
+
+ @Mock
+ private NormalizedNode<?, ?> data;
+
+ private ReadWriteTransaction readWriteTx;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ store = LogicalDatastoreType.CONFIGURATION;
+ readWriteTx = new ReadWriteTransaction(readTx, writeTx);
+ }
+
+ @Test
+ public void testCancel() {
+ readWriteTx.cancel();
+ verify(writeTx).cancel();
+ }
+
+ @Test
+ public void testPut() {
+ readWriteTx.put(store, path, data);
+ verify(writeTx).put(store, path, data);
+ }
+
+ @Test
+ public void testMerge() {
+ readWriteTx.merge(store, path, data);
+ verify(writeTx).merge(store, path, data);
+ }
+
+ @Test
+ public void testDelete() {
+ readWriteTx.delete(store, path);
+ verify(writeTx).delete(store, path);
+ }
+
+ @Test
+ public void testSubmit() throws Exception {
+ readWriteTx.submit();
+ verify(writeTx).submit();
+ }
+
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testCommit() throws Exception {
+ readWriteTx.commit();
+ verify(writeTx).commit();
+ }
+
+ @Test
+ public void testRead() {
+ readWriteTx.read(store, path);
+ verify(readTx).read(store, path);
+ }
+
+ @Test
+ public void testExists() {
+ readWriteTx.exists(store, path);
+ verify(readTx).exists(store, path);
+ }
+
+ @Test
+ public void testGetIdentifier() throws Exception {
+ assertNotNull(readWriteTx.getIdentifier());
+ }
+} \ No newline at end of file
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java
new file mode 100644
index 000000000..455050ab0
--- /dev/null
+++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/ReadableDataTreeDelegatorTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import java.util.Collections;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class ReadableDataTreeDelegatorTest {
+
+ @Mock
+ private BindingNormalizedNodeSerializer serializer;
+ @Mock
+ private ReaderRegistry reader;
+
+ private ReadableDataTreeDelegator operationalData;
+
+ @Mock
+ private InstanceIdentifier<DataObject> id;
+ @Mock
+ private Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> entry;
+ @Mock
+ private SchemaContext globalContext;
+ @Mock
+ private DataSchemaNode schemaNode;
+ @Mock
+ private ReadContext readCtx;
+ @Mock
+ private DOMDataBroker netconfMonitoringBroker;
+ @Mock
+ private DOMDataReadOnlyTransaction domDataReadOnlyTransaction;
+ @Mock
+ private DataBroker contextBroker;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ operationalData = new ReadableDataTreeDelegator(serializer, globalContext, reader, contextBroker);
+ doReturn(schemaNode).when(globalContext).getDataChildByName(any(QName.class));
+
+ doReturn(domDataReadOnlyTransaction).when(netconfMonitoringBroker).newReadOnlyTransaction();
+ doReturn(Futures.immediateCheckedFuture(Optional.absent())).when(domDataReadOnlyTransaction)
+ .read(any(LogicalDatastoreType.class), any(YangInstanceIdentifier.class));
+
+ final org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTransaction = mock(
+ org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction.class);
+ doReturn(ctxTransaction).when(contextBroker).newReadWriteTransaction();
+ doReturn(Futures.immediateCheckedFuture(null)).when(ctxTransaction).submit();
+ }
+
+ @Test
+ public void testReadNode() throws Exception {
+ final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
+ final YangInstanceIdentifier.PathArgument pArg = mock(YangInstanceIdentifier.PathArgument.class);
+ doReturn(pArg).when(yangId).getLastPathArgument();
+ doReturn(Collections.singletonList(pArg)).when(yangId).getPathArguments();
+
+ doReturn(QName.create("namespace", "2012-12-12", "local")).when(pArg).getNodeType();
+ doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
+
+ final DataObject dataObject = mock(DataObject.class);
+ doReturn(Optional.of(dataObject)).when(reader).read(same(id), any(ReadContext.class));
+
+ when(serializer.toNormalizedNode(id, dataObject)).thenReturn(entry);
+ final DataContainerChild<?, ?> expectedValue = mock(DataContainerChild.class);
+ doReturn(expectedValue).when(entry).getValue();
+
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = operationalData.read(yangId);
+
+ verify(serializer).fromYangInstanceIdentifier(yangId);
+ verify(reader).read(same(id), any(ReadContext.class));
+ final Optional<NormalizedNode<?, ?>> result = future.get();
+ assertTrue(result.isPresent());
+ assertEquals(expectedValue, result.get());
+ }
+
+ @Test
+ public void testReadNonExistingNode() throws Exception {
+ final YangInstanceIdentifier yangId = mock(YangInstanceIdentifier.class);
+ doReturn(id).when(serializer).fromYangInstanceIdentifier(yangId);
+ doReturn(Optional.absent()).when(reader).read(same(id), any(ReadContext.class));
+
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future = operationalData.read(yangId);
+
+ verify(serializer).fromYangInstanceIdentifier(yangId);
+ verify(reader).read(same(id), any(ReadContext.class));
+ final Optional<NormalizedNode<?, ?>> result = future.get();
+ assertFalse(result.isPresent());
+ }
+
+ @Test
+ public void testReadFailed() throws Exception {
+ doThrow(io.fd.honeycomb.v3po.translate.read.ReadFailedException.class).when(reader).readAll(any(ReadContext.class));
+
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+ operationalData.read( YangInstanceIdentifier.EMPTY);
+
+ try {
+ future.checkedGet();
+ } catch (ReadFailedException e) {
+ assertTrue(e.getCause() instanceof io.fd.honeycomb.v3po.translate.read.ReadFailedException);
+ return;
+ }
+ fail("ReadFailedException was expected");
+ }
+
+ @Test
+ public void testReadRootWithOneNonListElement() throws Exception {
+ // Prepare data
+ final InstanceIdentifier<DataObject> vppStateII = InstanceIdentifier.create(DataObject.class);
+ final DataObject vppState = mock(DataObject.class);
+ Multimap<InstanceIdentifier<?>, DataObject> dataObjects = LinkedListMultimap.create();
+ dataObjects.put(vppStateII, vppState);
+ doReturn(dataObjects).when(reader).readAll(any(ReadContext.class));
+
+ // Init serializer
+ final YangInstanceIdentifier vppYangId = YangInstanceIdentifier.builder().node(QName.create("n", "d")).build();
+ when(serializer.toYangInstanceIdentifier(vppStateII)).thenReturn(vppYangId);
+ when(serializer.toNormalizedNode(vppStateII, vppState)).thenReturn(entry);
+ final DataContainerChild<?, ?> vppStateContainer = mock(DataContainerChild.class);
+ doReturn(vppStateContainer).when(entry).getValue();
+ doReturn(vppYangId.getLastPathArgument()).when(vppStateContainer).getIdentifier();
+
+ // Read root
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> future =
+ operationalData.read(YangInstanceIdentifier.EMPTY);
+
+ verify(reader).readAll(any(ReadContext.class));
+ verify(serializer).toYangInstanceIdentifier(vppStateII);
+ verify(serializer).toNormalizedNode(vppStateII, vppState);
+
+ // Check the result is an ContainerNode with only one child
+ final Optional<NormalizedNode<?, ?>> result = future.get();
+ assertTrue(result.isPresent());
+
+ final ContainerNode rootNode = (ContainerNode) result.get();
+ assertEquals(SchemaContext.NAME, rootNode.getIdentifier().getNodeType());
+ assertEquals(vppStateContainer, Iterables.getOnlyElement(rootNode.getValue()));
+ }
+} \ No newline at end of file
diff --git a/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java
new file mode 100644
index 000000000..9cde27d2b
--- /dev/null
+++ b/infra/data-impl/src/test/java/io/fd/honeycomb/v3po/data/impl/WriteTransactionTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.data.DataModification;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+
+public class WriteTransactionTest {
+
+ @Mock
+ private DataModification configSnapshot;
+ @Mock
+ private YangInstanceIdentifier path;
+ @Mock
+ private NormalizedNode<?,?> data;
+
+ private WriteTransaction writeTx;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ writeTx = WriteTransaction.createConfigOnly(configSnapshot);
+ }
+
+ @Test
+ public void testPut() {
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
+ verify(configSnapshot).write(path, data);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPutOperational() {
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data);
+ verify(configSnapshot).write(path, data);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testOnFinishedTx() {
+ writeTx.submit();
+ writeTx.put(LogicalDatastoreType.CONFIGURATION, path, data);
+ verify(configSnapshot).write(path, data);
+ }
+
+ @Test
+ public void testMerge() {
+ writeTx.merge(LogicalDatastoreType.CONFIGURATION, path, data);
+ verify(configSnapshot).merge(path, data);
+ }
+
+ @Test
+ public void testCancel() {
+ assertTrue(writeTx.cancel());
+ }
+
+ @Test
+ public void testCancelFinished() {
+ writeTx.submit();
+ assertFalse(writeTx.cancel());
+ }
+
+ @Test
+ public void testDelete() {
+ writeTx.delete(LogicalDatastoreType.CONFIGURATION, path);
+ verify(configSnapshot).delete(path);
+ }
+
+ @Test
+ public void testSubmit() throws Exception {
+ writeTx.submit();
+ verify(configSnapshot).validate();
+ verify(configSnapshot).commit();
+ }
+
+ @Test
+ public void testSubmitFailed() throws Exception {
+ doThrow(mock(DataValidationFailedException.class)).when(configSnapshot).commit();
+ final CheckedFuture<Void, TransactionCommitFailedException> future = writeTx.submit();
+ try {
+ future.get();
+ } catch (Exception e) {
+ assertTrue(e.getCause() instanceof TransactionCommitFailedException);
+ return;
+ }
+ fail("Expected exception to be thrown");
+
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testCommit() {
+ writeTx.commit();
+ }
+
+ @Test
+ public void testGetIdentifier() {
+ assertNotNull(writeTx.getIdentifier());
+ }
+} \ No newline at end of file
diff --git a/infra/data-impl/src/test/resources/test-diff.yang b/infra/data-impl/src/test/resources/test-diff.yang
new file mode 100644
index 000000000..5cccc8718
--- /dev/null
+++ b/infra/data-impl/src/test/resources/test-diff.yang
@@ -0,0 +1,54 @@
+module test-diff {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:test:diff";
+ prefix "td";
+
+ revision "2015-01-05" {
+ description "Initial revision";
+ }
+
+ container top-container {
+ leaf string {
+ type string;
+ }
+
+ list nested-list {
+ key "name";
+
+ leaf name {
+ type string;
+ }
+
+ leaf text {
+ type string;
+ }
+
+ list deep-list {
+ key "name";
+
+ leaf name {
+ type string;
+ }
+
+ }
+ }
+ }
+
+ container with-choice {
+
+ choice choice {
+ case case1 {
+ leaf in-case1 {
+ type string;
+ }
+ }
+
+ case case2 {
+ leaf in-case2 {
+ type string;
+ }
+ }
+ }
+ }
+
+}
diff --git a/infra/features/pom.xml b/infra/features/pom.xml
new file mode 100644
index 000000000..732c6ca8f
--- /dev/null
+++ b/infra/features/pom.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>features-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/features-parent</relativePath>
+ </parent>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>honeycomb-features</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>${project.artifactId}</name>
+ <modelVersion>4.0.0</modelVersion>
+ <prerequisites>
+ <maven>3.1.1</maven>
+ </prerequisites>
+ <properties>
+ <mdsal.model.version>0.8.2-Beryllium-SR2</mdsal.model.version>
+ <mdsal.version>1.3.2-Beryllium-SR2</mdsal.version>
+ <restconf.version>1.3.2-Beryllium-SR2</restconf.version>
+ <netconf.version>1.0.2-Beryllium-SR2</netconf.version>
+ <yangtools.version>0.8.2-Beryllium-SR2</yangtools.version>
+ <dlux.version>0.3.2-Beryllium-SR2</dlux.version>
+ <configfile.directory>etc/opendaylight/karaf</configfile.directory>
+ </properties>
+ <dependencyManagement>
+ <dependencies>
+ <!-- project specific dependencies -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>mdsal-artifacts</artifactId>
+ <version>${mdsal.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>restconf-artifacts</artifactId>
+ <version>${restconf.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>netconf-artifacts</artifactId>
+ <version>${netconf.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-yangtools</artifactId>
+ <classifier>features</classifier>
+ <version>${yangtools.version}</version>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal.model</groupId>
+ <artifactId>features-mdsal-model</artifactId>
+ <version>${mdsal.model.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>features-mdsal</artifactId>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>features-restconf</artifactId>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>features-netconf-connector</artifactId>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.dlux</groupId>
+ <artifactId>features-dlux</artifactId>
+ <classifier>features</classifier>
+ <version>${dlux.version}</version>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-impl</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-impl</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>netconf</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-impl</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>init</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-impl</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>context</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-impl</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>restconf</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-impl</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-impl</artifactId>
+ <version>${project.version}</version>
+ <type>xml</type>
+ <classifier>notification2netconf</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cfg-init</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jgrapht</groupId>
+ <artifactId>jgrapht-core</artifactId>
+ <version>0.9.2</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/infra/features/src/main/features/features.xml b/infra/features/src/main/features/features.xml
new file mode 100644
index 000000000..d0431d656
--- /dev/null
+++ b/infra/features/src/main/features/features.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<features name="odl-honeycomb-${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.yangtools/features-yangtools/${yangtools.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.controller/features-mdsal/${mdsal.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.mdsal.model/features-mdsal-model/${mdsal.model.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.netconf/features-restconf/${restconf.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.netconf/features-netconf-connector/${netconf.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.dlux/features-dlux/${dlux.version}/xml/features</repository>
+
+ <feature name='odl-honeycomb-api' version='${project.version}' description='OpenDaylight :: honeycomb :: api'>
+ <feature version='${mdsal.model.version}'>odl-mdsal-models</feature>
+ </feature>
+
+ <feature name='odl-honeycomb' version='${project.version}' description='OpenDaylight :: honeycomb'>
+ <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
+ <feature version='${project.version}'>odl-honeycomb-api</feature>
+ <!-- FIXME remove netconf-connector-ssh from features, only netconf northbound is needed -->
+ <feature version='${netconf.version}'>odl-netconf-connector-ssh</feature>
+ <feature version='${mdsal.version}'>odl-netconf-mdsal</feature>
+ <bundle>mvn:io.fd.honeycomb/honeycomb-impl/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/translate-api/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/translate-spi/{{VERSION}}</bundle>
+ <bundle>mvn:org.jgrapht/jgrapht-core/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/translate-utils/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/data-api/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/data-impl/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/notification-api/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/notification-impl/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/translate-impl/{{VERSION}}</bundle>
+ <bundle>mvn:io.fd.honeycomb/cfg-init/{{VERSION}}</bundle>
+ <configfile finalname="${configfile.directory}/honeycomb-context.xml">mvn:io.fd.honeycomb/honeycomb-impl/{{VERSION}}/xml/context</configfile>
+ <configfile finalname="${configfile.directory}/honeycomb-notification.xml">mvn:io.fd.honeycomb/notification-impl/{{VERSION}}/xml/config</configfile>
+ <configfile finalname="${configfile.directory}/honeycomb.xml">mvn:io.fd.honeycomb/honeycomb-impl/{{VERSION}}/xml/config</configfile>
+ <configfile finalname="${configfile.directory}/honeycomb-init.xml">mvn:io.fd.honeycomb/honeycomb-impl/{{VERSION}}/xml/init</configfile>
+ </feature>
+
+ <feature name='odl-honeycomb-rest' version='${project.version}' description='OpenDaylight :: honeycomb :: REST'>
+ <feature version="${project.version}">odl-honeycomb</feature>
+ <feature version="${restconf.version}">odl-restconf</feature>
+ <!-- Northbound interfaces configuration -->
+ <configfile finalname="${configfile.directory}/honeycomb-netconf.xml">mvn:io.fd.honeycomb/honeycomb-impl/{{VERSION}}/xml/netconf</configfile>
+ <configfile finalname="${configfile.directory}/honeycomb-notification2netconf.xml">mvn:io.fd.honeycomb/notification-impl/{{VERSION}}/xml/notification2netconf</configfile>
+ <configfile finalname="${configfile.directory}/honeycomb-restconf.xml">mvn:io.fd.honeycomb/honeycomb-impl/{{VERSION}}/xml/restconf</configfile>
+ </feature>
+
+ <feature name='odl-honeycomb-ui' version='${project.version}' description='OpenDaylight :: honeycomb :: UI'>
+ <feature version="${project.version}">odl-honeycomb-rest</feature>
+ <feature version="${restconf.version}">odl-mdsal-apidocs</feature>
+ <feature version="${mdsal.version}">odl-mdsal-xsql</feature>
+ <feature version="${dlux.version}">odl-dlux-yangui</feature>
+ </feature>
+
+</features>
diff --git a/infra/impl/pom.xml b/infra/impl/pom.xml
new file mode 100644
index 000000000..4a316ceaf
--- /dev/null
+++ b/infra/impl/pom.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>impl-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>honeycomb-impl</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>cfg-init</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- TODO used by NetconfMonitoringReaderModule, get it out of here-->
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>ietf-netconf-monitoring</artifactId>
+ <version>1.0.2-Beryllium-SR2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>ietf-netconf-monitoring-extension</artifactId>
+ <version>1.0.2-Beryllium-SR2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.mdsal.model</groupId>
+ <artifactId>ietf-topology</artifactId>
+ </dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${config.file}</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ <artifact>
+ <file>src/main/config/netconf-north-config.xml</file>
+ <type>xml</type>
+ <classifier>netconf</classifier>
+ </artifact>
+ <artifact>
+ <file>src/main/config/initializer-config.xml</file>
+ <type>xml</type>
+ <classifier>init</classifier>
+ </artifact>
+ <artifact>
+ <file>src/main/config/context-datatree-config.xml</file>
+ <type>xml</type>
+ <classifier>context</classifier>
+ </artifact>
+ <artifact>
+ <file>src/main/config/restconf-north-config.xml</file>
+ <type>xml</type>
+ <classifier>restconf</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/infra/impl/src/main/config/context-datatree-config.xml b/infra/impl/src/main/config/context-datatree-config.xml
new file mode 100644
index 000000000..313f0ebe6
--- /dev/null
+++ b/infra/impl/src/main/config/context-datatree-config.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2016 Cisco and/or its affiliates.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at:
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+
+<snapshot>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&amp;revision=2014-12-10</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:translate:utils?module=translate-utils&amp;revision=2016-04-06</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:vpp:data:init?module=vpp-cfg-init&amp;revision=2016-04-07</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:data:api?module=data-api&amp;revision=2016-04-11</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:data:impl?module=data-impl&amp;revision=2016-04-11</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
+ </required-capabilities>
+ <configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <!-- TODO provide information stored in context data-tree to users as part of operational data -->
+ <!-- In-memory data tree for context(special data required for YANG <-> VPP mapping) data -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:impl">prefix:inmemory-data-tree</type>
+ <name>inmemory-context-data-tree</name>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <type xmlns="urn:honeycomb:params:xml:ns:yang:data:impl">oper</type>
+ </module>
+
+ <!-- DataTree adapter with persistence for context DT -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:impl">prefix:persisting-data-tree-adapter</type>
+ <name>inmemory-persisted-context-data-tree</name>
+ <delegate>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type>
+ <name>inmemory-context-data-tree</name>
+ </delegate>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <persist-file-path>etc/opendaylight/honeycomb/context.json</persist-file-path>
+ </module>
+
+ <!-- DOM Data Broker for context data -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:v3po:impl">prefix:honeycomb-context-dom-data-broker</type>
+ <name>honeycomb-context-data-broker</name>
+ <!-- With persistence -->
+ <context-data-tree>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type>
+ <name>inmemory-persisted-context-data-tree</name>
+ </context-data-tree>
+ </module>
+
+ <!-- BA Data Broker for context data -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type>
+ <name>honeycomb-context-binding-data-broker</name>
+ <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <dom-async-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <name>honeycomb-context-data-broker</name>
+ </dom-async-broker>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <binding-mapping-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </binding-forwarded-data-broker>
+ </module>
+
+ <!-- Mapping context on top of BA context broker. Utilized by eg notification producers -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:realtime-mapping-context</type>
+ <name>realtime-mapping-context</name>
+ <context-binding-broker>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <name>honeycomb-context-binding-data-broker</name>
+ </context-binding-broker>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type>
+ <instance>
+ <name>inmemory-context-data-tree</name>
+ <provider>/modules/module[type='inmemory-data-tree'][name='inmemory-context-data-tree']
+ </provider>
+ </instance>
+ <instance>
+ <name>inmemory-persisted-context-data-tree</name>
+ <provider>/modules/module[type='persisting-data-tree-adapter'][name='inmemory-persisted-context-data-tree']
+ </provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <instance>
+ <name>honeycomb-context-data-broker</name>
+ <provider>/modules/module[type='honeycomb-context-dom-data-broker'][name='honeycomb-context-data-broker']</provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <instance>
+ <name>honeycomb-context-binding-data-broker</name>
+ <provider>/modules/module[type='binding-forwarded-data-broker'][name='honeycomb-context-binding-data-broker']</provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-mapping-context</type>
+ <instance>
+ <name>realtime-mapping-context</name>
+ <provider>/modules/module[type='realtime-mapping-context'][name='realtime-mapping-context']</provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+ </configuration>
+</snapshot>
diff --git a/infra/impl/src/main/config/default-config.xml b/infra/impl/src/main/config/default-config.xml
new file mode 100644
index 000000000..c817a5db3
--- /dev/null
+++ b/infra/impl/src/main/config/default-config.xml
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<snapshot>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&amp;revision=2014-12-10</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:translate:utils?module=translate-utils&amp;revision=2016-04-06</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:data:api?module=data-api&amp;revision=2016-04-11</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:data:impl?module=data-impl&amp;revision=2016-04-11</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
+ </required-capabilities>
+ <configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:delegating-reader-registry</type>
+ <name>read-registry</name>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:delegating-writer-registry</type>
+ <name>write-registry</name>
+ </module>
+
+ <!-- In-memory data tree for HC config data tree -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:impl">prefix:inmemory-data-tree</type>
+ <name>inmemory-config-data-tree</name>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <type xmlns="urn:honeycomb:params:xml:ns:yang:data:impl">config</type>
+ </module>
+
+ <!-- DataTree adapter with persistence for config DT -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:impl">prefix:persisting-data-tree-adapter</type>
+ <name>inmemory-persisted-config-data-tree</name>
+ <delegate>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type>
+ <name>inmemory-config-data-tree</name>
+ </delegate>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <persist-file-path>etc/opendaylight/honeycomb/config.json</persist-file-path>
+ </module>
+
+
+ <!-- HC config data tree -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:impl">prefix:honeycomb-config-data-tree</type>
+ <name>config-data-tree</name>
+ <!-- Without persistence -->
+ <!--<data-tree>-->
+ <!--<type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:impl">prefix:data-tree</type>-->
+ <!--<name>inmemory-config-data-tree</name>-->
+ <!--</data-tree>-->
+ <!-- With persistence -->
+ <data-tree>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type>
+ <name>inmemory-persisted-config-data-tree</name>
+ </data-tree>
+
+ <serializer>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </serializer>
+ <writer-registry-builder>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry-builder</type>
+ <name>write-registry-builder</name>
+ </writer-registry-builder>
+ <context-binding-broker>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <name>honeycomb-context-binding-data-broker</name>
+ </context-binding-broker>
+ </module>
+
+ <!-- HC operational data tree -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:impl">prefix:honeycomb-operational-data-tree</type>
+ <name>operational-data-tree</name>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <serializer>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </serializer>
+ <reader-registry-builder>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-reader-registry</type>
+ <name>read-registry</name>
+ </reader-registry-builder>
+ <context-binding-broker>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <name>honeycomb-context-binding-data-broker</name>
+ </context-binding-broker>
+ </module>
+
+ <!-- DOM data broker which provides transaction functionality for HC using BI format-->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:v3po:impl">prefix:honeycomb-dom-data-broker</type>
+ <name>honeycomb-dom-data-broker</name>
+ <config-data-tree>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:honeycomb-modifiable-data-tree</type>
+ <name>config-data-tree</name>
+ </config-data-tree>
+ <operational-data-tree>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:honeycomb-readable-data-tree</type>
+ <name>operational-data-tree</name>
+ </operational-data-tree>
+ </module>
+
+ <!-- Binding data broker which provides transaction functionality for HC using BA format -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type>
+ <name>honeycomb-binding-data-broker</name>
+ <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+
+ <dom-async-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <name>honeycomb-dom-data-broker</name>
+ </dom-async-broker>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <binding-mapping-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </binding-forwarded-data-broker>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:v3po:impl">prefix:v3po</type>
+ <name>v3po-default</name>
+ <dom-broker>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-broker>
+ <honeycomb-dom-data-broker>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-async-data-broker</type>
+ <name>honeycomb-dom-data-broker</name>
+ </honeycomb-dom-data-broker>
+ <honeycomb-dom-notification-service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:dom-notification-service</type>
+ <name>honeycomb-dom-notification-service</name>
+ </honeycomb-dom-notification-service>
+ </module>
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <instance>
+ <name>honeycomb-dom-data-broker</name>
+ <provider>/modules/module[type='honeycomb-dom-data-broker'][name='honeycomb-dom-data-broker']</provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <instance>
+ <name>honeycomb-binding-data-broker</name>
+ <provider>/modules/module[type='binding-forwarded-data-broker'][name='honeycomb-binding-data-broker']</provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-reader-registry</type>
+ <instance>
+ <name>read-registry</name>
+ <provider>/modules/module[type='delegating-reader-registry'][name='read-registry']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry</type>
+ <instance>
+ <name>write-registry</name>
+ <provider>/modules/module[type='delegating-writer-registry'][name='write-registry']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry-builder</type>
+ <instance>
+ <name>write-registry-builder</name>
+ <provider>/modules/module[type='delegating-writer-registry'][name='write-registry']</provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type>
+ <instance>
+ <name>inmemory-config-data-tree</name>
+ <provider>/modules/module[type='inmemory-data-tree'][name='inmemory-config-data-tree']
+ </provider>
+ </instance>
+ <instance>
+ <name>inmemory-persisted-config-data-tree</name>
+ <provider>/modules/module[type='persisting-data-tree-adapter'][name='inmemory-persisted-config-data-tree']
+ </provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:honeycomb-modifiable-data-tree</type>
+ <instance>
+ <name>config-data-tree</name>
+ <provider>/modules/module[type='honeycomb-config-data-tree'][name='config-data-tree']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:honeycomb-readable-data-tree</type>
+ <instance>
+ <name>operational-data-tree</name>
+ <provider>/modules/module[type='honeycomb-operational-data-tree'][name='operational-data-tree']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-broker-osgi-registry
+ </type>
+ <instance>
+ <name>vpp-dom-broker</name>
+ <provider>/modules/module[type='v3po'][name='v3po-default']</provider>
+ </instance>
+ </service>
+
+ </services>
+ </data>
+ </configuration>
+</snapshot>
diff --git a/infra/impl/src/main/config/initializer-config.xml b/infra/impl/src/main/config/initializer-config.xml
new file mode 100644
index 000000000..f46069340
--- /dev/null
+++ b/infra/impl/src/main/config/initializer-config.xml
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2016 Cisco and/or its affiliates.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at:
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+
+<snapshot>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&amp;revision=2014-12-10</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:translate:utils?module=translate-utils&amp;revision=2016-04-06</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:vpp:data:init?module=vpp-cfg-init&amp;revision=2016-04-07</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:data:api?module=data-api&amp;revision=2016-04-11</capability>
+ <capability>urn:honeycomb:params:xml:ns:yang:data:impl?module=data-impl&amp;revision=2016-04-11</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
+ </required-capabilities>
+ <configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <!-- Config initialization -->
+ <!-- Empty registry which does not pass data to VPP -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:noop-writer-registry-builder</type>
+ <name>noop-writer-registry-builder</name>
+ </module>
+ <!-- Config data tree which does not pass data to translation layer (uses noop-write-registry) -->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:impl">prefix:honeycomb-config-data-tree</type>
+ <name>cfg-init-config-data-tree</name>
+ <!-- Without persistence -->
+ <data-tree>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:data-tree</type>
+ <name>inmemory-config-data-tree</name>
+ </data-tree>
+ <serializer>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </serializer>
+ <writer-registry-builder>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry-builder</type>
+ <name>noop-writer-registry-builder</name>
+ </writer-registry-builder>
+ <context-binding-broker>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <name>honeycomb-context-binding-data-broker</name>
+ </context-binding-broker>
+ </module>
+ <!-- DOM data broker for config initialization -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:v3po:impl">prefix:honeycomb-dom-data-broker</type>
+ <name>cfg-init-dom-data-broker</name>
+ <config-data-tree>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:honeycomb-modifiable-data-tree</type>
+ <name>cfg-init-config-data-tree</name>
+ </config-data-tree>
+ <operational-data-tree>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:honeycomb-readable-data-tree</type>
+ <name>operational-data-tree</name>
+ </operational-data-tree>
+ </module>
+ <!-- Binding data broker for config initialization -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type>
+ <name>cfg-init-binding-data-broker</name>
+ <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <dom-async-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <name>cfg-init-dom-data-broker</name>
+ </dom-async-broker>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <binding-mapping-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </binding-forwarded-data-broker>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:data:init">prefix:persisted-file-initializer</type>
+ <name>persisted-context-initializer</name>
+ <dom-data-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <name>honeycomb-context-data-broker</name>
+ </dom-data-broker>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <persist-file-path>etc/opendaylight/honeycomb/context.json</persist-file-path>
+ <restoration-type>merge</restoration-type>
+ <datastore-type>oper</datastore-type>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:data:init">prefix:persisted-file-initializer</type>
+ <name>persisted-config-initializer</name>
+ <dom-data-broker>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">prefix:dom-async-data-broker</type>
+ <name>honeycomb-dom-data-broker</name>
+ </dom-data-broker>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <persist-file-path>etc/opendaylight/honeycomb/config.json</persist-file-path>
+ <restoration-type>merge</restoration-type>
+ <datastore-type>config</datastore-type>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:data:init">prefix:cfg-initializer-registry</type>
+ <name>initializer-registry</name>
+ <persisted-context-initializer>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:data:init">prefix:cfg-initializer</type>
+ <name>persisted-context-initializer</name>
+ </persisted-context-initializer>
+ <persisted-config-initializer>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:data:init">prefix:cfg-initializer</type>
+ <name>persisted-config-initializer</name>
+ </persisted-config-initializer>
+ </module>
+ <!-- END: Config initialization -->
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <!-- Config initialization -->
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-writer-registry-builder</type>
+ <instance>
+ <name>noop-writer-registry-builder</name>
+ <provider>/modules/module[type='noop-writer-registry-builder'][name='noop-writer-registry-builder']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:data:api">prefix:honeycomb-modifiable-data-tree</type>
+ <instance>
+ <name>cfg-init-config-data-tree</name>
+ <provider>/modules/module[type='honeycomb-config-data-tree'][name='cfg-init-config-data-tree']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <instance>
+ <name>cfg-init-dom-data-broker</name>
+ <provider>/modules/module[type='honeycomb-dom-data-broker'][name='cfg-init-dom-data-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <instance>
+ <name>cfg-init-binding-data-broker</name>
+ <provider>/modules/module[type='binding-forwarded-data-broker'][name='cfg-init-binding-data-broker']</provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:vpp:data:init">prefix:cfg-initializer</type>
+ <instance>
+ <name>persisted-context-initializer</name>
+ <provider>/modules/module[type='persisted-file-initializer'][name='persisted-context-initializer']
+ </provider>
+ </instance>
+ <instance>
+ <name>persisted-config-initializer</name>
+ <provider>/modules/module[type='persisted-file-initializer'][name='persisted-config-initializer']
+ </provider>
+ </instance>
+ </service>
+ </services>
+ </data>
+ </configuration>
+</snapshot>
diff --git a/infra/impl/src/main/config/netconf-north-config.xml b/infra/impl/src/main/config/netconf-north-config.xml
new file mode 100644
index 000000000..35bd5ff04
--- /dev/null
+++ b/infra/impl/src/main/config/netconf-north-config.xml
@@ -0,0 +1,496 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2016 Cisco and/or its affiliates.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at:
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+
+<snapshot>
+ <configuration>
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+
+ <!-- In memory DS dedicated to NETCONF monitoring, notifications etc. -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider">prefix:inmemory-config-datastore-provider</type>
+ <name>netconf-config-store-service</name>
+ <inmemory-config-datastore-provider xmlns="urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider">
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ </inmemory-config-datastore-provider>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider">prefix:inmemory-operational-datastore-provider</type>
+ <name>netconf-operational-store-service</name>
+ <inmemory-operational-datastore-provider xmlns="urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider">
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ </inmemory-operational-datastore-provider>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-inmemory-data-broker</type>
+ <name>netconf-inmemory-data-broker</name>
+
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+
+ <config-data-store>
+ <type xmlns:config-dom-store-spi="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:config-dom-store">config-dom-store-spi:config-dom-datastore</type>
+ <name>netconf-config-store-service</name>
+ </config-data-store>
+
+ <operational-data-store>
+ <type xmlns:operational-dom-store-spi="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:operational-dom-store">operational-dom-store-spi:operational-dom-datastore</type>
+ <name>netconf-operational-store-service</name>
+ </operational-data-store>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type>
+ <name>netconf-binding-data-broker</name>
+ <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <dom-async-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <name>netconf-inmemory-data-broker</name>
+ </dom-async-broker>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <binding-mapping-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </binding-forwarded-data-broker>
+ </module>
+ <module>
+ <!--binding-broker-osgi-registry-->
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:v3po:impl">prefix:binding-broker-netconf</type>
+ <name>binding-broker-netconf</name>
+ <netconf-binding-broker>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <name>netconf-binding-data-broker</name>
+ </netconf-binding-broker>
+ </module>
+ <!-- END:In memory DS dedicated to NETCONF monitoring, notifications etc. -->
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper">
+ prefix:netconf-mdsal-mapper
+ </type>
+ <name>netconf-vpp-mapper</name>
+ <root-schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service
+ </type>
+ <name>yang-schema-service</name>
+ </root-schema-service>
+ <root-schema-source-provider>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:yang-text-source-provider
+ </type>
+ <name>yang-text-source-provider</name>
+ </root-schema-source-provider>
+ <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">
+ dom:dom-broker-osgi-registry
+ </type>
+ <name>vpp-dom-broker</name>
+ </dom-broker>
+ <mapper-aggregator xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ prefix:netconf-mapper-registry
+ </type>
+ <name>vpp-mapper-aggregator-registry</name>
+ </mapper-aggregator>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ prefix:netconf-server-dispatcher-impl
+ </type>
+ <name>netconf-vpp-server-dispatcher</name>
+ <mappers xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ dom:netconf-northbound-mapper
+ </type>
+ <name>vpp-mapper-aggregator</name>
+ </mappers>
+ <server-monitor
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">
+ prefix:netconf-server-monitoring
+ </type>
+ <name>vpp-server-monitor</name>
+ </server-monitor>
+ <boss-thread-group
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">
+ prefix:netty-threadgroup
+ </type>
+ <name>global-boss-group</name>
+ </boss-thread-group>
+ <worker-thread-group
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">
+ prefix:netty-threadgroup
+ </type>
+ <name>global-worker-group</name>
+ </worker-thread-group>
+ <timer xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">prefix:netty-timer
+ </type>
+ <name>global-timer</name>
+ </timer>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring">
+ prefix:netconf-mdsal-monitoring-mapper
+ </type>
+ <name>netconf-vpp-monitoring-mapper</name>
+ <server-monitoring xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">
+ prefix:netconf-server-monitoring
+ </type>
+ <name>vpp-server-monitor</name>
+ </server-monitoring>
+ <binding-aware-broker
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-broker-osgi-registry
+ </type>
+ <name>binding-broker-netconf</name>
+ </binding-aware-broker>
+ <aggregator xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ prefix:netconf-mapper-registry
+ </type>
+ <name>vpp-mapper-aggregator-registry</name>
+ </aggregator>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ prefix:netconf-mapper-aggregator
+ </type>
+ <name>vpp-mapper-aggregator</name>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ prefix:netconf-server-monitoring-impl
+ </type>
+ <name>vpp-server-monitor</name>
+ <aggregator xmlns="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl">
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ dom:netconf-northbound-mapper
+ </type>
+ <name>vpp-mapper-aggregator</name>
+ </aggregator>
+ </module>
+
+ <!-- Change the port of global netconf north from 2830 to 2831, so that honeycomb netconf northbound can use 2830-->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">prefix:netconf-northbound-ssh</type>
+ <name>netconf-mdsal-ssh-server</name>
+ <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">2831</port>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ prefix:netconf-northbound-ssh
+ </type>
+ <name>netconf-vpp-ssh-server</name>
+
+ <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">2830</port>
+ <event-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">
+ prefix:netty-event-executor
+ </type>
+ <name>global-event-executor</name>
+ </event-executor>
+ <worker-thread-group xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netty">
+ prefix:netty-threadgroup
+ </type>
+ <name>global-worker-group</name>
+ </worker-thread-group>
+ <processing-executor xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:threadpool">
+ prefix:threadpool
+ </type>
+ <name>global-netconf-ssh-scheduled-executor</name>
+ </processing-executor>
+ <dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">
+ prefix:netconf-server-dispatcher
+ </type>
+ <name>netconf-vpp-server-dispatcher</name>
+ </dispatcher>
+ <auth-provider xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:auth">
+ prefix:netconf-auth-provider
+ </type>
+ <name>default-auth-provider</name>
+ </auth-provider>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification:impl">
+ prefix:netconf-notification-manager
+ </type>
+ <name>vpp-netconf-notification-manager</name>
+ </module>
+
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ prefix:netconf-mdsal-notification-mapper
+ </type>
+ <name>netconf-vpp-notification-mapper</name>
+ <!--This is used to listen to netconf-state/capabilities changes to send out notifications-->
+ <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ binding:binding-async-data-broker
+ </type>
+ <name>netconf-binding-data-broker</name>
+ </data-broker>
+ <!--This writes stream list into DS-->
+ <binding-aware-broker
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">
+ prefix:binding-broker-osgi-registry
+ </type>
+ <name>binding-broker-netconf</name>
+ </binding-aware-broker>
+ <aggregator xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ prefix:netconf-mapper-registry
+ </type>
+ <name>vpp-mapper-aggregator-registry</name>
+ </aggregator>
+ <notification-registry
+ xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">
+ prefix:netconf-notification-registry
+ </type>
+ <name>vpp-netconf-notification-manager</name>
+ </notification-registry>
+ <notification-collector>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">
+ prefix:netconf-notification-collector
+ </type>
+ <name>vpp-netconf-notification-manager</name>
+ </notification-collector>
+ </module>
+
+
+ <!--TCP endpoint for MD-SAL netconf server -->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:tcp">
+ prefix:netconf-northbound-tcp
+ </type>
+ <name>netconf-vpp-tcp-server</name>
+ <dispatcher xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:tcp">
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">
+ prefix:netconf-server-dispatcher
+ </type>
+ <name>netconf-vpp-server-dispatcher</name>
+ </dispatcher>
+ <port xmlns="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:tcp">7777</port>
+ </module>
+
+ <!-- Special reader for Netconf monitoring. The problem is that we store netconf monitoring in a dedicated DS.
+ However netconf is wired to our reader registry, so we need to delegate the reads of netconf-monitoring
+ through our readers to the dedicated DS-->
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:v3po:impl">prefix:netconf-monitoring-reader</type>
+ <name>netconf-monitoring-reader</name>
+ <netconf-monitoring-binding-broker>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <name>netconf-binding-data-broker</name>
+ </netconf-monitoring-binding-broker>
+ </module>
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:utils">prefix:delegating-reader-registry</type>
+ <name>read-registry</name>
+ <reader-factory>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-reader-factory</type>
+ <name>netconf-monitoring-reader</name>
+ </reader-factory>
+ </module>
+ <!-- END: Special reader for Netconf monitoring. -->
+
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:config-dom-store-spi="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:config-dom-store">config-dom-store-spi:config-dom-datastore</type>
+ <instance>
+ <name>netconf-config-store-service</name>
+ <provider>/modules/module[type='inmemory-config-datastore-provider'][name='netconf-config-store-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:operational-dom-store-spi="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:operational-dom-store">operational-dom-store-spi:operational-dom-datastore</type>
+ <instance>
+ <name>netconf-operational-store-service</name>
+ <provider>/modules/module[type='inmemory-operational-datastore-provider'][name='netconf-operational-store-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
+ <instance>
+ <name>netconf-inmemory-data-broker</name>
+ <provider>/modules/module[type='dom-inmemory-data-broker'][name='netconf-inmemory-data-broker']</provider>
+ </instance>
+ </service>
+
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:translate:api">prefix:honeycomb-reader-factory</type>
+ <instance>
+ <name>netconf-monitoring-reader</name>
+ <provider>/modules/module[type='netconf-monitoring-reader'][name='netconf-monitoring-reader']</provider>
+ </instance>
+ </service>
+
+ <!-- In memory DS dedicated to NETCONF monitoring, notifications etc. -->
+
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <instance>
+ <name>netconf-binding-data-broker</name>
+ <provider>/modules/module[type='binding-forwarded-data-broker'][name='netconf-binding-data-broker']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+ <instance>
+ <name>binding-broker-netconf</name>
+ <provider>/modules/module[type='binding-broker-netconf'][name='binding-broker-netconf']</provider>
+ </instance>
+ </service>
+ <!-- END:In memory DS dedicated to NETCONF monitoring, notifications etc. -->
+
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">
+ prefix:netconf-server-monitoring
+ </type>
+ <instance>
+ <name>vpp-server-monitor</name>
+ <provider>/modules/module[type='netconf-server-monitoring-impl'][name='vpp-server-monitor']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ prefix:netconf-northbound-mapper
+ </type>
+ <instance>
+ <name>netconf-vpp-mapper</name>
+ <provider>/modules/module[type='netconf-mdsal-mapper'][name='netconf-vpp-mapper']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ prefix:netconf-northbound-mapper
+ </type>
+ <instance>
+ <name>vpp-mapper-aggregator</name>
+ <provider>/modules/module[type='netconf-mapper-aggregator'][name='vpp-mapper-aggregator']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ prefix:netconf-mapper-registry
+ </type>
+ <instance>
+ <name>vpp-mapper-aggregator-registry</name>
+ <provider>/modules/module[type='netconf-mapper-aggregator'][name='vpp-mapper-aggregator']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound">
+ prefix:netconf-server-dispatcher
+ </type>
+ <instance>
+ <name>netconf-vpp-server-dispatcher</name>
+ <provider>
+ /modules/module[type='netconf-server-dispatcher-impl'][name='netconf-vpp-server-dispatcher']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:north:mapper">
+ prefix:netconf-northbound-mapper
+ </type>
+ <instance>
+ <name>netconf-vpp-notification-mapper</name>
+ <provider>
+ /modules/module[type='netconf-mdsal-notification-mapper'][name='netconf-vpp-notification-mapper']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">
+ prefix:netconf-notification-collector
+ </type>
+ <instance>
+ <name>vpp-netconf-notification-manager</name>
+ <provider>
+ /modules/module[type='netconf-notification-manager'][name='vpp-netconf-notification-manager']
+ </provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">
+ prefix:netconf-notification-registry
+ </type>
+ <instance>
+ <name>vpp-netconf-notification-manager</name>
+ <provider>
+ /modules/module[type='netconf-notification-manager'][name='vpp-netconf-notification-manager']
+ </provider>
+ </instance>
+ </service>
+ </services>
+
+ </data>
+ </configuration>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:v3po:impl?module=v3po-impl&amp;revision=2014-12-10</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&amp;revision=2013-10-28</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:mapper?module=netconf-mdsal-mapper&amp;revision=2015-01-14</capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:monitoring?module=netconf-mdsal-monitoring&amp;revision=2015-02-18
+ </capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:ssh?module=netconf-northbound-ssh&amp;revision=2015-01-14</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:tcp?module=netconf-northbound-tcp&amp;revision=2015-04-23</capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:config:netconf:northbound:impl?module=netconf-northbound-impl&amp;revision=2015-01-12
+ </capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled?module=threadpool-impl-scheduled&amp;revision=2013-12-01
+ </capability>
+ <capability>
+ urn:opendaylight:params:xml:ns:yang:controller:netconf:mdsal:notification?module=netconf-mdsal-notification&amp;revision=2015-08-03
+ </capability>
+ </required-capabilities>
+</snapshot>
diff --git a/infra/impl/src/main/config/restconf-north-config.xml b/infra/impl/src/main/config/restconf-north-config.xml
new file mode 100644
index 000000000..ff2cdac18
--- /dev/null
+++ b/infra/impl/src/main/config/restconf-north-config.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2016 Cisco and/or its affiliates.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at:
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<snapshot>
+ <configuration>
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:rest="urn:opendaylight:params:xml:ns:yang:controller:md:sal:rest:connector">rest:rest-connector-impl</type>
+ <name>rest-connector-default-impl</name>
+ <!-- Reconfiguring the default restconf northbound instance, to wire it just to honeycomb broker facade -->
+ <dom-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>vpp-dom-broker</name>
+ </dom-broker>
+ </module>
+ </modules>
+ </data>
+ </configuration>
+ <required-capabilities>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:sal:restconf:service?module=sal-restconf-service&amp;revision=2015-07-08</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:controller:md:sal:rest:connector?module=opendaylight-rest-connector&amp;revision=2014-07-24</capability>
+ </required-capabilities>
+</snapshot>
diff --git a/infra/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java b/infra/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java
new file mode 100644
index 000000000..c2d70c38c
--- /dev/null
+++ b/infra/impl/src/main/java/io/fd/honeycomb/v3po/impl/NorthboundFacadeHoneycombDOMBroker.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationPublishService;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcImplementationNotAvailableException;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.BrokerService;
+import org.opendaylight.controller.sal.core.api.Consumer;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Implementation of dom broker to facade VPP pipeline for northbound APIs
+ */
+public class NorthboundFacadeHoneycombDOMBroker implements AutoCloseable, Broker {
+
+ private static final BrokerService EMPTY_DOM_RPC_SERVICE = new EmptyDomRpcService();
+ private static final BrokerService EMPTY_DOM_MOUNT_SERVICE = new EmptyDomMountService();
+
+ private Map<Class<? extends BrokerService>, BrokerService> services;
+
+ public NorthboundFacadeHoneycombDOMBroker(@Nonnull final DOMDataBroker domDataBrokerDependency,
+ @Nonnull final SchemaService schemaBiService,
+ @Nonnull final DOMNotificationService domNotificatioNService) {
+ services = Maps.newHashMap();
+ services.put(DOMDataBroker.class, domDataBrokerDependency);
+ // All services below are required to be present by Restconf northbound
+ services.put(SchemaService.class, schemaBiService);
+ services.put(DOMRpcService.class, EMPTY_DOM_RPC_SERVICE);
+ services.put(DOMMountPointService.class, EMPTY_DOM_MOUNT_SERVICE);
+ services.put(DOMNotificationService.class, domNotificatioNService);
+ // TODO do both notification service types have to be registered ?
+ services.put(DOMNotificationPublishService.class, domNotificatioNService);
+ }
+
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+
+ @Override
+ public ConsumerSession registerConsumer(final Consumer consumer) {
+ final SimpleConsumerSession session = new SimpleConsumerSession(services);
+ consumer.onSessionInitiated(session);
+ return session;
+ }
+
+ @Deprecated
+ @Override
+ public ConsumerSession registerConsumer(final Consumer consumer, final BundleContext bundleContext) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ProviderSession registerProvider(final Provider provider) {
+ final SimpleProviderSession session = new SimpleProviderSession(services);
+ provider.onSessionInitiated(session);
+ return session;
+ }
+
+ @Override
+ public ProviderSession registerProvider(final Provider provider, final BundleContext bundleContext) {
+ throw new UnsupportedOperationException();
+ }
+
+ @NotThreadSafe
+ private static class SimpleConsumerSession implements ConsumerSession {
+ private boolean closed;
+ private final Map<Class<? extends BrokerService>, BrokerService> services;
+
+ private SimpleConsumerSession(final Map<Class<? extends BrokerService>, BrokerService> services) {
+ this.services = services;
+ }
+
+ @Override
+ public boolean isClosed() {
+ return closed;
+ }
+
+ @Override
+ public <T extends BrokerService> T getService(final Class<T> aClass) {
+ return (T)services.get(aClass);
+ }
+
+ @Override
+ public void close() {
+ closed = true;
+ }
+ }
+
+ @NotThreadSafe
+ private static class SimpleProviderSession implements ProviderSession {
+ private boolean closed;
+ private final Map<Class<? extends BrokerService>, BrokerService> services;
+
+ private SimpleProviderSession(final Map<Class<? extends BrokerService>, BrokerService> services) {
+ this.services = services;
+ }
+
+ @Override
+ public boolean isClosed() {
+ return closed;
+ }
+
+ @Override
+ public <T extends BrokerService> T getService(final Class<T> aClass) {
+ return (T)services.get(aClass);
+ }
+
+ @Override
+ public void close() {
+ closed = true;
+ }
+ }
+
+ private static class EmptyDomRpcService implements DOMRpcService {
+ @Nonnull
+ @Override
+ public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(@Nonnull final SchemaPath schemaPath,
+ @Nullable final NormalizedNode<?, ?> normalizedNode) {
+ return Futures.<DOMRpcResult, DOMRpcException>immediateFailedCheckedFuture(
+ new DOMRpcImplementationNotAvailableException("RPCs not supported"));
+ }
+
+ @Nonnull
+ @Override
+ public <T extends DOMRpcAvailabilityListener> ListenerRegistration<T> registerRpcListener(@Nonnull final T t) {
+ return new ListenerRegistration<T>() {
+ @Override
+ public void close() {
+ // Noop
+ }
+
+ @Override
+ public T getInstance() {
+ return t;
+ }
+ };
+ }
+ }
+
+ private static class EmptyDomMountService implements DOMMountPointService {
+ @Override
+ public Optional<DOMMountPoint> getMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) {
+ return Optional.absent();
+ }
+
+ @Override
+ public DOMMountPointBuilder createMountPoint(final YangInstanceIdentifier yangInstanceIdentifier) {
+ throw new UnsupportedOperationException("No mountpoint support");
+ }
+
+ @Override
+ public ListenerRegistration<MountProvisionListener> registerProvisionListener(
+ final MountProvisionListener mountProvisionListener) {
+ return new ListenerRegistration<MountProvisionListener>() {
+ @Override
+ public void close() {
+ // Noop
+ }
+
+ @Override
+ public MountProvisionListener getInstance() {
+ return mountProvisionListener;
+ }
+ };
+ }
+ }
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java
new file mode 100644
index 000000000..1d1fa5323
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModule.java
@@ -0,0 +1,26 @@
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210;
+
+import io.fd.honeycomb.v3po.data.impl.DataBroker;
+import io.fd.honeycomb.v3po.data.impl.ModifiableDataTreeManager;
+
+public class ContextDataBrokerModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractContextDataBrokerModule {
+
+ public ContextDataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public ContextDataBrokerModule(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.v3po.impl.rev141210.ContextDataBrokerModule 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() {
+ return DataBroker.create(new ModifiableDataTreeManager(getContextDataTreeDependency()));
+ }
+
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java
new file mode 100644
index 000000000..360f5f472
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/ContextDataBrokerModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: v3po-impl yang module local name: honeycomb-context-dom-data-broker
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Mon May 16 15:27:12 CEST 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.v3po.impl.rev141210;
+public class ContextDataBrokerModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractContextDataBrokerModuleFactory {
+
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java
new file mode 100644
index 000000000..8aa3d64d9
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModule.java
@@ -0,0 +1,34 @@
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210;
+
+import io.fd.honeycomb.v3po.data.impl.DataBroker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataBrokerModule extends
+ org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractDataBrokerModule {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DataBrokerModule.class);
+
+ public DataBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public DataBrokerModule(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.v3po.impl.rev141210.DataBrokerModule 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() {
+ LOG.debug("DataBrokerModule.createInstance()");
+ return DataBroker.create(getConfigDataTreeDependency(), getOperationalDataTreeDependency());
+ }
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModuleFactory.java
new file mode 100644
index 000000000..cc30beacf
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/DataBrokerModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: v3po-impl yang module local name: honeycomb-dom-data-broker
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Mon Apr 11 07:53:38 CEST 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.v3po.impl.rev141210;
+public class DataBrokerModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractDataBrokerModuleFactory {
+
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModule.java
new file mode 100644
index 000000000..48d227d01
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModule.java
@@ -0,0 +1,117 @@
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.binding.api.rpc.RpcContextIdentifier;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+import org.osgi.framework.BundleContext;
+
+public class NetconfBindingBrokerModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractNetconfBindingBrokerModule {
+ public NetconfBindingBrokerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfBindingBrokerModule(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.v3po.impl.rev141210.NetconfBindingBrokerModule 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() {
+ return new FakeBindingAwareBroker(getNetconfBindingBrokerDependency());
+ }
+
+ private static class FakeBindingAwareBroker implements BindingAwareBroker, AutoCloseable {
+
+ private DataBroker netconfBindingBrokerDependency;
+
+ public FakeBindingAwareBroker(final DataBroker netconfBindingBrokerDependency) {
+
+ this.netconfBindingBrokerDependency = netconfBindingBrokerDependency;
+ }
+
+ @Deprecated
+ @Override
+ public ConsumerContext registerConsumer(final BindingAwareConsumer bindingAwareConsumer,
+ final BundleContext bundleContext) {
+ throw new UnsupportedOperationException("Unsupported");
+ }
+
+ @Override
+ public ConsumerContext registerConsumer(final BindingAwareConsumer bindingAwareConsumer) {
+ final ConsumerContext consumerContext = new ConsumerContext() {
+ @Override
+ public <T extends BindingAwareService> T getSALService(final Class<T> aClass) {
+ return aClass.equals(DataBroker.class)
+ ? (T) netconfBindingBrokerDependency
+ : null;
+ }
+
+ @Override
+ public <T extends RpcService> T getRpcService(final Class<T> aClass) {
+ return null;
+ }
+ };
+ bindingAwareConsumer.onSessionInitialized(consumerContext);
+ return consumerContext;
+ }
+
+ @Override
+ public ProviderContext registerProvider(final BindingAwareProvider bindingAwareProvider,
+ final BundleContext bundleContext) {
+ throw new UnsupportedOperationException("Unsupported");
+ }
+
+ @Override
+ public ProviderContext registerProvider(final BindingAwareProvider bindingAwareProvider) {
+ final ProviderContext context = new ProviderContext() {
+ @Override
+ public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
+ final L l) {
+ throw new UnsupportedOperationException("Unsupported");
+ }
+
+ @Override
+ public <T extends RpcService> T getRpcService(final Class<T> aClass) {
+ throw new UnsupportedOperationException("Unsupported");
+ }
+
+ @Override
+ public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> aClass, final T t)
+ throws IllegalStateException {
+ throw new UnsupportedOperationException("Unsupported");
+ }
+
+ @Override
+ public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(
+ final Class<T> aClass, final T t) throws IllegalStateException {
+ throw new UnsupportedOperationException("Unsupported");
+ }
+
+ @Override
+ public <T extends BindingAwareService> T getSALService(final Class<T> aClass) {
+ return aClass.equals(DataBroker.class)
+ ? (T) netconfBindingBrokerDependency
+ : null; }
+ };
+ bindingAwareProvider.onSessionInitiated(context);
+ return context;
+ }
+
+ @Override
+ public void close() throws Exception {
+ netconfBindingBrokerDependency = null;
+ }
+ }
+}
+
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModuleFactory.java
new file mode 100644
index 000000000..b64b0b1e1
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfBindingBrokerModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: v3po-impl yang module local name: binding-broker-netconf
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Mar 23 10:45:48 CET 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.v3po.impl.rev141210;
+public class NetconfBindingBrokerModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractNetconfBindingBrokerModuleFactory {
+
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModule.java
new file mode 100644
index 000000000..8502fcbd2
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModule.java
@@ -0,0 +1,46 @@
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210;
+
+import io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.util.read.BindingBrokerReader;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfStateBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class NetconfMonitoringReaderModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractNetconfMonitoringReaderModule {
+ public NetconfMonitoringReaderModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NetconfMonitoringReaderModule(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.v3po.impl.rev141210.NetconfMonitoringReaderModule 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() {
+ return new ReaderFactory(getNetconfMonitoringBindingBrokerDependency());
+ }
+
+
+ private static final class ReaderFactory implements AutoCloseable, io.fd.honeycomb.v3po.translate.read.ReaderFactory {
+
+ private final DataBroker netconfMonitoringBindingBrokerDependency;
+
+ public ReaderFactory(final DataBroker netconfMonitoringBindingBrokerDependency) {
+ this.netconfMonitoringBindingBrokerDependency = netconfMonitoringBindingBrokerDependency;
+ }
+
+ @Override
+ public void init(final ModifiableReaderRegistryBuilder registry) {
+ registry.add(new BindingBrokerReader<>(InstanceIdentifier.create(NetconfState.class),
+ netconfMonitoringBindingBrokerDependency,
+ LogicalDatastoreType.OPERATIONAL, NetconfStateBuilder.class));
+ }
+ }
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModuleFactory.java
new file mode 100644
index 000000000..b8b7bec63
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/NetconfMonitoringReaderModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: v3po-impl yang module local name: netconf-monitoring-reader
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Tue Apr 12 13:03:40 CEST 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.v3po.impl.rev141210;
+public class NetconfMonitoringReaderModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractNetconfMonitoringReaderModuleFactory {
+
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java
new file mode 100644
index 000000000..caa792da4
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210;
+
+import io.fd.honeycomb.v3po.impl.NorthboundFacadeHoneycombDOMBroker;
+import org.opendaylight.controller.sal.core.api.AbstractProvider;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+
+public class V3poModule extends
+ org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractV3poModule {
+
+ public V3poModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public V3poModule(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.v3po.impl.rev141210.V3poModule 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() {
+
+ final Broker.ProviderSession providerSession =
+ getDomBrokerDependency().registerProvider(new AbstractProvider() {
+ @Override
+ public void onSessionInitiated(final Broker.ProviderSession providerSession) {
+ // NOOP
+ }
+ });
+ final SchemaService schemaBiService = providerSession.getService(SchemaService.class);
+
+ return new NorthboundFacadeHoneycombDOMBroker(getHoneycombDomDataBrokerDependency(), schemaBiService,
+ getHoneycombDomNotificationServiceDependency());
+ }
+
+}
diff --git a/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java
new file mode 100644
index 000000000..5c942861d
--- /dev/null
+++ b/infra/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+* Generated file
+*
+* Generated from: yang module name: v3po yang module local name: v3po
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri Jan 02 13:49:24 CST 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.v3po.impl.rev141210;
+public class V3poModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210.AbstractV3poModuleFactory {
+
+}
diff --git a/infra/impl/src/main/yang/v3po-impl.yang b/infra/impl/src/main/yang/v3po-impl.yang
new file mode 100644
index 000000000..fa9670678
--- /dev/null
+++ b/infra/impl/src/main/yang/v3po-impl.yang
@@ -0,0 +1,155 @@
+module v3po-impl {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:v3po:impl";
+ prefix "v3po-impl";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;}
+ import opendaylight-md-sal-dom {prefix dom;}
+ import translate-api { prefix tapi; revision-date 2016-04-06; }
+ import data-api { prefix dapi; revision-date 2016-04-11; }
+ import notification-api { prefix hc-notif-a; revision-date 2016-06-01; }
+
+ description
+ "Service definition for v3po project";
+
+ revision "2014-12-10" {
+ description
+ "Initial revision";
+ }
+
+ identity v3po {
+ base config:module-type;
+ config:provided-service dom:dom-broker-osgi-registry;
+ config:java-name-prefix V3po;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case v3po {
+ when "/config:modules/config:module/config:type = 'v3po'";
+ container dom-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:dom-broker-osgi-registry;
+ }
+ }
+ }
+
+ container honeycomb-dom-data-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:dom-async-data-broker;
+ }
+ }
+ }
+
+ container honeycomb-dom-notification-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity hc-notif-a:dom-notification-service;
+ }
+ }
+ }
+
+ }
+ }
+
+ identity binding-broker-netconf {
+ base config:module-type;
+ config:provided-service md-sal-binding:binding-broker-osgi-registry;
+ config:java-name-prefix NetconfBindingBroker;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case binding-broker-netconf {
+ when "/config:modules/config:module/config:type = 'binding-broker-netconf'";
+
+ container netconf-binding-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-async-data-broker;
+ }
+ }
+ }
+
+ }
+ }
+
+ identity honeycomb-dom-data-broker {
+ base config:module-type;
+ config:provided-service dom:dom-async-data-broker;
+ config:java-name-prefix DataBroker;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case honeycomb-dom-data-broker {
+ when "/config:modules/config:module/config:type = 'honeycomb-dom-data-broker'";
+
+ container config-data-tree {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dapi:honeycomb-modifiable-data-tree;
+ }
+ }
+ }
+
+ container operational-data-tree {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dapi:honeycomb-readable-data-tree;
+ }
+ }
+ }
+
+ }
+ }
+
+ identity honeycomb-context-dom-data-broker {
+ base config:module-type;
+ config:provided-service dom:dom-async-data-broker;
+ config:java-name-prefix ContextDataBroker;
+ description "DomBroker on top of context data-tree";
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case honeycomb-context-dom-data-broker {
+ when "/config:modules/config:module/config:type = 'honeycomb-context-dom-data-broker'";
+
+ container context-data-tree {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dapi:data-tree;
+ }
+ }
+ }
+ }
+ }
+
+ identity netconf-monitoring-reader {
+ base config:module-type;
+ config:provided-service tapi:honeycomb-reader-factory;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case netconf-monitoring-reader {
+ when "/config:modules/config:module/config:type = 'netconf-monitoring-reader'";
+
+ container netconf-monitoring-binding-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-async-data-broker;
+ }
+ }
+ }
+
+ }
+ }
+}
diff --git a/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java b/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java
new file mode 100644
index 000000000..5b9a67458
--- /dev/null
+++ b/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleFactoryTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210;
+
+import org.junit.Test;
+
+public class V3poModuleFactoryTest {
+ @Test
+ public void testFactoryConstructor() {
+ // ensure no exceptions on construction
+ new V3poModuleFactory();
+ }
+}
diff --git a/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java b/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java
new file mode 100644
index 000000000..3d8e79641
--- /dev/null
+++ b/infra/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/v3po/impl/rev141210/V3poModuleTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.impl.rev141210;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import javax.management.ObjectName;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.JmxAttribute;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+
+public class V3poModuleTest {
+ @Test
+ public void testCustomValidation() {
+ V3poModule module = new V3poModule(mock(ModuleIdentifier.class), mock(DependencyResolver.class));
+
+ // ensure no exceptions on validation
+ // currently this method is empty
+ module.customValidation();
+ }
+
+ @Test
+ public void testCreateInstance() throws Exception {
+ // configure mocks
+ DependencyResolver dependencyResolver = mock(DependencyResolver.class);
+ BindingAwareBroker broker = mock(BindingAwareBroker.class);
+ when(dependencyResolver.resolveInstance(eq(BindingAwareBroker.class), any(ObjectName.class), any(JmxAttribute.class)))
+ .thenReturn(broker);
+ final org.opendaylight.controller.sal.core.api.Broker domBroker = mock(org.opendaylight.controller.sal.core.api.Broker.class);
+ when(dependencyResolver.resolveInstance(eq(org.opendaylight.controller.sal.core.api.Broker.class), any(ObjectName.class), any(JmxAttribute.class)))
+ .thenReturn(domBroker);
+ doReturn(mock(Broker.ProviderSession.class)).when(domBroker).registerProvider(any(Provider.class));
+ when(dependencyResolver.resolveInstance(eq(BindingNormalizedNodeSerializer.class), any(ObjectName.class), any(JmxAttribute.class)))
+ .thenReturn(mock(BindingNormalizedNodeSerializer.class));
+ when(dependencyResolver.resolveInstance(eq(DOMDataBroker.class), any(ObjectName.class), any(JmxAttribute.class)))
+ .thenReturn(mock(DOMDataBroker.class));
+
+ // create instance of module with injected mocks
+ V3poModule module = new V3poModule(mock(ModuleIdentifier.class), dependencyResolver);
+
+ // getInstance calls resolveInstance to get the broker dependency and then calls createInstance
+ AutoCloseable closeable = module.getInstance();
+
+ // ensure no exceptions on close
+ closeable.close();
+ }
+}
diff --git a/infra/it/it-test/pom.xml b/infra/it/it-test/pom.xml
new file mode 100644
index 000000000..c7a310390
--- /dev/null
+++ b/infra/it/it-test/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <artifactId>impl-parent</artifactId>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>honeycomb-it-test</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>data-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.skinny-framework</groupId>
+ <artifactId>skinny-logback</artifactId>
+ <version>1.0.8</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-test-model</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+
+</project> \ No newline at end of file
diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java
new file mode 100644
index 000000000..47644cc70
--- /dev/null
+++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/AbstractInfraTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.Futures;
+import java.util.Collections;
+import java.util.Map;
+import javassist.ClassPool;
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.$YangModuleInfoImpl;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
+import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
+import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
+import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Base IT test infrastructure.
+ */
+abstract class AbstractInfraTest {
+
+ protected BindingNormalizedNodeSerializer serializer;
+ protected SchemaContext schemaContext;
+
+ @Mock
+ protected org.opendaylight.controller.md.sal.binding.api.DataBroker contextBroker;
+ @Mock
+ private org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction ctxTx;
+
+ static BindingToNormalizedNodeCodec getSerializer(final ModuleInfoBackedContext moduleInfoBackedContext,
+ final SchemaContext schemaContext) {
+ final DataObjectSerializerGenerator serializerGenerator = new StreamWriterGenerator(JavassistUtils.forClassPool(
+ ClassPool.getDefault()));
+ final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(serializerGenerator);
+ final BindingRuntimeContext ctx =
+ BindingRuntimeContext.create(moduleInfoBackedContext, schemaContext);
+ codecRegistry.onBindingRuntimeContextUpdated(ctx);
+ return new BindingToNormalizedNodeCodec(moduleInfoBackedContext, codecRegistry);
+ }
+
+ static ModuleInfoBackedContext getSchemaContext() {
+ final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+ moduleInfoBackedContext.addModuleInfos(Collections.singleton($YangModuleInfoImpl.getInstance()));
+ return moduleInfoBackedContext;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(contextBroker.newReadWriteTransaction()).thenReturn(ctxTx);
+ when(ctxTx.submit()).thenReturn(Futures.immediateCheckedFuture(null));
+
+ initSerializer();
+ postSetup();
+ }
+
+ abstract void postSetup();
+
+ private void initSerializer() {
+ final ModuleInfoBackedContext moduleInfoBackedContext = getSchemaContext();
+ schemaContext = moduleInfoBackedContext.tryToCreateSchemaContext().get();
+ serializer = getSerializer(moduleInfoBackedContext, schemaContext);
+ }
+
+ protected Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> toBinding(
+ final NormalizedNode<?, ?> read) {
+ Multimap<InstanceIdentifier<? extends DataObject>, DataObject> baNodes = HashMultimap.create();
+
+ for (DataContainerChild<?, ?> o : ((DataContainerNode<?>) read).getValue()) {
+ final YangInstanceIdentifier yid = YangInstanceIdentifier.of(o.getNodeType());
+ final Map.Entry<InstanceIdentifier<?>, DataObject> baEntry = serializer.fromNormalizedNode(yid, o);
+ baNodes.put(baEntry.getKey(), baEntry.getValue());
+ }
+ return baNodes;
+ }
+}
diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java
new file mode 100644
index 000000000..b1f3a196a
--- /dev/null
+++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombReadInfraTest.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.translate.impl.read.GenericListReader;
+import io.fd.honeycomb.v3po.translate.read.ListReader;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.util.read.ReflexiveListReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.util.read.ReflexiveReader;
+import io.fd.honeycomb.v3po.translate.util.read.registry.CompositeReaderRegistryBuilder;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.exceptions.misusing.MockitoConfigurationException;
+import org.mockito.invocation.InvocationOnMock;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class HoneycombReadInfraTest extends AbstractInfraTest {
+
+ @Mock
+ private ReadContext ctx;
+ private ReaderRegistry registry;
+
+ // 1
+ private Reader<SimpleContainer, SimpleContainerBuilder> simpleContainerReader =
+ mockReader(Ids.SIMPLE_CONTAINER_ID, this::readSimpleContainer, SimpleContainerBuilder.class);
+ // 1.1
+ private Reader<SimpleAugment, SimpleAugmentBuilder> simpleAugmentReader =
+ mockReader(Ids.SIMPLE_AUGMENT_ID, this::readSimpleAugment, SimpleAugmentBuilder.class);
+ // 1.2
+ // Noop reader(no real attributes)
+ private Reader<ComplexAugment, ComplexAugmentBuilder> complexAugmentReader =
+ mockReader(Ids.COMPLEX_AUGMENT_ID, HoneycombReadInfraTest::noopRead, ComplexAugmentBuilder.class);
+ // 1.2.1
+ private Reader<ComplexAugmentContainer, ComplexAugmentContainerBuilder> complexAugmentContainerReader =
+ mockReader(Ids.COMPLEX_AUGMENT_CONTAINER_ID, this::readComplexAugmentContainer, ComplexAugmentContainerBuilder.class);
+ // 2
+ // Noop reader(no real attributes)
+ private Reader<ContainerWithList, ContainerWithListBuilder> containerWithListReader =
+ mockReader(Ids.CONTAINER_WITH_LIST_ID, HoneycombReadInfraTest::noopRead, ContainerWithListBuilder.class);
+ // 2.1
+ private ListReader<ListInContainer, ListInContainerKey, ListInContainerBuilder> listInContainerReader =
+ mockListReader(Ids.LIST_IN_CONTAINER_ID, this::readListInContainer, this::getListInContainerIds,
+ ListInContainerBuilder.class);
+ // 2.1.1
+ private Reader<ContainerInList, ContainerInListBuilder> containerInListReader =
+ mockReader(Ids.CONTAINER_IN_LIST_ID, this::readContainerInList, ContainerInListBuilder.class);
+ // 2.1.1.1
+ private ListReader<NestedList, NestedListKey, NestedListBuilder> nestedListReader =
+ mockListReader(Ids.NESTED_LIST_ID, this::readNestedList, this::getNestedListIds,
+ NestedListBuilder.class);
+
+ @Override
+ void postSetup() {
+ initReaderRegistry();
+ }
+
+ private void initReaderRegistry() {
+ registry = new CompositeReaderRegistryBuilder()
+ .add(containerWithListReader) // 2
+ .addBefore(simpleContainerReader, Ids.CONTAINER_WITH_LIST_ID) // 1
+ .add(simpleAugmentReader) // 1.1
+ .addAfter(complexAugmentReader, Ids.SIMPLE_AUGMENT_ID) // 1.2
+ .add(complexAugmentContainerReader) // 1.2.1
+ .add(listInContainerReader) // 2.1
+ .add(containerInListReader) // 2.1.1
+ .addBefore(nestedListReader, Ids.SIMPLE_AUGMENT_ID) // 2.1.1.1 - Before relationship should be ignored
+ .build();
+ }
+
+ private Reader<?, ?>[] getOrderedReaders() {
+ return new Reader<?, ?>[]{simpleContainerReader, // 1
+ simpleAugmentReader, // 1.1
+ complexAugmentReader, // 1.2
+ complexAugmentContainerReader, // 1.2.1
+ containerWithListReader, // 2
+ listInContainerReader, // 2.1
+ containerInListReader, // 2.1.1
+ nestedListReader}; // 2.1.1.1
+ }
+
+ @Test
+ public void testReadAll() throws Exception {
+ final ReadableDataTreeDelegator readableDataTreeDelegator =
+ new ReadableDataTreeDelegator(serializer, schemaContext, registry, contextBroker);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException>
+ read = readableDataTreeDelegator.read(YangInstanceIdentifier.EMPTY);
+
+ final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll =
+ toBinding(read.checkedGet().get());
+ assertThat(readAll.size(), is(2));
+ assertTrue(readAll.containsKey(Ids.CONTAINER_WITH_LIST_ID));
+ assertEquals(readContainerWithList(), readAll.get(Ids.CONTAINER_WITH_LIST_ID).stream().findFirst().get());
+ assertTrue(readAll.containsKey(Ids.SIMPLE_CONTAINER_ID));
+ assertEquals(readSimpleContainer(), readAll.get(Ids.SIMPLE_CONTAINER_ID).stream().findFirst().get());
+
+ final Reader<?, ?>[] ordered = getOrderedReaders();
+ final InOrder inOrder = inOrder(ordered);
+
+ final List<ListInContainerKey> listInContainerKeys = getListInContainerIds(Ids.LIST_IN_CONTAINER_ID, ctx);
+
+ verifyRootReader(inOrder, simpleContainerReader, Ids.SIMPLE_CONTAINER_ID, SimpleContainerBuilder.class);
+ verifyLeafChildReader(inOrder, simpleAugmentReader, Ids.SIMPLE_AUGMENT_ID);
+ verifyCompositeChildReader(inOrder, complexAugmentReader, ComplexAugmentBuilder.class, Ids.COMPLEX_AUGMENT_ID);
+ verifyLeafChildReader(inOrder, complexAugmentContainerReader, Ids.COMPLEX_AUGMENT_CONTAINER_ID);
+ verifyRootReader(inOrder, containerWithListReader, Ids.CONTAINER_WITH_LIST_ID, ContainerWithListBuilder.class);
+ verifyCompositeListReader(inOrder, listInContainerReader, Ids.LIST_IN_CONTAINER_ID,
+ listInContainerKeys, ListInContainerBuilder.class);
+
+ for (ListInContainerKey k : listInContainerKeys) {
+ final InstanceIdentifier<ContainerInList> id =
+ Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, k).child(ContainerInList.class);
+ verifyCompositeChildReader(inOrder, containerInListReader, ContainerInListBuilder.class, id);
+ final InstanceIdentifier<NestedList> nestedId = id.child(NestedList.class);
+ verifyLeafListReader(inOrder, nestedListReader, nestedId);
+ }
+
+ for (Reader<?, ?> reader : ordered) {
+ verifyNoMoreReadInteractions(reader);
+ }
+ }
+
+ private <D extends DataObject, B extends Builder<D>> void verifyNoMoreReadInteractions(final Reader<D, B> reader) {
+ verify(reader, atLeastOnce()).getManagedDataObjectType();
+ verifyNoMoreInteractions(reader);
+ }
+
+ private <D extends DataObject, B extends Builder<D>> void verifyCompositeChildReader(final InOrder inOrder,
+ final Reader<D, B> reader,
+ final Class<B> builderCls,
+ final InstanceIdentifier<D> id)
+ throws ReadFailedException {
+ verifyRootReader(inOrder, reader, id, builderCls);
+ verify(reader, atLeastOnce()).merge(any(Builder.class), any(id.getTargetType()));
+ }
+
+ private <D extends DataObject & Identifiable<K>, K extends Identifier<D>, B extends Builder<D>> void verifyCompositeListReader(
+ final InOrder inOrder,
+ final ListReader<D, K, B> reader,
+ final InstanceIdentifier<D> id,
+ final List<K> keys,
+ final Class<B> builderCls)
+ throws ReadFailedException {
+
+ // Id has to be wildcarded, since it was created using InstanceIdentifier.child() method
+ inOrder.verify(reader).getAllIds(eq(RWUtils.makeIidLastWildcarded(id)), any(ReadContext.class));
+ keys.stream()
+ .map(k -> RWUtils.replaceLastInId(id, RWUtils.getCurrentIdItem(id, k)))
+ .forEach(keyedId -> {
+ try {
+ verify(reader).getBuilder(keyedId);
+ verify(reader).readCurrentAttributes(eq(keyedId), any(builderCls), any(ReadContext.class));
+ } catch (ReadFailedException e) { throw new RuntimeException(e); }
+ });
+ verify(reader, atLeastOnce()).merge(any(Builder.class), anyListOf(id.getTargetType()));
+ }
+
+ private <D extends DataObject & Identifiable<K>, K extends Identifier<D>, B extends Builder<D>> void verifyLeafListReader(
+ final InOrder inOrder,
+ final ListReader<D, K, B> reader,
+ final InstanceIdentifier<D> id)
+ throws ReadFailedException {
+
+ // Id has to be wildcarded, since it was created using InstanceIdentifier.child() method
+ inOrder.verify(reader).readList(eq(RWUtils.makeIidLastWildcarded(id)), any(ReadContext.class));
+ verify(reader, atLeastOnce()).merge(any(Builder.class), anyListOf(id.getTargetType()));
+ }
+
+ private <D extends DataObject, B extends Builder<D>> void verifyRootReader(final InOrder inOrder,
+ final Reader<D, B> reader,
+ final InstanceIdentifier<D> id,
+ final Class<B> builderCls)
+ throws ReadFailedException {
+ inOrder.verify(reader).readCurrentAttributes(eq(id), any(builderCls), any(ReadContext.class));
+ verify(reader).getBuilder(id);
+ }
+
+
+ private <D extends DataObject, B extends Builder<D>> void verifyLeafChildReader(final InOrder inOrder,
+ final Reader<D, B> reader,
+ final InstanceIdentifier<D>... id)
+ throws ReadFailedException {
+ for (InstanceIdentifier<D> singleId : id) {
+ inOrder.verify(reader).read(eq(singleId), any(ReadContext.class));
+ verify(reader, atLeastOnce()).merge(any(Builder.class), any(singleId.getTargetType()));
+ }
+ }
+
+ static <D extends DataObject, B extends Builder<D>> Reader<D, B> mockReader(InstanceIdentifier<D> id,
+ CurrentAttributesReader<D, B> currentAttributesReader,
+ Class<B> builderClass) {
+ final ReflexiveReader<D, B> reflex = new ReflexiveReader<D, B>(id, builderClass) {
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final B builder,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ currentAttributesReader.readCurrentAttributes(id, builder, ctx);
+ }
+ };
+
+ // Simple spy on top of this reflexive reader cannot be used, spy also checks protected methods for invocation
+ // but those cannot be verified by mockito
+ final Reader<D, B> mock = mock(Reader.class);
+ try {
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).read(any(InstanceIdentifier.class), any(ReadContext.class));
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock)
+ .readCurrentAttributes(any(InstanceIdentifier.class), any(builderClass), any(ReadContext.class));
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), any(id.getTargetType()));
+ doReturn(id).when(mock).getManagedDataObjectType();
+ doReturn(builderClass.newInstance()).when(mock).getBuilder(any(InstanceIdentifier.class));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ return mock;
+ }
+
+ static <D extends DataObject & Identifiable<K>, K extends Identifier<D>, B extends Builder<D>> ListReader<D, K, B> mockListReader(
+ InstanceIdentifier<D> id,
+ CurrentAttributesReader<D, B> currentAttributesReader,
+ ListKeysReader<D, K> listKeysReader,
+ Class<B> builderClass) {
+
+ ListReader<D, K, B> reflex = new GenericListReader<>(id,
+ new ReflexiveListReaderCustomizer<D, K, B>(id.getTargetType(), builderClass) {
+
+ @Nonnull
+ @Override
+ public List<K> getAllIds(@Nonnull final InstanceIdentifier<D> id, @Nonnull final ReadContext context)
+ throws ReadFailedException {
+ return listKeysReader.getAllIds(id, context);
+ }
+
+ @Override
+ public void readCurrentAttributes(final InstanceIdentifier<D> id, final B builder,
+ final ReadContext context)
+ throws ReadFailedException {
+ currentAttributesReader.readCurrentAttributes(id, builder, context);
+ }
+ });
+
+ final ListReader<D, K, B> mock = mock(ListReader.class /*, withSettings().verboseLogging()*/);
+ try {
+ // not using eq(id) instead using any(InstanceIdentifier.class) due to InstanceIdentifier.equals weird behavior
+ // with wildcarded instance identifiers for lists
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).read(any(InstanceIdentifier.class), any(ReadContext.class));
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock)
+ .readCurrentAttributes(any(InstanceIdentifier.class), any(builderClass), any(ReadContext.class));
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), any(id.getTargetType()));
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).merge(any(Builder.class), anyListOf(id.getTargetType()));
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).getAllIds(any(InstanceIdentifier.class), any(ReadContext.class));
+ doAnswer(i -> reflexiveAnswer(reflex, i)).when(mock).readList(any(InstanceIdentifier.class), any(ReadContext.class));
+ doReturn(id).when(mock).getManagedDataObjectType();
+ doReturn(builderClass.newInstance()).when(mock).getBuilder(any(InstanceIdentifier.class));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ return mock;
+ }
+
+ private static <D extends DataObject, B extends Builder<D>> Object reflexiveAnswer(final Reader<D, B> instance,
+ final InvocationOnMock i) {
+ try {
+ return i.getMethod().invoke(instance, i.getArguments());
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new MockitoConfigurationException("Unable to invoke stubbed method invocation: " + i + " on " + instance);
+ }
+ }
+
+ @FunctionalInterface
+ interface CurrentAttributesReader<D extends DataObject, B extends Builder<D>> {
+ void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final B builder,
+ @Nonnull final ReadContext ctx);
+ }
+
+ @FunctionalInterface
+ interface ListKeysReader<D extends DataObject & Identifiable<K>, K extends Identifier<D>> {
+ List<K> getAllIds(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final ReadContext ctx);
+ }
+
+
+ private void readSimpleContainer(final InstanceIdentifier<SimpleContainer> id,
+ final SimpleContainerBuilder b,
+ final ReadContext readContext) {
+ b.setSimpleContainerName("simple");
+ }
+
+ private SimpleContainer readSimpleContainer() {
+ final SimpleContainerBuilder b = new SimpleContainerBuilder();
+ readSimpleContainer(Ids.SIMPLE_CONTAINER_ID, b, ctx);
+ b.addAugmentation(SimpleAugment.class, readSimpleAugment());
+ b.addAugmentation(ComplexAugment.class,
+ new ComplexAugmentBuilder().setComplexAugmentContainer(readComplexAugmentContainer()).build());
+ return b.build();
+ }
+
+ private void readSimpleAugment(final InstanceIdentifier<SimpleAugment> id,
+ final SimpleAugmentBuilder b,
+ final ReadContext readContext) {
+ b.setSimpleAugmentLeaf("simple augment");
+ }
+
+ private SimpleAugment readSimpleAugment() {
+ final SimpleAugmentBuilder b = new SimpleAugmentBuilder();
+ readSimpleAugment(Ids.SIMPLE_AUGMENT_ID, b, ctx);
+ return b.build();
+ }
+
+ private void readComplexAugmentContainer(final InstanceIdentifier<ComplexAugmentContainer> id,
+ final ComplexAugmentContainerBuilder b,
+ final ReadContext readContext) {
+ b.setSomeLeaf("nested container in augment");
+ }
+
+ private ComplexAugmentContainer readComplexAugmentContainer() {
+ final ComplexAugmentContainerBuilder b = new ComplexAugmentContainerBuilder();
+ readComplexAugmentContainer(Ids.COMPLEX_AUGMENT_CONTAINER_ID, b, ctx);
+ return b.build();
+ }
+
+ private ContainerWithList readContainerWithList() {
+ final ContainerWithListBuilder b = new ContainerWithListBuilder();
+ b.setListInContainer(readListInContainer());
+ return b.build();
+ }
+
+ private List<ListInContainerKey> getListInContainerIds(final InstanceIdentifier<ListInContainer> id,
+ final ReadContext readContext) {
+ return Lists.newArrayList(1L, 2L)
+ .stream()
+ .map(ListInContainerKey::new)
+ .collect(Collectors.toList());
+ }
+
+ private List<NestedListKey> getNestedListIds(final InstanceIdentifier<NestedList> id,
+ final ReadContext readContext) {
+ return Lists.newArrayList("one", "two")
+ .stream()
+ .map(NestedListKey::new)
+ .collect(Collectors.toList());
+ }
+
+ private void readListInContainer(final InstanceIdentifier<ListInContainer> id,
+ final ListInContainerBuilder b,
+ final ReadContext readContext) {
+ final ListInContainerKey key = id.firstKeyOf(ListInContainer.class);
+ b.setId(key.getId());
+ final ContainerInListBuilder cilBuilder = new ContainerInListBuilder();
+ readContainerInList(id.child(ContainerInList.class), cilBuilder, readContext);
+ b.setContainerInList(cilBuilder.build());
+ }
+
+ private void readNestedList(final InstanceIdentifier<NestedList> id,
+ final NestedListBuilder b,
+ final ReadContext readContext) {
+ final NestedListKey key = id.firstKeyOf(NestedList.class);
+ b.setNestedId(key.getNestedId());
+ b.setNestedName(key.getNestedId() + "name");
+ }
+
+ private void readContainerInList(final InstanceIdentifier<ContainerInList> id,
+ final ContainerInListBuilder b,
+ final ReadContext readContext) {
+ b.setName(id.firstKeyOf(ListInContainer.class).getId().toString());
+ b.setNestedList(getNestedListIds(Ids.NESTED_LIST_ID, ctx).stream()
+ .map(key -> id.child(NestedList.class, key))
+ .map(nestedId -> {
+ final NestedListBuilder nestedB = new NestedListBuilder();
+ readNestedList(nestedId, nestedB, readContext);
+ return nestedB.build();
+ })
+ .collect(Collectors.toList()));
+ }
+
+ private List<ListInContainer> readListInContainer() {
+ return getListInContainerIds(Ids.LIST_IN_CONTAINER_ID, ctx).stream()
+ .map(key -> Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, key))
+ .map(id -> {
+ final ListInContainerBuilder b = new ListInContainerBuilder();
+ readListInContainer(id, b, ctx);
+ return b.build();
+ }).collect(Collectors.toList());
+ }
+
+ static <D extends DataObject, B extends Builder<D>> void noopRead(final InstanceIdentifier<D> id, final B b,
+ final ReadContext readContext) {
+ // NOOP
+ }
+
+}
diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java
new file mode 100644
index 000000000..8faeb84b6
--- /dev/null
+++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombSubtreeReadInfraTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.translate.impl.read.GenericListReader;
+import io.fd.honeycomb.v3po.translate.read.ListReader;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.util.read.ReflexiveListReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.util.read.registry.CompositeReaderRegistryBuilder;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class HoneycombSubtreeReadInfraTest extends AbstractInfraTest {
+
+ @Mock
+ private ReadContext ctx;
+ private ReaderRegistry registry;
+
+ private Reader<ContainerWithList, ContainerWithListBuilder> containerWithListReader =
+ HoneycombReadInfraTest.mockReader(Ids.CONTAINER_WITH_LIST_ID, this::readSubtree, ContainerWithListBuilder.class);
+
+ private ListReader<ListInContainer, ListInContainerKey, ListInContainerBuilder> listInContainerReader =
+ new GenericListReader<>(Ids.LIST_IN_CONTAINER_ID,
+ new ReflexiveListReaderCustomizer<ListInContainer, ListInContainerKey, ListInContainerBuilder>(Ids.LIST_IN_CONTAINER_ID.getTargetType(), ListInContainerBuilder.class) {
+
+ @Nonnull
+ @Override
+ public List<ListInContainerKey> getAllIds(@Nonnull final InstanceIdentifier<ListInContainer> id,
+ @Nonnull final ReadContext context)
+ throws ReadFailedException {
+ // FIXME this is the only way of extending subtree reader via its list child
+ // Reflexive list reader has to be used in place of the list(managed by subtree reader perent)
+ // to enable further children readers. However, it will not work out of the box, because
+ // reflexive list reader has no way to tell what are the IDs to correctly invoke its children.
+ // Only way is to override the getAllIds method in reflexive reader and return the same list
+ // as parent used (this can be done using cache or repeated dump call)
+ return Lists.newArrayList(new ListInContainerKey(1L), new ListInContainerKey(2L));
+ }
+
+ @Override
+ public void readCurrentAttributes(final InstanceIdentifier<ListInContainer> id,
+ final ListInContainerBuilder builder,
+ final ReadContext context) throws ReadFailedException {
+ super.readCurrentAttributes(id, builder, context);
+ builder.setKey(id.firstKeyOf(ListInContainer.class));
+ }
+ });
+
+ private Reader<ContainerInList, ContainerInListBuilder> containerInListReader =
+ HoneycombReadInfraTest.mockReader(Ids.CONTAINER_IN_LIST_ID, this::readContainerInList, ContainerInListBuilder.class);
+
+ // TODO Test subtree readers especially composite structure where readers are under subtree reader
+
+ @Override
+ void postSetup() {
+ initReaderRegistry();
+ }
+
+ private void initReaderRegistry() {
+ registry = new CompositeReaderRegistryBuilder()
+ // Subtree reader handling its child list
+ .subtreeAdd(Sets.newHashSet(Ids.LIST_IN_CONTAINER_ID), containerWithListReader)
+ // Reflexive
+ .add(listInContainerReader)
+ .add(containerInListReader)
+ .build();
+ }
+
+ @Test
+ public void testReadAll() throws Exception {
+ final ReadableDataTreeDelegator readableDataTreeDelegator =
+ new ReadableDataTreeDelegator(serializer, schemaContext, registry, contextBroker);
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException>
+ read = readableDataTreeDelegator.read(YangInstanceIdentifier.EMPTY);
+
+ final Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll =
+ toBinding(read.checkedGet().get());
+ assertThat(readAll.size(), is(1));
+ assertEquals(readEntireSubtree(), readAll.get(Ids.CONTAINER_WITH_LIST_ID).stream().findFirst().get());
+ }
+
+ private void readSubtree(final InstanceIdentifier<ContainerWithList> id,
+ final ContainerWithListBuilder b,
+ final ReadContext readContext) {
+ b.setListInContainer(Lists.newArrayList(1L, 2L).stream()
+ .map(l -> new ListInContainerBuilder().setId(l).build())
+ .collect(Collectors.toList()));
+ }
+
+ private ContainerWithList readEntireSubtree() {
+ final ContainerWithListBuilder b = new ContainerWithListBuilder();
+ b.setListInContainer(Lists.newArrayList(1L, 2L).stream()
+ .map(l -> {
+ final ContainerInListBuilder containerInListBuilder = new ContainerInListBuilder();
+ readContainerInList(
+ Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey(l)).child(ContainerInList.class),
+ containerInListBuilder,
+ ctx);
+ return new ListInContainerBuilder().setId(l).setContainerInList(containerInListBuilder.build()).build();
+ })
+ .collect(Collectors.toList()));
+ return b.build();
+ }
+
+ private void readContainerInList(final InstanceIdentifier<ContainerInList> id,
+ final ContainerInListBuilder b,
+ final ReadContext readContext) {
+ b.setName(id.firstKeyOf(ListInContainer.class).getId().toString());
+ }
+}
diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java
new file mode 100644
index 000000000..b3a06ec46
--- /dev/null
+++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/HoneycombWriteInfraTest.java
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.data.DataModification;
+import io.fd.honeycomb.v3po.translate.util.write.registry.FlatWriterRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoiceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.Choice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGroupingBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+
+/**
+ * Testing honeycomb writes from data tree up to mocked writers.
+ */
+public class HoneycombWriteInfraTest extends AbstractInfraTest {
+
+ private TipProducingDataTree dataTree;
+ private WriterRegistry writerRegistry;
+
+ Writer<SimpleContainer> simpleContainerWriter = mockWriter(Ids.SIMPLE_CONTAINER_ID);
+ Writer<ComplexAugment> complexAugmentWriter = mockWriter(Ids.COMPLEX_AUGMENT_ID);
+ Writer<ComplexAugmentContainer> complexAugmentContainerWriter = mockWriter(Ids.COMPLEX_AUGMENT_CONTAINER_ID);
+ Writer<SimpleAugment> simpleAugmentWriter = mockWriter(Ids.SIMPLE_AUGMENT_ID);
+
+ Writer<ContainerWithList> containerWithListWriter = mockWriter(Ids.CONTAINER_WITH_LIST_ID);
+ Writer<ListInContainer> listInContainerWriter = mockWriter(Ids.LIST_IN_CONTAINER_ID);
+ Writer<ContainerInList> containerInListWriter = mockWriter(Ids.CONTAINER_IN_LIST_ID);
+ Writer<NestedList> nestedListWriter = mockWriter(Ids.NESTED_LIST_ID);
+
+ Writer<ContainerWithChoice> containerWithChoiceWriter = mockWriter(Ids.CONTAINER_WITH_CHOICE_ID);
+ Writer<ContainerFromGrouping> containerFromGroupingWriter = mockWriter(Ids.CONTAINER_FROM_GROUPING_ID);
+ Writer<C3> c3Writer = mockWriter(Ids.C3_ID);
+
+
+ private static <D extends DataObject> Writer<D> mockWriter(final InstanceIdentifier<D> id) {
+ final Writer<D> mock = (Writer<D>) mock(Writer.class);
+ when(mock.getManagedDataObjectType()).thenReturn(id);
+ return mock;
+ }
+
+ @Override
+ void postSetup() {
+ initDataTree();
+ initWriterRegistry();
+ }
+
+ private void initDataTree() {
+ dataTree = InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+ dataTree.setSchemaContext(schemaContext);
+ }
+
+ private void initWriterRegistry() {
+ writerRegistry = new FlatWriterRegistryBuilder()
+ .add(complexAugmentWriter) // unordered
+ .add(nestedListWriter) // 6
+ .addAfter(listInContainerWriter, Ids.NESTED_LIST_ID) // 7
+ .addAfter(containerInListWriter, Ids.LIST_IN_CONTAINER_ID) // 8
+ .addAfter(containerWithListWriter, Ids.CONTAINER_IN_LIST_ID) // 9
+ .addBefore(containerFromGroupingWriter, Ids.NESTED_LIST_ID) // 5
+ .addBefore(containerWithChoiceWriter, Ids.CONTAINER_FROM_GROUPING_ID) // 4
+ .addBefore(simpleContainerWriter, Ids.CONTAINER_WITH_CHOICE_ID) // 3
+ .addBefore(c3Writer, Ids.SIMPLE_CONTAINER_ID) // 2
+ .addBefore(simpleAugmentWriter, Ids.SIMPLE_CONTAINER_ID) // 2
+ .addBefore(complexAugmentContainerWriter, Sets.newHashSet(Ids.C3_ID, Ids.SIMPLE_AUGMENT_ID)) // 1
+ .build();
+ }
+
+ @Test
+ public void testWriteEmptyNonPresenceContainer() throws Exception {
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker);
+
+ final DataModification dataModification = modifiableDataTreeDelegator.newModification();
+ final SimpleContainer data = new SimpleContainerBuilder()
+ .build();
+
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, data);
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+
+ dataModification.commit();
+
+ verify(simpleContainerWriter).getManagedDataObjectType();
+ verifyNoMoreInteractions(simpleContainerWriter);
+ }
+
+ @Test
+ public void testWriteEverything() throws Exception {
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker);
+
+ final DataModification dataModification = modifiableDataTreeDelegator.newModification();
+ // Now write everything we can
+ writeSimpleContainer(dataModification);
+ writeContainerWithChoice(dataModification);
+ writeContainerWithList(dataModification);
+ dataModification.commit();
+
+ final Writer<?>[] orderedWriters = getOrderedWriters();
+ final InOrder inOrder = inOrder(orderedWriters);
+ verifyOrderedWrites(orderedWriters, inOrder);
+ }
+
+ private void verifyOrderedWrites(final Writer<?>[] orderedWriters, final InOrder inOrder)
+ throws io.fd.honeycomb.v3po.translate.write.WriteFailedException {
+ // TODO Modifications are not produced for nodes that do not contain any actual leaves (except when choice is a child) do we need those triggers ?
+ // Unordered
+ // verify(complexAugmentWriter).update(eq(COMPLEX_AUGMENT_ID), eq(null), eq(getComplexAugment()), any(WriteContext.class));
+ // 1
+ inOrder.verify(complexAugmentContainerWriter)
+ .update(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(null), eq(getComplexAugmentContainer()), any(WriteContext.class));
+ // 2
+ inOrder.verify(c3Writer)
+ .update(eq(Ids.C3_ID), eq(null), eq(getC3()), any(WriteContext.class));
+ // 2
+ verify(simpleAugmentWriter)
+ .update(eq(Ids.SIMPLE_AUGMENT_ID), eq(null), eq(getSimpleAugment()), any(WriteContext.class));
+ // 3
+ inOrder.verify(simpleContainerWriter)
+ .update(eq(Ids.SIMPLE_CONTAINER_ID), eq(null), eq(getSimpleContainer()), any(WriteContext.class));
+ // 4
+ inOrder.verify(containerWithChoiceWriter)
+ .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(getContainerWithChoiceWithComplexCase()), any(WriteContext.class));
+ // 5
+ inOrder.verify(containerFromGroupingWriter)
+ .update(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(null), eq(getContainerFromGrouping()), any(WriteContext.class));
+
+ final KeyedInstanceIdentifier<ListInContainer, ListInContainerKey> keyedListInContainer1 =
+ Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1));
+ final KeyedInstanceIdentifier<NestedList, NestedListKey> keyedNestedList1 =
+ keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1"));
+ final KeyedInstanceIdentifier<ListInContainer, ListInContainerKey> keyedListInContainer2 =
+ Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2));
+ final KeyedInstanceIdentifier<NestedList, NestedListKey> keyedNestedList2 =
+ keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2"));
+
+ // 6 - two items
+ inOrder.verify(nestedListWriter)
+ .update(eq(keyedNestedList1), eq(null), eq(getSingleNestedList("1")), any(WriteContext.class));
+ verify(nestedListWriter)
+ .update(eq(keyedNestedList2), eq(null), eq(getSingleNestedList("2")), any(WriteContext.class));
+
+ // 7 - two items
+ inOrder.verify(listInContainerWriter)
+ .update(eq(keyedListInContainer1), eq(null), eq(getSingleListInContainer((long)1)), any(WriteContext.class));
+ verify(listInContainerWriter)
+ .update(eq(keyedListInContainer2), eq(null), eq(getSingleListInContainer((long)2)), any(WriteContext.class));
+
+ // 8
+ inOrder.verify(containerInListWriter)
+ .update(eq(keyedListInContainer1.child(ContainerInList.class)), eq(null), eq(getContainerInList("1")), any(WriteContext.class));
+ verify(containerInListWriter)
+ .update(eq(keyedListInContainer2.child(ContainerInList.class)), eq(null), eq(getContainerInList("2")), any(WriteContext.class));
+
+ // 9 - Ignored because the container has no leaves, only complex child nodes
+ // inOrder.verify(containerWithListWriter)
+ // .update(eq(CONTAINER_WITH_LIST_ID), eq(null), eq(getContainerWithList()), any(WriteContext.class));
+
+ for (Writer<?> orderedWriter : orderedWriters) {
+ verify(orderedWriter).getManagedDataObjectType();
+ verifyNoMoreInteractions(orderedWriter);
+ }
+ }
+
+ private Writer<?>[] getOrderedWriters() {
+ return new Writer<?>[]{complexAugmentWriter, // Unordered
+ complexAugmentContainerWriter, // 1
+ c3Writer, // 2
+ simpleAugmentWriter, // 2
+ simpleContainerWriter, // 3
+ containerWithChoiceWriter, // 4
+ containerFromGroupingWriter, // 5
+ nestedListWriter, // 6
+ listInContainerWriter, // 7
+ containerInListWriter, // 8
+ containerWithListWriter};
+ }
+
+ @Test
+ public void testDeletes() throws Exception {
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker);
+
+ DataModification dataModification = modifiableDataTreeDelegator.newModification();
+ // Now write everything we can
+ writeSimpleContainer(dataModification);
+ writeContainerWithChoice(dataModification);
+ writeContainerWithList(dataModification);
+ dataModification.commit();
+ // Verify writes to be able to verifyNoMore interactions at the end
+ verifyOrderedWrites(getOrderedWriters(), inOrder(getOrderedWriters()));
+
+ dataModification = modifiableDataTreeDelegator.newModification();
+ deleteSimpleContainer(dataModification);
+ deleteContainerWithChoice(dataModification);
+ deleteContainerWithList(dataModification);
+ dataModification.commit();
+
+ final Writer<?>[] orderedWriters = getOrderedWriters();
+ Collections.reverse(Arrays.asList(orderedWriters));
+ final InOrder inOrder = inOrder(orderedWriters);
+
+ final KeyedInstanceIdentifier<ListInContainer, ListInContainerKey> keyedListInContainer1 =
+ Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 1));
+ final KeyedInstanceIdentifier<NestedList, NestedListKey> keyedNestedList1 =
+ keyedListInContainer1.child(ContainerInList.class).child(NestedList.class, new NestedListKey("1"));
+ final KeyedInstanceIdentifier<ListInContainer, ListInContainerKey> keyedListInContainer2 =
+ Ids.CONTAINER_WITH_LIST_ID.child(ListInContainer.class, new ListInContainerKey((long) 2));
+ final KeyedInstanceIdentifier<NestedList, NestedListKey> keyedNestedList2 =
+ keyedListInContainer2.child(ContainerInList.class).child(NestedList.class, new NestedListKey("2"));
+
+ // Deletes are handled in reverse order
+ // 1
+ inOrder.verify(containerInListWriter)
+ .update(eq(keyedListInContainer1.child(ContainerInList.class)), eq(getContainerInList("1")), eq(null), any(WriteContext.class));
+ verify(containerInListWriter)
+ .update(eq(keyedListInContainer2.child(ContainerInList.class)), eq(getContainerInList("2")), eq(null), any(WriteContext.class));
+
+ // 2
+ inOrder.verify(listInContainerWriter)
+ .update(eq(keyedListInContainer1), eq(getSingleListInContainer((long)1)), eq(null), any(WriteContext.class));
+ verify(listInContainerWriter)
+ .update(eq(keyedListInContainer2), eq(getSingleListInContainer((long)2)), eq(null), any(WriteContext.class));
+
+ // 3
+ inOrder.verify(nestedListWriter)
+ .update(eq(keyedNestedList1), eq(getSingleNestedList("1")), eq(null), any(WriteContext.class));
+ verify(nestedListWriter)
+ .update(eq(keyedNestedList2), eq(getSingleNestedList("2")), eq(null), any(WriteContext.class));
+ // 4
+ inOrder.verify(containerFromGroupingWriter)
+ .update(eq(Ids.CONTAINER_FROM_GROUPING_ID), eq(getContainerFromGrouping()), eq(null), any(WriteContext.class));
+ // 5
+ inOrder.verify(containerWithChoiceWriter)
+ .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(getContainerWithChoiceWithComplexCase()), eq(null), any(WriteContext.class));
+ // 6
+ inOrder.verify(simpleContainerWriter)
+ .update(eq(Ids.SIMPLE_CONTAINER_ID), eq(getSimpleContainer()), eq(null), any(WriteContext.class));
+ // 7
+ verify(simpleAugmentWriter)
+ .update(eq(Ids.SIMPLE_AUGMENT_ID), eq(getSimpleAugment()), eq(null), any(WriteContext.class));
+ // 8
+ inOrder.verify(c3Writer)
+ .update(eq(Ids.C3_ID), eq(getC3()), eq(null), any(WriteContext.class));
+ // 9
+ inOrder.verify(complexAugmentContainerWriter)
+ .update(eq(Ids.COMPLEX_AUGMENT_CONTAINER_ID), eq(getComplexAugmentContainer()), eq(null), any(WriteContext.class));
+
+ for (Writer<?> orderedWriter : orderedWriters) {
+ verify(orderedWriter).getManagedDataObjectType();
+ verifyNoMoreInteractions(orderedWriter);
+ }
+ }
+
+ private void writeContainerWithList(final DataModification dataModification) {
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(Ids.CONTAINER_WITH_LIST_ID, getContainerWithList());
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+ }
+
+ private void deleteContainerWithList(final DataModification dataModification) {
+ dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_LIST_ID));
+ }
+
+ private ContainerWithList getContainerWithList() {
+ return new ContainerWithListBuilder()
+ .setListInContainer(getListInContainer((long)1, (long)2))
+ .build();
+ }
+
+ private List<ListInContainer> getListInContainer(final Long... keys) {
+ final ArrayList<ListInContainer> objects = Lists.newArrayList();
+ for (Long key : keys) {
+ objects.add(getSingleListInContainer(key));
+ }
+ return objects;
+ }
+
+ private ListInContainer getSingleListInContainer(final Long key) {
+ return new ListInContainerBuilder()
+ .setId(key)
+ .setContainerInList(getContainerInList(Long.toString(key)))
+ .build();
+ }
+
+ private ContainerInList getContainerInList(String... nestedKeys) {
+ return new ContainerInListBuilder()
+ .setName("inlist")
+ .setNestedList(getNestedList(nestedKeys))
+ .build();
+ }
+
+ private List<NestedList> getNestedList(String... keys) {
+ final ArrayList<NestedList> nestedList = new ArrayList<>();
+ for (String key : keys) {
+ nestedList.add(getSingleNestedList(key));
+ }
+ return nestedList;
+ }
+
+ private NestedList getSingleNestedList(final String key) {
+ return new NestedListBuilder()
+ .setNestedId(key)
+ .setNestedName(key)
+ .build();
+ }
+
+ private void writeContainerWithChoice(final DataModification dataModification) {
+ writeContainerWithChoice(dataModification, getContainerWithChoiceWithComplexCase());
+ }
+
+
+ private void deleteContainerWithChoice(final DataModification dataModification) {
+ dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_WITH_CHOICE_ID));
+ }
+
+ private void writeContainerWithChoice(final DataModification dataModification,
+ final ContainerWithChoice containerWithChoice) {
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(Ids.CONTAINER_WITH_CHOICE_ID, containerWithChoice);
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+ }
+
+ private ContainerWithChoice getContainerWithChoiceWithComplexCase() {
+ return new ContainerWithChoiceBuilder()
+ .setLeafFromGrouping("fromG")
+ .setName("name")
+ .setContainerFromGrouping(getContainerFromGrouping())
+ .setChoice(getComplexCase())
+ .build();
+ }
+
+ private Choice getComplexCase() {
+ return new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.C3Builder()
+ .setC3(getC3())
+ .build();
+ }
+
+ private C3 getC3() {
+ return new C3Builder()
+ .setName("c3")
+ .build();
+ }
+
+ private void writeContainerFromGrouping(final DataModification dataModification) {
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(Ids.CONTAINER_FROM_GROUPING_ID, getContainerFromGrouping());
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+ }
+
+ private ContainerFromGrouping getContainerFromGrouping() {
+ return new ContainerFromGroupingBuilder()
+ .setLeafInContainerFromGrouping(111)
+ .build();
+ }
+
+
+ private void writeSimpleContainer(final DataModification dataModification) {
+ final Map.Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> normalizedNode =
+ serializer.toNormalizedNode(Ids.SIMPLE_CONTAINER_ID, getSimpleContainer());
+ dataModification.write(normalizedNode.getKey(), normalizedNode.getValue());
+ }
+
+ private void deleteSimpleContainer(final DataModification dataModification) {
+ final YangInstanceIdentifier yangId =
+ serializer.toYangInstanceIdentifier(Ids.SIMPLE_CONTAINER_ID);
+ dataModification.delete(yangId);
+ }
+
+ private SimpleContainer getSimpleContainer() {
+ return new SimpleContainerBuilder()
+ .setSimpleContainerName("n")
+ .addAugmentation(SimpleAugment.class, getSimpleAugment())
+ .addAugmentation(ComplexAugment.class, getComplexAugment())
+ .build();
+ }
+
+ private ComplexAugment getComplexAugment() {
+ return new ComplexAugmentBuilder()
+ .setComplexAugmentContainer(getComplexAugmentContainer())
+ .build();
+ }
+
+ private ComplexAugmentContainer getComplexAugmentContainer() {
+ return new ComplexAugmentContainerBuilder().setSomeLeaf("s").build();
+ }
+
+ private SimpleAugment getSimpleAugment() {
+ return new SimpleAugmentBuilder().setSimpleAugmentLeaf("a").build();
+ }
+
+ @Test
+ public void testWriteAndDeleteInTx() throws Exception {
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker);
+
+ final DataModification dataModification = modifiableDataTreeDelegator.newModification();
+ // Now write everything we can
+ writeSimpleContainer(dataModification);
+ deleteSimpleContainer(dataModification);
+ dataModification.commit();
+
+ verify(simpleContainerWriter).getManagedDataObjectType();
+ // No modification
+ verifyNoMoreInteractions(simpleContainerWriter);
+ }
+
+ @Test
+ public void testSubtreeWriter() throws Exception {
+ writerRegistry = new FlatWriterRegistryBuilder()
+ // Handles also container from grouping
+ .subtreeAdd(Sets.newHashSet(Ids.CONTAINER_FROM_GROUPING_ID), containerWithChoiceWriter)
+ .build();
+
+ final ModifiableDataTreeDelegator modifiableDataTreeDelegator =
+ new ModifiableDataTreeDelegator(serializer, dataTree, writerRegistry, contextBroker);
+
+ final ContainerWithChoice containerWithChoice =
+ new ContainerWithChoiceBuilder().setContainerFromGrouping(getContainerFromGrouping()).build();
+
+ // Test write subtree node
+ DataModification dataModification = modifiableDataTreeDelegator.newModification();
+ writeContainerFromGrouping(dataModification);
+ dataModification.commit();
+
+ verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType();
+ verify(containerWithChoiceWriter)
+ .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(null), eq(containerWithChoice), any(WriteContext.class));
+ verifyNoMoreInteractions(containerWithChoiceWriter);
+
+ // Test delete sub-node
+ dataModification = modifiableDataTreeDelegator.newModification();
+ final ContainerWithChoice containerWithChoiceEmpty = new ContainerWithChoiceBuilder().build();
+ deleteContainerFromGrouping(dataModification);
+ dataModification.commit();
+
+ verify(containerWithChoiceWriter, atLeastOnce()).getManagedDataObjectType();
+ verify(containerWithChoiceWriter)
+ .update(eq(Ids.CONTAINER_WITH_CHOICE_ID), eq(containerWithChoice), eq(containerWithChoiceEmpty), any(WriteContext.class));
+ verifyNoMoreInteractions(containerWithChoiceWriter);
+
+ // Test write with subtree node that's not handled by subtree writer
+ dataModification = modifiableDataTreeDelegator.newModification();
+ writeContainerWithChoice(dataModification);
+ try {
+ dataModification.commit();
+ fail("Missing writer for C3 should occur");
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ }
+
+ private void deleteContainerFromGrouping(final DataModification dataModification) {
+ dataModification.delete(serializer.toYangInstanceIdentifier(Ids.CONTAINER_FROM_GROUPING_ID));
+ }
+} \ No newline at end of file
diff --git a/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java
new file mode 100644
index 000000000..200f50c32
--- /dev/null
+++ b/infra/it/it-test/src/test/java/io/fd/honeycomb/v3po/data/impl/Ids.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.data.impl;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ComplexAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleAugment;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.choice.choice.c3.C3;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.simple.container.ComplexAugmentContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.some.attributes.ContainerFromGrouping;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Instance identifiers referencing all complex nodes within IT test-model.
+ */
+interface Ids {
+
+ // Simple container
+ // ORDER = 3
+ InstanceIdentifier<SimpleContainer> SIMPLE_CONTAINER_ID = InstanceIdentifier.create(SimpleContainer.class);
+ // 2
+ InstanceIdentifier<SimpleAugment> SIMPLE_AUGMENT_ID = SIMPLE_CONTAINER_ID.augmentation(SimpleAugment.class);
+ // UNORDERED
+ InstanceIdentifier<ComplexAugment> COMPLEX_AUGMENT_ID = SIMPLE_CONTAINER_ID.augmentation(ComplexAugment.class);
+ // 1
+ InstanceIdentifier<ComplexAugmentContainer> COMPLEX_AUGMENT_CONTAINER_ID = COMPLEX_AUGMENT_ID.child(ComplexAugmentContainer.class);
+ // Container with list
+ // 9
+ InstanceIdentifier<ContainerWithList> CONTAINER_WITH_LIST_ID = InstanceIdentifier.create(ContainerWithList.class);
+ // 7
+ InstanceIdentifier<ListInContainer> LIST_IN_CONTAINER_ID = CONTAINER_WITH_LIST_ID.child(ListInContainer.class);
+ // 8
+ InstanceIdentifier<ContainerInList> CONTAINER_IN_LIST_ID = LIST_IN_CONTAINER_ID.child(ContainerInList.class);
+ // 6
+ InstanceIdentifier<NestedList> NESTED_LIST_ID = CONTAINER_IN_LIST_ID.child(NestedList.class);
+ // Container with choice
+ // 4
+ InstanceIdentifier<ContainerWithChoice> CONTAINER_WITH_CHOICE_ID = InstanceIdentifier.create(ContainerWithChoice.class);
+ // 2
+ InstanceIdentifier<C3> C3_ID = CONTAINER_WITH_CHOICE_ID.child(C3.class);
+ // 5
+ InstanceIdentifier<ContainerFromGrouping> CONTAINER_FROM_GROUPING_ID = CONTAINER_WITH_CHOICE_ID.child(ContainerFromGrouping.class);
+}
diff --git a/infra/it/pom.xml b/infra/it/pom.xml
new file mode 100644
index 000000000..e565f22df
--- /dev/null
+++ b/infra/it/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>honeycomb-it-aggregator</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>honeycomb</name>
+ <packaging>pom</packaging>
+ <modelVersion>4.0.0</modelVersion>
+ <prerequisites>
+ <maven>3.1.1</maven>
+ </prerequisites>
+ <modules>
+ <module>test-model</module>
+ <module>it-test</module>
+ </modules>
+ <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/infra/it/test-model/pom.xml b/infra/it/test-model/pom.xml
new file mode 100644
index 000000000..2f087edf9
--- /dev/null
+++ b/infra/it/test-model/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>api-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../../common/api-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>honeycomb-test-model</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.mdsal.model</groupId>
+ <artifactId>yang-ext</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/infra/it/test-model/src/main/yang/hc-test.yang b/infra/it/test-model/src/main/yang/hc-test.yang
new file mode 100644
index 000000000..e13b5001f
--- /dev/null
+++ b/infra/it/test-model/src/main/yang/hc-test.yang
@@ -0,0 +1,123 @@
+module hc-test {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:hc:test";
+ prefix "hct";
+
+ revision "2015-01-05" {
+ description "Testing HC model with bindings";
+ }
+
+ import yang-ext {
+ prefix "ext";
+ }
+
+ // WRITE ORDER 3
+ // READ ORDER 1
+ container simple-container {
+ leaf simple-container-name {
+ type string;
+ }
+ }
+
+ grouping some-attributes {
+ leaf leaf-from-grouping {
+ type string;
+ }
+
+ // WRITE ORDER 5
+ container container-from-grouping {
+ leaf leaf-in-container-from-grouping {
+ type int32;
+ }
+ }
+ }
+
+ // WRITE ORDER 9 (no real attributes though)
+ // READ ORDER 2
+ container container-with-list {
+ // WRITE ORDER 7
+ // READ ORDER 2.1
+ list list-in-container {
+ key "id";
+ ordered-by "user";
+
+ leaf id {
+ type uint32;
+ }
+
+ // WRITE ORDER 8
+ // READ ORDER 2.1.1
+ container container-in-list {
+ leaf name {
+ type string;
+ }
+
+ // WRITE ORDER 6
+ // READ ORDER 2.1.1.1
+ list nested-list {
+ key "nested-id";
+ ordered-by "user";
+
+ leaf nested-id {
+ type string;
+ }
+
+ leaf nested-name {
+ type string;
+ }
+ }
+ }
+ }
+ }
+
+ // WRITE ORDER 4
+ container container-with-choice {
+ leaf name {
+ type string;
+ }
+
+ uses some-attributes;
+
+ choice choice {
+ leaf c1 {
+ type string;
+ }
+
+ leaf c2 {
+ type string;
+ }
+
+ // WRITE ORDER: 2
+ container c3 {
+ leaf name {
+ type string;
+ }
+ }
+ }
+ }
+
+ // WRITE ORDER: 2
+ // READ ORDER 1.1
+ augment "/hct:simple-container" {
+ ext:augment-identifier "simple-augment";
+
+ leaf simple-augment-leaf {
+ type string;
+ }
+ }
+
+ // WRITE UNORDERED
+ // READ ORDER 1.2
+ augment "/hct:simple-container" {
+ ext:augment-identifier "complex-augment";
+
+ // WRITE ORDER: 1
+ // READ ORDER 1.2.1
+ container complex-augment-container {
+ leaf some-leaf {
+ type string;
+ }
+ }
+ }
+
+}
diff --git a/infra/karaf/pom.xml b/infra/karaf/pom.xml
new file mode 100644
index 000000000..6cee0295b
--- /dev/null
+++ b/infra/karaf/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>karaf-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/karaf-parent</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>honeycomb-karaf</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>${project.artifactId}</name>
+ <prerequisites>
+ <maven>3.1.1</maven>
+ </prerequisites>
+ <properties>
+ <karaf.localFeature>odl-honeycomb-ui</karaf.localFeature>
+ </properties>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-artifacts</artifactId>
+ <version>${project.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+ <dependencies>
+ <dependency>
+ <!-- scope is compile so all features (there is only one) are installed
+ into startup.properties and the feature repo itself is not installed -->
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>framework</artifactId>
+ <type>kar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>honeycomb-features</artifactId>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+ <!-- DO NOT install or deploy the karaf artifact -->
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/infra/notification/api/pom.xml b/infra/notification/api/pom.xml
new file mode 100644
index 000000000..2291025f9
--- /dev/null
+++ b/infra/notification/api/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>impl-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>notification-api</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-dom-api</artifactId>
+ <version>2.0.2-Beryllium-SR2</version>
+ </dependency>
+ </dependencies>
+
+
+</project>
diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java
new file mode 100644
index 000000000..0f5e28cde
--- /dev/null
+++ b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/ManagedNotificationProducer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.Nonnull;
+
+/**
+ * Special notification producer that is capable of starting and stopping the notification stream
+ */
+@Beta
+public interface ManagedNotificationProducer extends NotificationProducer {
+
+ /**
+ * Start notification stream managed by this producer.
+ *
+ * @param collector Notification collector expected to collect produced notifications
+ */
+ void start(@Nonnull NotificationCollector collector);
+
+ /**
+ * Stop notification stream managed by this producer.
+ */
+ void stop();
+}
diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java
new file mode 100644
index 000000000..406ab03d2
--- /dev/null
+++ b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationCollector.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Notification collector. Collects all the notifications, which are further
+ * propagated to all wired northbound interfaces.
+ */
+@Beta
+public interface NotificationCollector extends AutoCloseable, NotificationProducer {
+
+ /**
+ * Publish a single notification.
+ *
+ * @param notification notification to be published
+ */
+ void onNotification(@Nonnull Notification notification);
+}
diff --git a/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java
new file mode 100644
index 000000000..dab773c45
--- /dev/null
+++ b/infra/notification/api/src/main/java/io/fd/honeycomb/v3po/notification/NotificationProducer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification;
+
+import com.google.common.annotations.Beta;
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Produces notification of exposed notification types
+ */
+@Beta
+public interface NotificationProducer extends AutoCloseable {
+
+ /**
+ * Return collection of notification types that will be emitted by this producer.
+ * Other types of notifications should not be emitted, since they can be rejected.
+ *
+ * @return collection of all notification types emitted by this producer
+ */
+ @Nonnull
+ Collection<Class<? extends Notification>> getNotificationTypes();
+}
diff --git a/infra/notification/api/src/main/yang/notification-api.yang b/infra/notification/api/src/main/yang/notification-api.yang
new file mode 100644
index 000000000..4e6eb98ae
--- /dev/null
+++ b/infra/notification/api/src/main/yang/notification-api.yang
@@ -0,0 +1,31 @@
+module notification-api {
+ yang-version 1;
+ namespace "urn:honeycomb:params:xml:ns:yang:notification:api";
+ prefix "hc-notif-a";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "Module definition for honeycomb notification service APIs";
+
+ revision "2016-06-01" {
+ description
+ "Initial revision";
+ }
+
+ identity honeycomb-notification-collector {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.notification.NotificationCollector;
+ }
+
+ identity dom-notification-service {
+ base "config:service-type";
+ config:java-class org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+ }
+
+ identity honeycomb-notification-producer {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.notification.ManagedNotificationProducer;
+ }
+
+}
diff --git a/infra/notification/impl/pom.xml b/infra/notification/impl/pom.xml
new file mode 100644
index 000000000..9fa542422
--- /dev/null
+++ b/infra/notification/impl/pom.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>impl-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>notification-impl</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>notification-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ <version>1.3.2-Beryllium-SR2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.netconf</groupId>
+ <artifactId>netconf-notifications-api</artifactId>
+ <version>1.0.2-Beryllium-SR2</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${config.file}</file>
+ <type>xml</type>
+ <classifier>config</classifier>
+ </artifact>
+ <artifact>
+ <file>src/main/config/notification-to-netconf-config.xml</file>
+ <type>xml</type>
+ <classifier>notification2netconf</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/infra/notification/impl/src/main/config/default-config.xml b/infra/notification/impl/src/main/config/default-config.xml
new file mode 100644
index 000000000..2b91de47f
--- /dev/null
+++ b/infra/notification/impl/src/main/config/default-config.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<snapshot>
+ <required-capabilities>
+ <capability>urn:honeycomb:params:xml:ns:yang:notification:impl?module=notification-impl&amp;revision=2016-06-01</capability>
+ </required-capabilities>
+ <configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <!-- Underlying dom notification service(router)-->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:impl">prefix:honeycomb-dom-notification-service</type>
+ <name>honeycomb-dom-notification-service</name>
+ <queue-depth>1024</queue-depth>
+ </module>
+
+ <!-- Honeycomb notification collector built on top of dom notification service-->
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:impl">prefix:honeycomb-notification-manager</type>
+ <name>honeycomb-notification-manager</name>
+ <runtime-mapping-codec>
+ <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </runtime-mapping-codec>
+ <dom-notification-service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:dom-notification-service</type>
+ <name>honeycomb-dom-notification-service</name>
+ </dom-notification-service>
+ <!-- Here goes list of producers. They will register themselves from plugins -->
+ <!--<notification-producers>-->
+ <!--</notification-producers>-->
+ </module>
+
+ </modules>
+
+ <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:dom-notification-service</type>
+ <instance>
+ <name>honeycomb-dom-notification-service</name>
+ <provider>/modules/module[type='honeycomb-dom-notification-service'][name='honeycomb-dom-notification-service']</provider>
+ </instance>
+ </service>
+ <service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:honeycomb-notification-collector</type>
+ <instance>
+ <name>honeycomb-notification-manager</name>
+ <provider>/modules/module[type='honeycomb-notification-manager'][name='honeycomb-notification-manager']</provider>
+ </instance>
+ </service>
+
+ </services>
+ </data>
+ </configuration>
+</snapshot>
+
diff --git a/infra/notification/impl/src/main/config/notification-to-netconf-config.xml b/infra/notification/impl/src/main/config/notification-to-netconf-config.xml
new file mode 100644
index 000000000..d2aac0932
--- /dev/null
+++ b/infra/notification/impl/src/main/config/notification-to-netconf-config.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<snapshot>
+<required-capabilities>
+ <capability>urn:honeycomb:params:xml:ns:yang:notification:impl?module=notification-impl&amp;revision=2016-06-01</capability>
+</required-capabilities>
+<configuration>
+
+ <data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+ <module>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:impl">prefix:honeycomb-notification-to-netconf-translator</type>
+ <name>honeycomb-notification-to-netconf-translator</name>
+ <netconf-notification-registry>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">
+ prefix:netconf-notification-registry
+ </type>
+ <name>vpp-netconf-notification-manager</name>
+ </netconf-notification-registry>
+ <netconf-notification-collector>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:netconf:northbound:notification">
+ prefix:netconf-notification-collector
+ </type>
+ <name>vpp-netconf-notification-manager</name>
+ </netconf-notification-collector>
+ <schema-service>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+ <name>yang-schema-service</name>
+ </schema-service>
+ <honeycomb-notification-collector>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:honeycomb-notification-collector</type>
+ <name>honeycomb-notification-manager</name>
+ </honeycomb-notification-collector>
+ <dom-notification-service>
+ <type xmlns:prefix="urn:honeycomb:params:xml:ns:yang:notification:api">prefix:dom-notification-service</type>
+ <name>honeycomb-dom-notification-service</name>
+ </dom-notification-service>
+ <netconf-stream-name>honeycomb</netconf-stream-name>
+ <netconf-stream-description>All notifications received by honeycomb's plugins</netconf-stream-description>
+ </module>
+ </modules>
+ </data>
+</configuration>
+</snapshot>
diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java
new file mode 100644
index 000000000..e7d54e318
--- /dev/null
+++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollector.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification.impl;
+
+import io.fd.honeycomb.v3po.notification.NotificationCollector;
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Notification collector based on MD-SAL's {@link NotificationPublishService}.
+ */
+public final class HoneycombNotificationCollector implements NotificationCollector, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationCollector.class);
+
+ private final NotificationPublishService bindingDOMNotificationPublishServiceAdapter;
+ private final NotificationProducerRegistry notificationProducerRegistry;
+
+ public HoneycombNotificationCollector(
+ @Nonnull final NotificationPublishService bindingDOMNotificationPublishServiceAdapter,
+ @Nonnull final NotificationProducerRegistry notificationProducerRegistry) {
+ this.bindingDOMNotificationPublishServiceAdapter = bindingDOMNotificationPublishServiceAdapter;
+ this.notificationProducerRegistry = notificationProducerRegistry;
+ }
+
+ @Override
+ public void close() throws Exception {
+ LOG.trace("Closing");
+ }
+
+ @Override
+ public void onNotification(@Nonnull final Notification notification) {
+ LOG.debug("Notification: {} pushed into collector", notification.getClass().getSimpleName());
+ LOG.trace("Notification: {} pushed into collector", notification);
+ try {
+ bindingDOMNotificationPublishServiceAdapter.putNotification(notification);
+ } catch (InterruptedException e) {
+ LOG.warn("Interrupted", e);
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public Collection<Class<? extends Notification>> getNotificationTypes() {
+ return notificationProducerRegistry.getNotificationTypes();
+ }
+}
diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java
new file mode 100644
index 000000000..8fba700bd
--- /dev/null
+++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistry.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer;
+import io.fd.honeycomb.v3po.notification.NotificationProducer;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * Holds the collection of registered notification producers.
+ * Provides additional information about the types of notifications produced per producer and overall.
+ */
+@ThreadSafe
+public final class NotificationProducerRegistry {
+
+ private final Set<Class<? extends Notification>> notificationTypes;
+ private final Map<QName, ManagedNotificationProducer> notificationQNameToProducer;
+ private final Multimap<ManagedNotificationProducer, QName> notificationProducerQNames;
+
+ public NotificationProducerRegistry(final List<ManagedNotificationProducer> notificationProducersDependency) {
+ this.notificationTypes = toTypes(notificationProducersDependency);
+ this.notificationQNameToProducer = toQNameMap(notificationProducersDependency);
+ this.notificationProducerQNames = toQNameMapReversed(notificationProducersDependency);
+ }
+
+ private static Multimap<ManagedNotificationProducer, QName> toQNameMapReversed(final List<ManagedNotificationProducer> notificationProducers) {
+ final Multimap<ManagedNotificationProducer, QName> multimap = HashMultimap.create();
+
+ for (ManagedNotificationProducer producer : notificationProducers) {
+ for (Class<? extends Notification> aClass : producer.getNotificationTypes()) {
+ multimap.put(producer, getQName(aClass));
+ }
+ }
+ return multimap;
+ }
+
+ private static Set<Class<? extends Notification>> toTypes(final List<ManagedNotificationProducer> notificationProducersDependency) {
+ // Get all notification types registered from HC notification producers
+ return notificationProducersDependency
+ .stream()
+ .flatMap(producer -> producer.getNotificationTypes().stream())
+ .collect(Collectors.toSet());
+ }
+
+
+ private static Map<QName, ManagedNotificationProducer> toQNameMap(final List<ManagedNotificationProducer> producerDependencies) {
+ // Only a single notification producer per notification type is allowed
+ final Map<QName, ManagedNotificationProducer> qNamesToProducers = Maps.newHashMap();
+ for (ManagedNotificationProducer notificationProducer : producerDependencies) {
+ for (QName qName : typesToQNames(notificationProducer.getNotificationTypes())) {
+ final NotificationProducer previousProducer = qNamesToProducers.put(qName, notificationProducer);
+ checkArgument(previousProducer == null, "2 producers of the same notification type: %s. " +
+ "Producer 1: {} Producer 2: {}" , qName, previousProducer, notificationProducer);
+ }
+ }
+ return qNamesToProducers;
+ }
+
+
+ private static Set<QName> typesToQNames(final Collection<Class<? extends Notification>> notificationTypes) {
+ return notificationTypes
+ .stream()
+ .map(NotificationProducerRegistry::getQName)
+ .collect(Collectors.toSet());
+ }
+
+
+ public static QName getQName(final Class<? extends Notification> aClass) {
+ try {
+ return (QName) aClass.getField("QNAME").get(null);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new IllegalArgumentException("Unable to retrieve QName for notification of type: " + aClass, e);
+ }
+ }
+
+ Set<Class<? extends Notification>> getNotificationTypes() {
+ return notificationTypes;
+ }
+
+ Map<QName, ManagedNotificationProducer> getNotificationQNameToProducer() {
+ return notificationQNameToProducer;
+ }
+
+ Multimap<ManagedNotificationProducer, QName> getNotificationProducerQNames() {
+ return notificationProducerQNames;
+ }
+}
diff --git a/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java
new file mode 100644
index 000000000..cefb50ac9
--- /dev/null
+++ b/infra/notification/impl/src/main/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTracker.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification.impl;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer;
+import io.fd.honeycomb.v3po.notification.NotificationCollector;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListener;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Starts & stops notification producer dependencies on demand.
+ * Uses {@link DOMNotificationSubscriptionListenerRegistry} to receive subscription change notifications.
+ */
+@ThreadSafe
+public final class NotificationProducerTracker
+ implements DOMNotificationSubscriptionListener, AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NotificationProducerTracker.class);
+
+ private final ListenerRegistration<NotificationProducerTracker> subscriptionListener;
+ private final NotificationProducerRegistry registry;
+ private final NotificationCollector collector;
+
+ private final Set<ManagedNotificationProducer> alreadyStartedProducers = new HashSet<>();
+
+ public NotificationProducerTracker(@Nonnull final NotificationProducerRegistry registry,
+ @Nonnull final NotificationCollector collector,
+ @Nonnull final DOMNotificationSubscriptionListenerRegistry notificationRouter) {
+ this.registry = registry;
+ this.collector = collector;
+ this.subscriptionListener = notificationRouter.registerSubscriptionListener(this);
+ }
+
+ @Override
+ public synchronized void onSubscriptionChanged(final Set<SchemaPath> set) {
+ LOG.debug("Subscriptions changed. Current subscriptions: {}", set);
+ final Set<QName> currentSubscriptions = set.stream().map(SchemaPath::getLastComponent).collect(Collectors.toSet());
+ final Set<QName> startedQNames = getStartedQNames(alreadyStartedProducers);
+ final Sets.SetView<QName> newSubscriptions = Sets.difference(currentSubscriptions, startedQNames);
+ LOG.debug("Subscriptions changed. New subscriptions: {}", newSubscriptions);
+ final Sets.SetView<QName> deletedSubscriptions = Sets.difference(startedQNames, currentSubscriptions);
+ LOG.debug("Subscriptions changed. Deleted subscriptions: {}", deletedSubscriptions);
+
+ newSubscriptions.stream().forEach(newSub -> {
+ if(!registry.getNotificationQNameToProducer().containsKey(newSub)) {
+ return;
+ }
+ final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub);
+ if(alreadyStartedProducers.contains(producer)) {
+ return;
+ }
+ LOG.debug("Starting notification producer: {}", producer);
+ producer.start(collector);
+ alreadyStartedProducers.add(producer);
+ });
+
+ deletedSubscriptions.stream().forEach(newSub -> {
+ checkState(registry.getNotificationQNameToProducer().containsKey(newSub));
+ final ManagedNotificationProducer producer = registry.getNotificationQNameToProducer().get(newSub);
+ checkState(alreadyStartedProducers.contains(producer));
+ LOG.debug("Stopping notification producer: {}", producer);
+ producer.stop();
+ alreadyStartedProducers.remove(producer);
+ });
+
+ }
+
+ private Set<QName> getStartedQNames(final Set<ManagedNotificationProducer> alreadyStartedProducers) {
+ return alreadyStartedProducers.stream()
+ .flatMap(p -> registry.getNotificationProducerQNames().get(p).stream())
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public synchronized void close() throws Exception {
+ LOG.trace("Closing");
+ subscriptionListener.close();
+ // Stop all producers
+ LOG.debug("Stopping all producers: {}", alreadyStartedProducers);
+ alreadyStartedProducers.forEach(ManagedNotificationProducer::stop);
+ alreadyStartedProducers.clear();
+ }
+}
diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java
new file mode 100644
index 000000000..9a9c7def0
--- /dev/null
+++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModule.java
@@ -0,0 +1,27 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601;
+
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+
+public class HoneycombDomNotificationServiceModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombDomNotificationServiceModule {
+ public HoneycombDomNotificationServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public HoneycombDomNotificationServiceModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombDomNotificationServiceModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ JmxAttributeValidationException.checkCondition(getQueueDepth() > 0, "Queue depth must be > 0", queueDepthJmxAttribute);
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ // Create DOMNotificationRouter to do the heavy lifting for HoneycombNotificationCollector
+ // It creates executor internally
+ return DOMNotificationRouter.create(getQueueDepth());
+ }
+
+}
diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java
new file mode 100644
index 000000000..d3603acb3
--- /dev/null
+++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombDomNotificationServiceModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: notification-impl yang module local name: honeycomb-dom-notification-service
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Jun 08 09:49:08 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601;
+public class HoneycombDomNotificationServiceModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombDomNotificationServiceModuleFactory {
+
+}
diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java
new file mode 100644
index 000000000..4a9440cbc
--- /dev/null
+++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModule.java
@@ -0,0 +1,93 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601;
+
+import io.fd.honeycomb.v3po.notification.NotificationCollector;
+import io.fd.honeycomb.v3po.notification.NotificationProducer;
+import io.fd.honeycomb.v3po.notification.impl.HoneycombNotificationCollector;
+import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry;
+import io.fd.honeycomb.v3po.notification.impl.NotificationProducerTracker;
+import java.util.Collection;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMNotificationPublishServiceAdapter;
+import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.dom.broker.impl.DOMNotificationRouter;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+public class HoneycombNotificationManagerModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationManagerModule {
+
+ public HoneycombNotificationManagerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public HoneycombNotificationManagerModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombNotificationManagerModule 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() {
+ final DOMNotificationRouter notificationRouter = getDomNotificationServiceDependency();
+
+ // Create the registry to keep track of what's registered
+ final NotificationProducerRegistry notificationProducerRegistry =
+ new NotificationProducerRegistry(getNotificationProducersDependency());
+
+ // Create BA version of notification service (implementation is free from ODL)
+ final BindingToNormalizedNodeCodec codec = getRuntimeMappingCodecDependency();
+ final BindingDOMNotificationPublishServiceAdapter bindingDOMNotificationPublishServiceAdapter =
+ new BindingDOMNotificationPublishServiceAdapter(codec, notificationRouter);
+
+ // Create Collector on top of BA notification service and registry
+ final HoneycombNotificationCollector honeycombNotificationCollector =
+ new HoneycombNotificationCollector(bindingDOMNotificationPublishServiceAdapter, notificationProducerRegistry);
+
+ // Create tracker, responsible for starting and stopping registered notification producers whenever necessary
+ final NotificationProducerTracker notificationProducerTracker =
+ new NotificationProducerTracker(notificationProducerRegistry, honeycombNotificationCollector,
+ notificationRouter);
+
+ // TODO wire with restconf
+ // DOMNotificationService is already provided by DOMBroker injected into RESTCONF, however RESTCONF
+ // only supports data-change notification, nothing else. So currently its impossible.
+
+ return new CloseableCollector(honeycombNotificationCollector, () -> {
+ // Close all resources in order opposite to instantiation
+ notificationProducerTracker.close();
+ honeycombNotificationCollector.close();
+ bindingDOMNotificationPublishServiceAdapter.close();
+ // notificationProducerRegistry; no close, it's just a collection
+ });
+ }
+
+ /**
+ * NotificationCollector wrapper in which close method execution can be injected
+ */
+ private class CloseableCollector implements AutoCloseable, NotificationCollector, NotificationProducer {
+
+ private final HoneycombNotificationCollector honeycombNotificationCollector;
+ private final AutoCloseable resources;
+
+ CloseableCollector(final HoneycombNotificationCollector honeycombNotificationCollector,
+ final AutoCloseable resources) {
+ this.honeycombNotificationCollector = honeycombNotificationCollector;
+ this.resources = resources;
+ }
+
+ @Override
+ public void close() throws Exception {
+ resources.close();
+ }
+
+ @Override
+ public void onNotification(final Notification notification) {
+ honeycombNotificationCollector.onNotification(notification);
+ }
+
+ @Override
+ public Collection<Class<? extends Notification>> getNotificationTypes() {
+ return honeycombNotificationCollector.getNotificationTypes();
+ }
+ }
+}
diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java
new file mode 100644
index 000000000..b12b70017
--- /dev/null
+++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationManagerModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: notification-impl yang module local name: honeycomb-notification-manager
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Jun 01 16:08:01 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601;
+public class HoneycombNotificationManagerModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationManagerModuleFactory {
+
+}
diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java
new file mode 100644
index 000000000..4d85d64c5
--- /dev/null
+++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModule.java
@@ -0,0 +1,157 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.fd.honeycomb.v3po.notification.NotificationCollector;
+import io.fd.honeycomb.v3po.notification.impl.NotificationProducerRegistry;
+import java.io.IOException;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.dom.DOMResult;
+import org.opendaylight.controller.config.api.JmxAttributeValidationException;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.netconf.notifications.NetconfNotification;
+import org.opendaylight.netconf.notifications.NotificationPublisherRegistration;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netmod.notification.rev080714.netconf.streams.StreamBuilder;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class HoneycombNotificationToNetconfTranslatorModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationToNetconfTranslatorModule {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HoneycombNotificationToNetconfTranslatorModule.class);
+
+ public HoneycombNotificationToNetconfTranslatorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public HoneycombNotificationToNetconfTranslatorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.HoneycombNotificationToNetconfTranslatorModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ JmxAttributeValidationException.checkCondition(!getNetconfStreamName().isEmpty(),
+ "Stream name cannot be empty", netconfStreamNameJmxAttribute);
+ JmxAttributeValidationException.checkCondition(!getNetconfStreamDescription().isEmpty(),
+ "Stream description cannot be empty", netconfStreamDescriptionJmxAttribute);
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ final SchemaService schemaService = getSchemaServiceDependency();
+ final StreamNameType streamType = new StreamNameType(getNetconfStreamName());
+ final NotificationCollector hcNotificationCollector = getHoneycombNotificationCollectorDependency();
+
+ // Register as NETCONF notification publisher under configured name
+ final NotificationPublisherRegistration netconfNotificationProducerReg =
+ getNetconfNotificationCollectorDependency().registerNotificationPublisher(new StreamBuilder()
+ .setName(streamType)
+ .setReplaySupport(false)
+ .setDescription(getNetconfStreamDescription()).build());
+
+ // Notification Translator, get notification from HC producers and put into NETCONF notification collector
+ final DOMNotificationListener domNotificationListener =
+ notification -> {
+ LOG.debug("Propagating notification: {} into NETCONF", notification.getType());
+ netconfNotificationProducerReg.onNotification(streamType, notificationToXml(notification, schemaService.getGlobalContext()));
+ };
+
+ // NotificationManager is used to provide list of available notifications (which are all of the notifications registered)
+ // TODO make available notifications configurable here so that any number of notification streams for NETCONF
+ // can be configured on top of a single notification manager
+ LOG.debug("Current notifications to be exposed over NETCONF: {}", hcNotificationCollector.getNotificationTypes());
+ final Set<SchemaPath> currentNotificationSchemaPaths = hcNotificationCollector.getNotificationTypes()
+ .stream()
+ .map(NotificationProducerRegistry::getQName)
+ .map(qName -> SchemaPath.create(true, qName))
+ .collect(Collectors.toSet());
+
+ // Register as listener to HC's DOM notification service
+ // TODO This should only be triggered when NETCONF notifications are activated
+ // Because this way we actually start all notification producers
+ // final Collection<QName> notificationQNames =
+ final ListenerRegistration<DOMNotificationListener> domNotificationListenerReg = getDomNotificationServiceDependency()
+ .registerNotificationListener(domNotificationListener, currentNotificationSchemaPaths);
+
+ LOG.info("Exposing NETCONF notification stream: {}", streamType.getValue());
+ return () -> {
+ domNotificationListenerReg.close();
+ netconfNotificationProducerReg.close();
+ };
+ }
+
+ @VisibleForTesting
+ static NetconfNotification notificationToXml(final DOMNotification domNotification, final SchemaContext ctx) {
+ LOG.trace("Transforming notification: {} into XML", domNotification.getType());
+
+ final SchemaPath type = domNotification.getType();
+ final QName notificationQName = type.getLastComponent();
+ final DOMResult result = prepareDomResultForRpcRequest(notificationQName);
+
+ try {
+ writeNormalizedRpc(domNotification, result, type, ctx);
+ } catch (final XMLStreamException | IOException | IllegalStateException e) {
+ LOG.warn("Unable to transform notification: {} into XML", domNotification.getType(), e);
+ throw new IllegalArgumentException("Unable to serialize " + type, e);
+ }
+
+ final Document node = result.getNode().getOwnerDocument();
+ return new NetconfNotification(node);
+ }
+
+ private static DOMResult prepareDomResultForRpcRequest(final QName notificationQName) {
+ final Document document = XmlUtil.newDocument();
+ final Element notificationElement =
+ document.createElementNS(notificationQName.getNamespace().toString(), notificationQName.getLocalName());
+ document.appendChild(notificationElement);
+ return new DOMResult(notificationElement);
+ }
+
+ private static final XMLOutputFactory XML_FACTORY;
+
+ static {
+ XML_FACTORY = XMLOutputFactory.newFactory();
+ XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
+ }
+
+ private static void writeNormalizedRpc(final DOMNotification normalized, final DOMResult result,
+ final SchemaPath schemaPath, final SchemaContext baseNetconfCtx)
+ throws IOException, XMLStreamException {
+ final XMLStreamWriter writer = XML_FACTORY.createXMLStreamWriter(result);
+ try {
+ try (final NormalizedNodeStreamWriter normalizedNodeStreamWriter =
+ XMLStreamNormalizedNodeStreamWriter.create(writer, baseNetconfCtx, schemaPath)) {
+ try (final NormalizedNodeWriter normalizedNodeWriter =
+ NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter)) {
+ for (DataContainerChild<?, ?> dataContainerChild : normalized.getBody().getValue()) {
+ normalizedNodeWriter.write(dataContainerChild);
+ }
+ normalizedNodeWriter.flush();
+ }
+ }
+ } finally {
+ try {
+ writer.close();
+ } catch (final Exception e) {
+ LOG.warn("Unable to close resource properly. Ignoring", e);
+ }
+ }
+ }
+
+}
diff --git a/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java
new file mode 100644
index 000000000..336223040
--- /dev/null
+++ b/infra/notification/impl/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/HoneycombNotificationToNetconfTranslatorModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: notification-impl yang module local name: honeycomb-notification-to-netconf-translator
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Thu Jun 02 14:39:23 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601;
+public class HoneycombNotificationToNetconfTranslatorModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601.AbstractHoneycombNotificationToNetconfTranslatorModuleFactory {
+
+}
diff --git a/infra/notification/impl/src/main/yang/notification-impl.yang b/infra/notification/impl/src/main/yang/notification-impl.yang
new file mode 100644
index 000000000..84899751c
--- /dev/null
+++ b/infra/notification/impl/src/main/yang/notification-impl.yang
@@ -0,0 +1,141 @@
+module notification-impl {
+ yang-version 1;
+ namespace "urn:honeycomb:params:xml:ns:yang:notification:impl";
+ prefix "hc-notif-i";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import netconf-northbound-notification { prefix notify-api; revision-date 2015-08-06; }
+ import notification-api { prefix hc-notif-a; revision-date 2016-06-01; }
+ import opendaylight-md-sal-dom { prefix dom; revision-date 2013-10-28;}
+ import opendaylight-sal-binding-broker-impl { prefix binding-impl; revision-date 2013-10-28;}
+
+ description
+ "Module definition for honeycomb notification implementations";
+
+ revision "2016-06-01" {
+ description
+ "Initial revision";
+ }
+
+ identity honeycomb-notification-manager {
+ base config:module-type;
+ config:java-name-prefix HoneycombNotificationManager;
+ config:provided-service hc-notif-a:honeycomb-notification-collector;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case honeycomb-notification-manager {
+ when "/config:modules/config:module/config:type = 'honeycomb-notification-manager'";
+
+ container runtime-mapping-codec {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity binding-impl:binding-dom-mapping-service;
+ }
+ }
+ }
+
+ container dom-notification-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity hc-notif-a:dom-notification-service;
+ }
+ }
+ }
+
+ list notification-producers {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity hc-notif-a:honeycomb-notification-producer;
+ }
+ }
+ }
+
+ }
+ }
+
+ identity honeycomb-dom-notification-service {
+ base config:module-type;
+ config:provided-service hc-notif-a:dom-notification-service;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case honeycomb-dom-notification-service {
+ when "/config:modules/config:module/config:type = 'honeycomb-dom-notification-service'";
+
+ leaf queue-depth {
+ type uint16;
+ description "Size of the queue for outgoing notifications";
+ }
+ }
+ }
+
+ identity honeycomb-notification-to-netconf-translator {
+ base config:module-type;
+ config:java-name-prefix HoneycombNotificationToNetconfTranslator;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case honeycomb-notification-to-netconf-translator {
+ when "/config:modules/config:module/config:type = 'honeycomb-notification-to-netconf-translator'";
+
+ container netconf-notification-collector {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity notify-api:netconf-notification-collector;
+ }
+ }
+ }
+ container honeycomb-notification-collector {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity hc-notif-a:honeycomb-notification-collector;
+ }
+ }
+ }
+ container netconf-notification-registry {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity notify-api:netconf-notification-registry;
+ }
+ }
+ }
+ container schema-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:schema-service;
+ }
+ }
+ }
+ container dom-notification-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity hc-notif-a:dom-notification-service;
+ }
+ }
+ }
+
+ leaf netconf-stream-name {
+ type string;
+ mandatory true;
+ description "Name of the stream under which all the registered notifications should be emitted";
+ }
+
+ leaf netconf-stream-description {
+ type string;
+ mandatory true;
+ description "Description for the stream under which all the registered notifications should be emitted";
+ }
+
+ }
+ }
+
+}
diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java
new file mode 100644
index 000000000..f55d3abdf
--- /dev/null
+++ b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/HoneycombNotificationCollectorTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification.impl;
+
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStartBuilder;
+
+public class HoneycombNotificationCollectorTest {
+
+ private NotificationProducerRegistry notificationRegistry;
+ @Mock
+ private NotificationPublishService notificationService;
+ @Mock
+ private ManagedNotificationProducer producer;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ notificationRegistry = new NotificationProducerRegistry(Lists.newArrayList(producer));
+ }
+
+ @Test
+ public void testNotificationTypes() throws Exception {
+ final HoneycombNotificationCollector honeycombNotificationCollector =
+ new HoneycombNotificationCollector(notificationService, notificationRegistry);
+
+ honeycombNotificationCollector.getNotificationTypes();
+ verify(producer, atLeast(1)).getNotificationTypes();
+ }
+
+ @Test
+ public void testCollect() throws Exception {
+ final HoneycombNotificationCollector honeycombNotificationCollector =
+ new HoneycombNotificationCollector(notificationService, notificationRegistry);
+
+ final NetconfSessionStart notif = new NetconfSessionStartBuilder().build();
+ honeycombNotificationCollector.onNotification(notif);
+ verify(notificationService).putNotification(notif);
+ }
+} \ No newline at end of file
diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java
new file mode 100644
index 000000000..5fdf502b9
--- /dev/null
+++ b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerRegistryTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification.impl;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.mockito.Mockito.doReturn;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfCapabilityChange;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionEnd;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart;
+import org.opendaylight.yangtools.yang.binding.Notification;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class NotificationProducerRegistryTest {
+
+ @Mock
+ private ManagedNotificationProducer producer;
+ @Mock
+ private ManagedNotificationProducer producer2;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn(Collections.singleton(NetconfCapabilityChange.class))
+ .when(producer).getNotificationTypes();
+ final ArrayList<Object> producer2Notifications = Lists.newArrayList();
+ producer2Notifications.add(NetconfSessionStart.class);
+ producer2Notifications.add(NetconfSessionEnd.class);
+ doReturn(producer2Notifications).when(producer2).getNotificationTypes();
+ }
+
+ @Test
+ public void testNotificationTypes() throws Exception {
+ final NotificationProducerRegistry notificationRegistry =
+ new NotificationProducerRegistry(Lists.newArrayList(producer, producer2));
+
+ final Set<Class<? extends Notification>> notificationTypes =
+ notificationRegistry.getNotificationTypes();
+
+ Assert.assertThat(notificationTypes, hasItem(NetconfSessionEnd.class));
+ Assert.assertThat(notificationTypes, hasItem(NetconfSessionStart.class));
+ Assert.assertThat(notificationTypes, hasItem(NetconfCapabilityChange.class));
+ }
+
+ @Test
+ public void testNotificationTypesMapped() throws Exception {
+ final NotificationProducerRegistry notificationRegistry =
+ new NotificationProducerRegistry(Lists.newArrayList(producer, producer2));
+
+ final Multimap<ManagedNotificationProducer, QName> notificationTypes =
+ notificationRegistry.getNotificationProducerQNames();
+
+ Assert.assertThat(notificationTypes.keySet(), hasItem(producer));
+ Assert.assertThat(notificationTypes.get(producer), hasItem(NetconfCapabilityChange.QNAME));
+ Assert.assertThat(notificationTypes.keySet(), hasItem(producer2));
+ Assert.assertThat(notificationTypes.get(producer2), hasItem(NetconfSessionStart.QNAME));
+ Assert.assertThat(notificationTypes.get(producer2), hasItem(NetconfSessionEnd.QNAME));
+
+ final Map<QName, ManagedNotificationProducer> notificationQNameToProducer =
+ notificationRegistry.getNotificationQNameToProducer();
+
+ Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfCapabilityChange.QNAME));
+ Assert.assertThat(notificationQNameToProducer.get(NetconfCapabilityChange.QNAME), is(producer));
+
+ Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfSessionStart.QNAME));
+ Assert.assertThat(notificationQNameToProducer.keySet(), hasItem(NetconfSessionEnd.QNAME));
+ Assert.assertThat(notificationQNameToProducer.get(NetconfSessionStart.QNAME), is(producer2));
+ Assert.assertThat(notificationQNameToProducer.get(NetconfSessionEnd.QNAME), is(producer2));
+
+
+ }
+} \ No newline at end of file
diff --git a/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java
new file mode 100644
index 000000000..b62bf0709
--- /dev/null
+++ b/infra/notification/impl/src/test/java/io/fd/honeycomb/v3po/notification/impl/NotificationProducerTrackerTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fd.honeycomb.v3po.notification.impl;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.notification.ManagedNotificationProducer;
+import io.fd.honeycomb.v3po.notification.NotificationCollector;
+import java.util.Collections;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.dom.spi.DOMNotificationSubscriptionListenerRegistry;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.NetconfSessionStart;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class NotificationProducerTrackerTest {
+
+ private NotificationProducerRegistry registry;
+ @Mock
+ private DOMNotificationSubscriptionListenerRegistry subscriptionRegistry;
+ @Mock
+ private NotificationCollector collector;
+ @Mock
+ private ManagedNotificationProducer producer;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn(Collections.singleton(NetconfSessionStart.class)).when(producer).getNotificationTypes();
+ registry = new NotificationProducerRegistry(Lists.newArrayList(producer));
+ }
+
+ @Test
+ public void name() throws Exception {
+ final NotificationProducerTracker notificationProducerTracker =
+ new NotificationProducerTracker(registry, collector, subscriptionRegistry);
+ verify(subscriptionRegistry).registerSubscriptionListener(notificationProducerTracker);
+
+ final Set<SchemaPath> subscriptions = Sets.newHashSet();
+ subscriptions.add(SchemaPath.create(true, NetconfSessionStart.QNAME));
+ notificationProducerTracker.onSubscriptionChanged(subscriptions);
+
+ verify(producer).start(collector);
+
+ notificationProducerTracker.onSubscriptionChanged(Sets.newHashSet());
+ verify(producer).stop();
+ }
+} \ No newline at end of file
diff --git a/infra/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java b/infra/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java
new file mode 100644
index 000000000..ccfb4bb9d
--- /dev/null
+++ b/infra/notification/impl/src/test/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/notification/impl/rev160601/NoetificationToNetconfModuleTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.notification.impl.rev160601;
+
+import javax.annotation.Nonnull;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.netconf.notifications.NetconfNotification;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.*;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public class NoetificationToNetconfModuleTest {
+
+ private final DOMNotification notification = new DOMNotification() {
+
+ private QName qname = NetconfSessionStart.QNAME;
+ private YangInstanceIdentifier.NodeIdentifier nodeIdentifier =
+ new YangInstanceIdentifier.NodeIdentifier(NetconfSessionStart.QNAME);
+
+ @Nonnull
+ @Override
+ public SchemaPath getType() {
+ return SchemaPath.create(true, qname);
+ }
+
+ @Nonnull
+ @Override
+ public ContainerNode getBody() {
+ return Builders.containerBuilder()
+ .withNodeIdentifier(nodeIdentifier)
+ .withChild(ImmutableNodes.leafNode(QName.create(qname, "username"), "user"))
+ .withChild(ImmutableNodes.leafNode(QName.create(qname, "session-id"), 1))
+ .withChild(ImmutableNodes.leafNode(QName.create(qname, "source-host"), "127.0.0.1"))
+ .build();
+ }
+ };
+
+ @Test
+ public void notificationToXml() throws Exception {
+ final ModuleInfoBackedContext moduleInfoBackedContext = getModuleInfoBackedCOntext();
+
+ final NetconfNotification netconfNotification = HoneycombNotificationToNetconfTranslatorModule
+ .notificationToXml(notification, moduleInfoBackedContext.getSchemaContext());
+
+ final String notificationString = netconfNotification.toString();
+ Assert.assertThat(notificationString, CoreMatchers.containsString("<netconf-session-start"));
+ Assert.assertThat(notificationString, CoreMatchers.containsString("<username>user</username>"));
+ Assert.assertThat(notificationString, CoreMatchers.containsString("eventTime"));
+ }
+
+ private static ModuleInfoBackedContext getModuleInfoBackedCOntext() {
+ final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create();
+ final YangModuleInfo ietfNetconfNotifModuleInfo =
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.notifications.rev120206.$YangModuleInfoImpl
+ .getInstance();
+ moduleInfoBackedContext.registerModuleInfo(ietfNetconfNotifModuleInfo);
+ return moduleInfoBackedContext;
+ }
+} \ No newline at end of file
diff --git a/infra/notification/pom.xml b/infra/notification/pom.xml
new file mode 100644
index 000000000..81b8aae40
--- /dev/null
+++ b/infra/notification/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+
+ <parent>
+ <groupId>org.opendaylight.odlparent</groupId>
+ <artifactId>odlparent</artifactId>
+ <version>1.6.2-Beryllium-SR2</version>
+ <relativePath/>
+ </parent>
+
+ <artifactId>notification-aggregator</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>notification</name>
+ <packaging>pom</packaging>
+ <modelVersion>4.0.0</modelVersion>
+ <prerequisites>
+ <maven>3.1.1</maven>
+ </prerequisites>
+ <modules>
+ <module>api</module>
+ <module>impl</module>
+ </modules>
+ <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/infra/pom.xml b/infra/pom.xml
new file mode 100644
index 000000000..872b749c2
--- /dev/null
+++ b/infra/pom.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+
+ <parent>
+ <groupId>org.opendaylight.odlparent</groupId>
+ <artifactId>odlparent</artifactId>
+ <version>1.6.2-Beryllium-SR2</version>
+ <relativePath/>
+ </parent>
+
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>infra-aggregator</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>honeycomb-infra</name>
+ <packaging>pom</packaging>
+ <modelVersion>4.0.0</modelVersion>
+ <prerequisites>
+ <maven>3.1.1</maven>
+ </prerequisites>
+ <modules>
+ <module>data-api</module>
+ <module>data-impl</module>
+ <module>translate-spi</module>
+ <module>translate-api</module>
+ <module>translate-impl</module>
+ <module>translate-utils</module>
+ <module>notification</module>
+ <module>cfg-init</module>
+ <module>impl</module>
+ <module>karaf</module>
+ <module>features</module>
+ <module>artifacts</module>
+ <module>it</module>
+ </modules>
+ <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-install-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/infra/postman_rest_collection.json b/infra/postman_rest_collection.json
new file mode 100644
index 000000000..3adf19582
--- /dev/null
+++ b/infra/postman_rest_collection.json
@@ -0,0 +1,1386 @@
+{
+ "id": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "name": "Honeycomb RESTCONF calls",
+ "description": "Common mgmt operations on VPP using Honeycomb REST interface",
+ "order": [
+ "286f2d2d-88eb-ad68-7c6f-43caecc6073e",
+ "d27322b8-59d5-a2dc-a7a2-8f7197057164",
+ "5dc1d9f4-e7ae-bb21-e558-8a56ac825922",
+ "2f1d8e0d-4961-4b7f-50ad-0210e52e59aa",
+ "93022e97-24fa-5784-1e59-94674817215f",
+ "8b867c90-459f-68f6-4b2e-fa653098c28d",
+ "e611d23a-c205-192b-e8f9-dd99fa169274",
+ "3fccc4cd-f14b-0333-83d5-924afe3938e4"
+ ],
+ "folders": [
+ {
+ "id": "7a914134-23ea-3154-1557-d29dc8d464e7",
+ "name": "IP",
+ "description": "",
+ "order": [
+ "014dcd2b-1024-37a3-bc85-c18b0fdc0e8c",
+ "eb4852a6-ff3a-f9f4-4332-6677a93fcd2a",
+ "dc22d351-497a-4f01-52c1-6f01c76cf80b",
+ "69a05849-026d-f120-5c6a-f3e2b0884d88",
+ "b10169c2-ae0e-bb1c-3dea-ca06839c6c9c",
+ "8d49cf41-facc-c4ac-cee7-475d50e2a0be",
+ "70835949-7252-a3cc-491b-f8aa69286399",
+ "d5a90cfd-fd7e-63b6-e1f1-16fc0671b0e1",
+ "3f7ee49b-ae11-b032-915f-a14bf838246f",
+ "345ba26c-60cf-5fa1-37d0-f5af7a8c33bf"
+ ],
+ "owner": "45557"
+ },
+ {
+ "id": "3d67940d-3c7a-8127-3965-6aee160e6b6f",
+ "name": "L2 FIB",
+ "description": "",
+ "order": [
+ "76e9ee89-1594-ff38-ffff-ffc8de5d4054",
+ "dfbcc9e8-6f3d-b1c2-8bf6-989b1b1b3196",
+ "e2ff4db0-c013-cf89-d7b5-66de22bc7115",
+ "a937f1fc-10d5-bc19-ad5f-ac91fc007779",
+ "cf177689-9dd7-bcfe-d490-e9de6bde4dbb",
+ "0b63013d-3958-b427-2288-380019800468",
+ "99674f70-ec54-99f2-9300-545a031d5bab",
+ "f624bc96-502b-685c-1596-672ad5fcf6a9",
+ "0a1d6c0d-fe5e-1e1d-2471-96d7a4683e2f",
+ "d752fed9-127a-9289-71f3-90ba2b459d77",
+ "5c800144-cf89-7b77-1dd2-f488101ca441"
+ ],
+ "owner": "45557",
+ "collectionId": "e9ba4e80-fb4d-1eae-07e7-97b323164130"
+ },
+ {
+ "id": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "name": "ACL",
+ "description": "",
+ "order": [
+ "d7cb3d97-fc40-f471-33f3-07aad557777d",
+ "c067f721-6b1c-f205-799d-720b242f8592",
+ "5ab03c5f-03c5-8d42-bf65-024239049857",
+ "4f11c937-9562-3377-d03b-a9829b25f688",
+ "491ee2b7-88bf-3a71-282d-a16a62996026",
+ "fa63c194-ea7a-51ad-a6d8-d7485ed67f2e",
+ "b9931024-fa86-4e04-e2a4-4feeaa9e99e1",
+ "d39e4c39-90b6-072c-44b8-d909cac53a5b",
+ "2ce36b98-d7a9-fe37-80f6-f3a4abf25103",
+ "d0984799-d321-d1e0-a855-cc434519bef0",
+ "be223323-bf59-9c4f-f896-ff74c03b01a1",
+ "ee11f1c0-a1fe-51a6-a5e5-fcdcfc2b620a"
+ ],
+ "owner": "45557",
+ "collectionId": "e9ba4e80-fb4d-1eae-07e7-97b323164130"
+ },
+ {
+ "id": "60596aab-a4f1-bb64-d701-816de9482201",
+ "name": "TAP",
+ "description": "",
+ "order": [
+ "3f9588e4-885f-3792-bdf4-d0f10704ae4d",
+ "27106942-aff5-6ab5-07ae-5315135297a1",
+ "4db9b360-0d57-6ce6-7e3c-a6acfe17d512",
+ "5995dcf7-51c0-42ef-448b-a70bb68735d5"
+ ],
+ "owner": "45557"
+ },
+ {
+ "id": "c97b9ad9-64e6-5de3-09b8-851c1189d767",
+ "name": "VHOST",
+ "description": "",
+ "order": [
+ "7f312f15-a81e-b513-83b7-a5f7755eae67",
+ "ac161bec-e046-4bb7-d695-60bdfb0c6cea",
+ "55102dbd-1fc9-5f91-2082-40a2dc311f4f"
+ ],
+ "owner": "45557"
+ },
+ {
+ "id": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "name": "VLAN",
+ "description": "",
+ "order": [
+ "2ca639f4-f4a4-07c7-9419-79fd66061458",
+ "27eb70b8-a191-dd5c-f106-6693086ce872",
+ "f2e9a3cb-f3cc-f501-6015-8ebf7d8b2c3e",
+ "cbb77318-52e0-5647-cd1c-8c679ea7b830",
+ "8b339568-b60a-715f-d4fd-416bafe981a9",
+ "bd2897c6-5fc7-bec5-87be-4fd52bb8e471",
+ "44de3eff-ace5-90a9-dd16-af3f5f13daad",
+ "b08a0677-6b59-74ea-e5c6-7383194b19dd"
+ ],
+ "owner": "45557",
+ "collectionId": "e9ba4e80-fb4d-1eae-07e7-97b323164130"
+ },
+ {
+ "id": "288569d0-e425-7877-4fd9-a82e8a340ade",
+ "name": "VXLAN-GPE-TUNNEL",
+ "description": "",
+ "order": [
+ "0f223706-ca7f-fb8f-60ed-2b18e769e344",
+ "203ea32c-e43a-669c-4f28-232926df6928"
+ ],
+ "owner": "45557"
+ },
+ {
+ "id": "3868e66d-0ee5-bd4d-6b35-075c4841b5c1",
+ "name": "VXLAN-TUNNEL",
+ "description": "",
+ "order": [
+ "f014ab21-8e40-c344-a68b-0f1b86578bd8",
+ "4f15d0e9-8530-157e-0fe8-d24034bc31df"
+ ],
+ "owner": "45557"
+ }
+ ],
+ "timestamp": 1460119141419,
+ "owner": "45557",
+ "remoteLink": "",
+ "public": false,
+ "requests": [
+ {
+ "id": "0f223706-ca7f-fb8f-60ed-2b18e769e344",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vxlanGpeTun3",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464768006369,
+ "name": "Add vxlan-gpe - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"vxlanGpeTun3\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:vxlan-gpe-tunnel\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"routing\" : {\r\n \"vrf-id\" : \"0\"\r\n },\r\n \"vxlan-gpe\" : {\r\n \"local\" : \"192.168.50.76\",\r\n \"remote\" : \"192.168.50.71\",\r\n \"vni\" : \"9\",\r\n \"next-protocol\" : \"ethernet\",\r\n \"encap-vrf-id\" : \"0\",\r\n \"decap-vrf-id\" : \"0\"\r\n }\r\n }\r\n ]\r\n \r\n}",
+ "folder": "288569d0-e425-7877-4fd9-a82e8a340ade"
+ },
+ {
+ "id": "203ea32c-e43a-669c-4f28-232926df6928",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vxlanGpeTun3",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464764638498,
+ "name": "Delete vxlan-gpe - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "",
+ "folder": "288569d0-e425-7877-4fd9-a82e8a340ade"
+ },
+ {
+ "id": "27106942-aff5-6ab5-07ae-5315135297a1",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/tapp2",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1462796722186,
+ "name": "Add complex tap ifc - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"tapp2\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:tap\",\r\n \"tap\" :{\r\n \"tap-name\" : \"tapp2\",\r\n \"mac\" : \"00:ff:ff:ff:ff:ff\",\r\n \"device-instance\" : 55\r\n }\r\n }\r\n ]\r\n \r\n}",
+ "folder": "60596aab-a4f1-bb64-d701-816de9482201"
+ },
+ {
+ "id": "27eb70b8-a191-dd5c-f106-6693086ce872",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464804325716,
+ "name": "Enable GigabitEthernet0/9/0 interface - cfg",
+ "description": "Enables GigabitEthernet0/9/0 interface. Equivalent vppctl command:\n\nvppctl set in state GigabitEthernet0/9/0 up\n\nNeeded to enable sub interface.\n\nTo verify invoke:\n\nvppctl show int",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"GigabitEthernet0/9/0\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"v3po:ethernet\": {\r\n \"mtu\": 9216\r\n },\r\n \"enabled\": true,\r\n \"vpp-vlan:sub-interfaces\": {\r\n \"sub-interface\": [\r\n {\r\n \"identifier\": 1,\r\n \"vlan-type\": \"802dot1q\",\r\n \"tags\": {\r\n \"tag\": [\r\n {\r\n \"index\": 1,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": \"any\"\r\n }\r\n },\r\n {\r\n \"index\": 0,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": \"100\"\r\n }\r\n }\r\n ]\r\n },\r\n \"match\": {\r\n \"vlan-tagged\": {\r\n \"match-exact-tags\": true\r\n }\r\n },\r\n \"enabled\": false\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "286f2d2d-88eb-ad68-7c6f-43caecc6073e",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1460539962712,
+ "name": "List ifcs - cfg",
+ "description": "List ifcs - cfg",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "014dcd2b-1024-37a3-bc85-c18b0fdc0e8c",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F8%2F0/ipv4/neighbor/172.16.0.2",
+ "preRequestScript": null,
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": null,
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1466498440466,
+ "name": "Ip Neighbour Get",
+ "description": "This call returns data stored in honeycomb,to verify actual data stored by vpp, call vppctl show ip fib|arp",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\n \n \"interface\":{\n \"name\":\"GigabitEthernet0/8/0\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"enabled\":\"true\",\n \"ipv4\":{\n \"neighbor\":{\n \"ip\":\"172.16.0.2\",\n \"link-layer-address\":\"00:01:11:00:02:02\"\n }\n }\n }\n}\n"
+ },
+ {
+ "id": "dc22d351-497a-4f01-52c1-6f01c76cf80b",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F8%2F0/ipv4/neighbor/172.16.0.2",
+ "preRequestScript": null,
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": null,
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1466498502212,
+ "name": "Ip Neighbour Delete",
+ "description": "Verify this call by invoking vppctl show ip fib|arp",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\n \"neighbor\":{\n \"ip\":\"172.16.0.2\",\n \"link-layer-address\":\"00:01:11:00:02:02\"\n }\n}\n"
+ },
+ {
+ "id": "eb4852a6-ff3a-f9f4-4332-6677a93fcd2a",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F8%2F0/ipv4/neighbor/172.16.0.2",
+ "preRequestScript": null,
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": null,
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1466498409820,
+ "name": "Ip Neighbour Add",
+ "description": "This call doesnt have respective dump call,because of performance issues(dump for this can contain millions of entries). Data inserted throught this call can be verified by calling vppctl show ip fib|arp",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\n \n \"neighbor\":{\n \"ip\":\"172.16.0.2\",\n \"link-layer-address\":\"00:01:11:00:02:02\"\n }\n \n}\n"
+ },
+ {
+ "id": "2ca639f4-f4a4-07c7-9419-79fd66061458",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464804304987,
+ "name": "Add sub interface - cfg",
+ "description": "Adds sub interface. Corresponsing vpp cli command:\n\nvppctl create sub GigabitEthernet0/9/0 1 dot1q 100 inner-dot1q any\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "rawModeData": "{\r\n \"sub-interface\": [\r\n {\r\n \"identifier\": \"1\",\r\n \"vlan-type\": \"802dot1q\",\r\n \"tags\": {\r\n \"tag\": [\r\n {\r\n \"index\": \"0\",\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": \"100\"\r\n }\r\n },\r\n {\r\n \"index\": \"1\",\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": \"any\"\r\n }\r\n }\r\n ]\r\n },\r\n \"match\": {\r\n \"vlan-tagged\": {\r\n \"match-exact-tags\": \"true\"\r\n }\r\n },\r\n \"enabled\": \"false\"\r\n }\r\n ]\r\n}"
+ },
+ {
+ "id": "2f1d8e0d-4961-4b7f-50ad-0210e52e59aa",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/v3po:vpp",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1460540006597,
+ "name": "Read vpp - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "76e9ee89-1594-ff38-ffff-ffc8de5d4054",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD",
+ "pathVariables": {},
+ "preRequestScript": "",
+ "method": "PUT",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "data": [],
+ "dataMode": "raw",
+ "name": "Create bridge domain - cfg",
+ "description": "Creates bridgfe domain. Corresponds to invoking:\n\nvat# bridge_domain_add_del bd_id [bd_id] learn 0 forward 0 uu-flood 0 flood 1 arp-term 0\n\nTo verify run:\n\nvppctl show bridge-domain [bd_id] detail",
+ "descriptionFormat": "html",
+ "time": 1465805138795,
+ "version": 2,
+ "responses": [],
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "folder": "3d67940d-3c7a-8127-3965-6aee160e6b6f",
+ "rawModeData": "{\r\n \"bridge-domain\": [\r\n {\r\n \"name\": \"testBD\",\r\n \"flood\": \"true\",\r\n \"forward\": \"false\",\r\n \"learn\": \"false\",\r\n \"unknown-unicast-flood\": \"false\",\r\n \"arp-termination\": \"false\"\r\n }\r\n ]\r\n}"
+ },
+ {
+ "id": "dfbcc9e8-6f3d-b1c2-8bf6-989b1b1b3196",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465806733493,
+ "name": "Show bridge domain - cfg",
+ "description": "Shows testBD bridge domain configuration.",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \"bridge-domain\": [\r\n {\r\n \"name\": \"testBD\",\r\n \"flood\": \"true\",\r\n \"forward\": \"false\",\r\n \"learn\": \"false\",\r\n \"unknown-unicast-flood\": \"false\",\r\n \"arp-termination\": \"false\"\r\n }\r\n ]\r\n}"
+ },
+ {
+ "id": "e2ff4db0-c013-cf89-d7b5-66de22bc7115",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/operational/v3po:vpp-state/bridge-domains/bridge-domain/testBD",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465806752970,
+ "name": "Show bridge domain - oper",
+ "description": "Shows testBD bridge domain operational state.\nCorresponds to invoking:\n\nvppctl show bridge-domain [bd_id] detail",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \"bridge-domain\": [\r\n {\r\n \"name\": \"testBD\",\r\n \"flood\": \"true\",\r\n \"forward\": \"false\",\r\n \"learn\": \"false\",\r\n \"unknown-unicast-flood\": \"false\",\r\n \"arp-termination\": \"false\"\r\n }\r\n ]\r\n}"
+ },
+ {
+ "id": "a937f1fc-10d5-bc19-ad5f-ac91fc007779",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/v3po:l2",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465805545425,
+ "name": "Add local0 to bridge domain",
+ "description": "Adds l2 interconnection of bridge-based type to local0 interface.\nCorresponds to the following command:\n\nvppctl set interface l2 bridge local0 [bd_id]\n\nTo verify invoke:\n\nvppctl show bridge-domain [bd_id] detail",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \"l2\": {\r\n \"bridge-domain\": \"testBD\",\r\n \"split-horizon-group\": 1,\r\n \"bridged-virtual-interface\": \"false\"\r\n }\r\n}"
+ },
+ {
+ "id": "cf177689-9dd7-bcfe-d490-e9de6bde4dbb",
+ "headers": "",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/",
+ "preRequestScript": null,
+ "pathVariables": {},
+ "method": "GET",
+ "data": null,
+ "dataMode": "params",
+ "tests": null,
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465805728052,
+ "name": "Show local0 - cfg",
+ "description": "Shows local0 interface configuration",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": []
+ },
+ {
+ "id": "0b63013d-3958-b427-2288-380019800468",
+ "headers": "",
+ "url": "http://localhost:8181/restconf/operational/ietf-interfaces:interfaces-state/interface/local0/",
+ "pathVariables": {},
+ "preRequestScript": null,
+ "method": "GET",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "data": null,
+ "dataMode": "params",
+ "name": "Show local0 - oper",
+ "description": "Shows local0 interface operational state",
+ "descriptionFormat": "html",
+ "time": 1465805719840,
+ "version": 2,
+ "responses": [],
+ "tests": null,
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "folder": "3d67940d-3c7a-8127-3965-6aee160e6b6f"
+ },
+ {
+ "id": "99674f70-ec54-99f2-9300-545a031d5bab",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD/l2-fib-table/l2-fib-entry/aa:bb:cc:dd:ee:ff",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465806456783,
+ "name": "Add L2 FIB entry (forward)",
+ "description": "Adds L2 FIB static entry. Corresponds to invoking:\n\nvppctl l2fib add aa:bb:cc:dd:ee:ff [bd_id] local0\n\nTo verify run:\n\nvppctl show l2fib verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\n \"l2-fib-entry\": [\n {\n \"phys-address\": \"aa:bb:cc:dd:ee:ff\",\n \"outgoing-interface\": \"local0\",\n \"action\": \"l2-fib-forward\"\n }\n ]\n}"
+ },
+ {
+ "id": "f624bc96-502b-685c-1596-672ad5fcf6a9",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD/l2-fib-table/l2-fib-entry/aa:bb:cc:dd:ee:ff",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465806577485,
+ "name": "Delete L2 FIB entry",
+ "description": "Removes L2 FIB entry. Corresponds to invoking:\n\nvppctl l2fib del aa:bb:cc:dd:ee:ff [bd_id]\n\nTo verify run:\n\nvppctl show l2fib verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": ""
+ },
+ {
+ "id": "0a1d6c0d-fe5e-1e1d-2471-96d7a4683e2f",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD/l2-fib-table/l2-fib-entry/11:22:33:44:55:66",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465806064247,
+ "name": "Add L2 FIB entry (static, forward)",
+ "description": "Adds L2 FIB static entry. Corresponds to invoking:\n\nvppctl l2fib add 11:22:33:44:55:66 [bd_id] local0 static\n\nTo verify run:\n\nvppctl show l2fib verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\n \"l2-fib-entry\": [\n {\n \"phys-address\": \"11:22:33:44:55:66\",\n \"outgoing-interface\": \"local0\",\n \"static-config\": \"true\",\n \"action\": \"l2-fib-forward\"\n }\n ]\n}"
+ },
+ {
+ "id": "d752fed9-127a-9289-71f3-90ba2b459d77",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/v3po:vpp/bridge-domains/bridge-domain/testBD/l2-fib-table/l2-fib-entry/00:01:02:03:04:05",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465814845273,
+ "name": "Add L2 FIB entry (filter)",
+ "description": "Adds L2 FIB static entry. Corresponds to invoking:\n\nvppctl l2fib add 00:01:02:03:04:05 [bd_id] filter\n\nTo verify run:\n\nvppctl show l2fib verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\n \"l2-fib-entry\": [\n {\n \"phys-address\": \"00:01:02:03:04:05\",\n \"outgoing-interface\": \"local0\",\n \"static-config\": \"true\",\n \"action\": \"l2-fib-filter\"\n }\n ]\n}"
+ },
+ {
+ "id": "5c800144-cf89-7b77-1dd2-f488101ca441",
+ "headers": "Content-Type: application/json\nAuthorization: Basic YWRtaW46YWRtaW4=\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/v3po:l2",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465984375972,
+ "name": "Remove local0 from bridge domain",
+ "description": "Removes l2 interconnection of bridge-based type from local0 interface.\nCorresponds to the following VAT command:\n\nvat# sw_interface_set_l2_bridge sw_if_index 0 bd_id 1 disable\n\nVerification in VAT:\n\nvat# bridge_domain_dump",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": ""
+ },
+ {
+ "id": "d7cb3d97-fc40-f471-33f3-07aad557777d",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1467377260818,
+ "name": "Get classify tables - cfg",
+ "description": "Shows classify table configuration.",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": ""
+ },
+ {
+ "id": "c067f721-6b1c-f205-799d-720b242f8592",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/operational/vpp-classifier:vpp-classifier-state/",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1467377066230,
+ "name": "Get classify tables - oper",
+ "description": "Shows classify tables configured in the VPP.\n\nCorresponds to:\n\nvppctl sh class table verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": ""
+ },
+ {
+ "id": "5ab03c5f-03c5-8d42-bf65-024239049857",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1468493354688,
+ "name": "Add classify table0",
+ "description": "Adds classify table0. Corresponding vpp cli command:\n\nvppctl classify table mask l2 src\n\nTo verify invoke:\n\nvppctl sh class table verbose\n\nor:\n\nvat# classify_table_info table_id 0",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": "{\n \"classify-table\": [\n {\n \"name\": \"table0\",\n \"nbuckets\": \"2\",\n \"memory_size\": \"1048576\",\n \"miss_next\": \"permit\",\n \"mask\": \"00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:00:00:00:00\"\n }\n ]\n}"
+ },
+ {
+ "id": "4f11c937-9562-3377-d03b-a9829b25f688",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/operational/vpp-classifier:vpp-classifier-state/classify-table/table0",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1467378420028,
+ "name": "Get classify table - oper",
+ "description": "Shows classify table 0 operational state.\n\nCorresponds to:\n\nvat# classify_table_info table_id 0",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": ""
+ },
+ {
+ "id": "491ee2b7-88bf-3a71-282d-a16a62996026",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1468493298160,
+ "name": "Add classify table1",
+ "description": "Adds classify table1 chained to classify table0.\n\nCorresponding vpp cli command:\n\nvppctl classify table mask l2 dst next-table 0\n\nTo verify invoke:\n\nvppctl sh class table verbose\n\nor:\n\nvat# classify_table_info table_id 1",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": "{\n \"classify-table\": [\n {\n \"name\": \"table1\",\n \"next_table\": \"table0\",\n \"nbuckets\": \"2\",\n \"memory_size\": \"1048576\",\n \"miss_next\": \"permit\",\n \"mask\": \"ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00\"\n }\n ]\n}"
+ },
+ {
+ "id": "fa63c194-ea7a-51ad-a6d8-d7485ed67f2e",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0/classify-session/00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1468493546163,
+ "name": "Add classify session",
+ "description": "Adds classify session to table0. Corresponding vpp cli command:\n\nvppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:06\n\nTo verify invoke:\n\nvppctl sh class table verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": "{\n \"classify-session\": [\n {\n \"hit_next\": \"deny\",\n \"match\": \"00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00\"\n }\n ]\n}"
+ },
+ {
+ "id": "b9931024-fa86-4e04-e2a4-4feeaa9e99e1",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0/classify-session/00:00:00:00:00:00:01:02:03:04:05:07:00:00:00:00",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1468493583927,
+ "name": "Add another classify session",
+ "description": "Adds second classify session to table0. Corresponding vpp cli command:\n\nvppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:07\n\nTo verify invoke:\n\nvppctl sh class table verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": "{\n \"classify-session\": [\n {\n \"hit_next\": \"deny\",\n \"match\": \"00:00:00:00:00:00:01:02:03:04:05:07:00:00:00:00\"\n }\n ]\n}"
+ },
+ {
+ "id": "d39e4c39-90b6-072c-44b8-d909cac53a5b",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/v3po:acl",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1468495690154,
+ "name": "Enable L2 and Ipv4 ACL on local0 interface",
+ "description": "Enables L2 and Ipv4 ACL on local0. Corresponding vpp cli commands:\n\nset int input acl intfc local0 l2-table 0\n\nset int input acl intfc local0 ip4-table 0\n\nTo verify invoke:\n\nvppctl show inacl type l2\n\nthen:\n\nvppctl show inacl type ip4",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": "{\n \"v3po:acl\": {\n \"l2-acl\": {\n \"classify-table\": \"table0\"\n },\n \"ip4-acl\": {\n \"classify-table\": \"table0\"\n }\n }\n}"
+ },
+ {
+ "id": "2ce36b98-d7a9-fe37-80f6-f3a4abf25103",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/v3po:acl/",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1467380189765,
+ "name": "Disable ACL on local0 interface",
+ "description": "Disables ACL on local0. To verify invoke:\n\nvppctl show inacl type l2\n\nand:\n\nvppctl show inacl type ip4",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": ""
+ },
+ {
+ "id": "d0984799-d321-d1e0-a855-cc434519bef0",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0/classify-session/00:00:00:00:00:00:01:02:03:04:05:06:00:00:00:00",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1468493934488,
+ "name": "Remove classify session",
+ "description": "Removes classify session. To verify invoke:\n\nvppctl sh class table verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": ""
+ },
+ {
+ "id": "be223323-bf59-9c4f-f896-ff74c03b01a1",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table0",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1467377830897,
+ "name": "Remove classify table0",
+ "description": "Removes classify table0. Corresponds to the following command:\n\nvppctl classify table del table 0\n\nTo verify invoke:\n\nvppctl sh class table verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": ""
+ },
+ {
+ "id": "ee11f1c0-a1fe-51a6-a5e5-fcdcfc2b620a",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/vpp-classifier:vpp-classifier/classify-table/table1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1467377388428,
+ "name": "Remove classify table1",
+ "description": "Removes classify table1. Corresponds to the following command:\n\nvppctl classify table del table 1\n\nTo verify invoke:\n\nvppctl sh class table verbose",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "f91a6422-9724-e504-a94c-c5a953315b6f",
+ "rawModeData": ""
+ },
+ {
+ "id": "3f9588e4-885f-3792-bdf4-d0f10704ae4d",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/tapp",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1462535811974,
+ "name": "Add simple tap ifc -cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "60596aab-a4f1-bb64-d701-816de9482201",
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"tapp\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:tap\",\r\n \"tap\" :{\r\n \"tap-name\" : \"tapp\"\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "3fccc4cd-f14b-0333-83d5-924afe3938e4",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1460640004531,
+ "name": "Set vrf id for local0 - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"local0\",\r\n \"description\": \"for testing purposes only\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"routing\" : {\r\n \"vrf-id\" : \"7\"\r\n },\r\n \"v3po:ethernet\": {\r\n \"mtu\": 64\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "44de3eff-ace5-90a9-dd16-af3f5f13daad",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2/rewrite",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464805188780,
+ "name": "Enable tag-rewrite translate 1-2 - cfg",
+ "description": "Enables tag-rewrite translate 1-2 operation for GigabitEthernet0/9/0.1 sub-interface. Corresponsing vpp cli command:\n\nvppctl set interface l2 tag-rewrite GigabitEthernet0/9/0.1 translate 1-2 dot1q 111 222\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump\n\nor if sub-interface was added to bridge domain:\n\nvppctl show bridge-domain [bd_id] detail",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "rawModeData": "{\r\n \"rewrite\": {\r\n \"vlan-type\": \"vpp-vlan:802dot1q\",\r\n \"pop-tags\": \"1\",\r\n \"push-tags\": [\r\n {\r\n \"index\": 0,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": 111\r\n }\r\n },\r\n {\r\n \"index\": 1,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": 222\r\n }\r\n }\r\n ]\r\n }\r\n}"
+ },
+ {
+ "id": "4db9b360-0d57-6ce6-7e3c-a6acfe17d512",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/tapp2",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1462796801468,
+ "name": "Modify complex tap ifc - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"tapp2\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:tap\",\r\n \"tap\" :{\r\n \"tap-name\" : \"tapp2\",\r\n \"mac\" : \"00:ff:ff:ff:ff:ae\",\r\n \"device-instance\" : 77\r\n }\r\n }\r\n ]\r\n \r\n}",
+ "folder": "60596aab-a4f1-bb64-d701-816de9482201"
+ },
+ {
+ "id": "4f15d0e9-8530-157e-0fe8-d24034bc31df",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vxlanTun1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": "{}",
+ "time": 1462892071867,
+ "name": "Delete virtual ifc - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "3868e66d-0ee5-bd4d-6b35-075c4841b5c1",
+ "timestamp": null,
+ "rawModeData": ""
+ },
+ {
+ "id": "55102dbd-1fc9-5f91-2082-40a2dc311f4f",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vhost1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": "{}",
+ "time": 1462891488523,
+ "name": "Delete vhost user ifc - cfg",
+ "description": "Deletes vhost user interface.\nCorresponding vpp CLI command:\n\nvppctl delete vhost-user sw_if_index [index]\n\nTo verify invoke:\n\nvppctl show vhost-user\n\nor\n\nvppctl show int",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "c97b9ad9-64e6-5de3-09b8-851c1189d767",
+ "rawModeData": "",
+ "descriptionFormat": null
+ },
+ {
+ "id": "5995dcf7-51c0-42ef-448b-a70bb68735d5",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/tapp2",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1462796994090,
+ "name": "Delete complex tap ifc - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "",
+ "folder": "60596aab-a4f1-bb64-d701-816de9482201"
+ },
+ {
+ "id": "5dc1d9f4-e7ae-bb21-e558-8a56ac825922",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/operational/v3po:vpp-state",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1460539994330,
+ "name": "Read vpp-state - oper",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "7f312f15-a81e-b513-83b7-a5f7755eae67",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vhost1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1462891472552,
+ "name": "Add vhost user ifc - cfg",
+ "description": "Adds vhost-user interface.\nCorresponsing vpp cli command:\n\ncreate vhost-user socket /tmp/soc1 server\n\nTo verify invoke:\n\nvppctl show vhost-user\n\nor\n\nvppctl show int",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"vhost1\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:vhost-user\",\r\n \"enabled\": \"true\",\r\n \"vhost-user\" : {\r\n \"socket\": \"/tmp/soc1\",\r\n \"role\": \"server\"\r\n }\r\n }\r\n ]\r\n \r\n}",
+ "folder": "c97b9ad9-64e6-5de3-09b8-851c1189d767"
+ },
+ {
+ "id": "8b339568-b60a-715f-d4fd-416bafe981a9",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2/rewrite",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": "{}",
+ "time": 1464803940412,
+ "name": "Enable tag-rewrite push - cfg",
+ "description": "Enables tag-rewrite push operation for GigabitEthernet0/9/0.1 sub-interface. Corresponsing vpp cli command:\n\nvppctl set interface l2 tag-rewrite GigabitEthernet0/9/0.1 push dot1q 123 456\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump\n\nor if sub-interface was added to bridge domain:\n\nvppctl show bridge-domain [bd_id] detail",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "timestamp": null,
+ "rawModeData": "{\r\n \"rewrite\": {\r\n \"vlan-type\": \"vpp-vlan:802dot1q\",\r\n \"push-tags\": [\r\n {\r\n \"index\": 0,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": 123\r\n }\r\n },\r\n {\r\n \"index\": 1,\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": 456\r\n }\r\n }\r\n ]\r\n }\r\n}"
+ },
+ {
+ "id": "8b867c90-459f-68f6-4b2e-fa653098c28d",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1460540047356,
+ "name": "Enable local0 interface - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"local0\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"v3po:ethernet\": {\r\n \"mtu\": 64\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "93022e97-24fa-5784-1e59-94674817215f",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/operational/naming-context:contexts",
+ "pathVariables": {},
+ "preRequestScript": "",
+ "method": "GET",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "data": [],
+ "dataMode": "raw",
+ "name": "List naming contexts - context",
+ "description": "List mapping context stored in context datastore",
+ "descriptionFormat": "html",
+ "time": 1463556756647,
+ "version": 2,
+ "responses": [],
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "69a05849-026d-f120-5c6a-f3e2b0884d88",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/operational/ietf-interfaces:interfaces-state/interface/local0",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465472429324,
+ "name": "Read local0 - oper",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "7a914134-23ea-3154-1557-d29dc8d464e7",
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "b10169c2-ae0e-bb1c-3dea-ca06839c6c9c",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/ipv4/address/127.0.0.1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1466496859574,
+ "name": "Set ipv4 local0 interface - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "7a914134-23ea-3154-1557-d29dc8d464e7",
+ "rawModeData": "{\r\n \"address\": [{\r\n \"ip\" : \"127.0.0.1\",\r\n \"prefix-length\" : \"24\"\r\n }]\r\n}"
+ },
+ {
+ "id": "8d49cf41-facc-c4ac-cee7-475d50e2a0be",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/ipv4/address/127.0.0.1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1466497187475,
+ "name": "Remove ipv4 from local0 interface - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "7a914134-23ea-3154-1557-d29dc8d464e7",
+ "rawModeData": ""
+ },
+ {
+ "id": "70835949-7252-a3cc-491b-f8aa69286399",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/ipv4/address/127.0.0.1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1466497176571,
+ "name": "Set ipv4 local0 interface - cfg netmask",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "7a914134-23ea-3154-1557-d29dc8d464e7",
+ "rawModeData": "{\r\n \"address\": [{\r\n \"ip\" : \"127.0.0.1\",\r\n \"netmask\": \"255.255.255.128\"\r\n }]\r\n}"
+ },
+ {
+ "id": "d5a90cfd-fd7e-63b6-e1f1-16fc0671b0e1",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/operational/ietf-interfaces:interfaces-state/interface/local0/ipv4",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1465473594194,
+ "name": "Read local0/ipv4 - oper",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "7a914134-23ea-3154-1557-d29dc8d464e7",
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "3f7ee49b-ae11-b032-915f-a14bf838246f",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/local0/ipv4",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1466497212835,
+ "name": "Read local0/ipv4 - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "7a914134-23ea-3154-1557-d29dc8d464e7",
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "345ba26c-60cf-5fa1-37d0-f5af7a8c33bf",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/ipv4/address/1.2.3.4",
+ "pathVariables": {},
+ "preRequestScript": "",
+ "method": "PUT",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "data": [],
+ "dataMode": "raw",
+ "name": "Set ipv4 for sub-interface - cfg",
+ "description": "Correponds to the following command:\n\nvppctl set interface ip address GigabitEthernet0/9/0.1 1.2.3.4/8\n\nTo verify invoke:\nvppctl show int",
+ "descriptionFormat": "html",
+ "time": 1467117222646,
+ "version": 2,
+ "responses": [],
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "folder": "7a914134-23ea-3154-1557-d29dc8d464e7",
+ "rawModeData": "{\r\n \"address\": [{\r\n \"ip\" : \"1.2.3.4\",\r\n \"prefix-length\" : \"8\"\r\n }]\r\n}"
+ },
+ {
+ "id": "ac161bec-e046-4bb7-d695-60bdfb0c6cea",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vhost1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1462891483790,
+ "name": "Modify vhost user ifc - cfg",
+ "description": "Modifies vhost-user interface socket.\nTo verify invoke:\n\nvppctl show vhost-user",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"vhost1\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:vhost-user\",\r\n \"enabled\": \"true\",\r\n \"vhost-user\" : {\r\n \"socket\": \"/tmp/soc2\",\r\n \"role\": \"server\"\r\n }\r\n }\r\n ]\r\n \r\n}",
+ "folder": "c97b9ad9-64e6-5de3-09b8-851c1189d767"
+ },
+ {
+ "id": "b08a0677-6b59-74ea-e5c6-7383194b19dd",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2/rewrite",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464804511683,
+ "name": "Disable tag-rewrite - cfg",
+ "description": "Disables tag-rewrite operation for GigabitEthernet0/9/0.1 sub-interface. Corresponsing vpp cli command:\n\nvppctl set interface l2 tag-rewrite GigabitEthernet0/9/0.1 disable\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump\n\nor if sub-interface was added to bridge domain:\n\nvppctl show bridge-domain [bd_id] detail",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "rawModeData": ""
+ },
+ {
+ "id": "bd2897c6-5fc7-bec5-87be-4fd52bb8e471",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2/rewrite",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464805111410,
+ "name": "Enable tag-rewrite pop 1 - cfg",
+ "description": "Enables tag-rewrite pop 1 operation for GigabitEthernet0/9/0.1 sub-interface. Corresponsing vpp cli command:\n\nvppctl set interface l2 tag-rewrite GigabitEthernet0/9/0.1 pop 1\n\nTo verify run:\n./build-root/install-vpp-native/vpp-api-test/bin/vpp_api_test json\nand invoke:\n\n#vat sw_interface_dump\n\nor if sub-interface was added to bridge domain:\n\nvppctl show bridge-domain [bd_id] detail",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "rawModeData": "{\r\n \"rewrite\": {\r\n \"pop-tags\": \"1\"\r\n }\r\n}"
+ },
+ {
+ "id": "cbb77318-52e0-5647-cd1c-8c679ea7b830",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1/l2",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464813539029,
+ "name": "Add sub interface to bridge-domain",
+ "description": "Adds sub interface to bridge domain. Corresponsing vpp cli command:\n\nvppctl set interface l2 bridge GigabitEthernet0/9/0.1 1 1\n\nTo verify invoke:\nvppctl show bridge-domain 1 detail",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "rawModeData": "{\r\n \"l2\": {\r\n \"bridge-domain\": \"testBD\",\r\n \"split-horizon-group\": 1,\r\n \"bridged-virtual-interface\": \"false\"\r\n }\r\n}"
+ },
+ {
+ "id": "d27322b8-59d5-a2dc-a7a2-8f7197057164",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/operational/ietf-interfaces:interfaces-state/",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1460539972177,
+ "name": "List ifcs - oper",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"testInterface\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"iana-if-type:ethernetCsmacd\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"ietf-ip:ipv4\": {\r\n \"enabled\": \"true\",\r\n \"mtu\": \"1500\",\r\n \"address\": [\r\n {\r\n \"ip\": \"1.2.3.0\",\r\n \"netmask\": \"255.255.255.0\"\r\n }\r\n ]\r\n }\r\n }\r\n ]\r\n \r\n}"
+ },
+ {
+ "id": "e611d23a-c205-192b-e8f9-dd99fa169274",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1460636113690,
+ "name": "Set interfaces bulk edit - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\n \"interfaces\": {\n \"interface\": [\n {\n \"name\": \"pg/stream-2\",\n \"enabled\": true,\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n {\n \"name\": \"pg/stream-3\",\n \"enabled\": true,\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n {\n \"name\": \"pg/stream-0\",\n \"enabled\": true,\n \"type\": \"iana-if-type:ethernetCsmacd\"\n },\n {\n \"name\": \"local0\",\n \"description\": \"for testing purposes\",\n \"type\": \"iana-if-type:ethernetCsmacd\",\n \"enabled\": true\n },\n {\n \"name\": \"pg/stream-1\",\n \"enabled\": true,\n \"type\": \"iana-if-type:ethernetCsmacd\"\n }\n ]\n }\n}"
+ },
+ {
+ "id": "f014ab21-8e40-c344-a68b-0f1b86578bd8",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/vxlanTun1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464767860505,
+ "name": "Add virtual ifc - cfg",
+ "description": "",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "rawModeData": "{\r\n \r\n \"interface\": [\r\n {\r\n \"name\": \"vxlanTun1\",\r\n \"description\": \"for testing purposes\",\r\n \"type\": \"v3po:vxlan-tunnel\",\r\n \"enabled\": \"true\",\r\n \"link-up-down-trap-enable\": \"enabled\",\r\n \"routing\" : {\r\n \"vrf-id\" : \"0\"\r\n },\r\n \"vxlan\" : {\r\n \"src\" : \"192.168.1.6\",\r\n \"dst\" : \"192.168.1.9\",\r\n \"vni\" : \"88\",\r\n \"encap-vrf-id\" : \"0\"\r\n }\r\n }\r\n ]\r\n \r\n}",
+ "folder": "3868e66d-0ee5-bd4d-6b35-075c4841b5c1"
+ },
+ {
+ "id": "f2e9a3cb-f3cc-f501-6015-8ebf7d8b2c3e",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://localhost:8181/restconf/config/ietf-interfaces:interfaces/interface/GigabitEthernet0%2F9%2F0/vpp-vlan:sub-interfaces/sub-interface/1",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1464804333865,
+ "name": "Enable GigabitEthernet0/9/0.1 interface - cfg",
+ "description": "Enables GigabitEthernet0/9/0.1 sub interface. Equivalent vppctl command:\n\nvppctl set in state GigabitEthernet0/9/0.1 up\n\nTo enable sub interface, super interface should be enabled first.\n\nTo verify invoke:\n\nvppctl show int",
+ "collectionId": "7c35192d-9085-20f6-9fcd-3f8570aaefd7",
+ "responses": [],
+ "folder": "37fd9569-06e3-1f08-fbcc-4b1462107a32",
+ "rawModeData": "{\r\n \"sub-interface\": [\r\n {\r\n \"identifier\": \"1\",\r\n \"vlan-type\": \"802dot1q\",\r\n \"tags\": {\r\n \"tag\": [\r\n {\r\n \"index\": \"0\",\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:s-vlan\",\r\n \"vlan-id\": \"100\"\r\n }\r\n },\r\n {\r\n \"index\": \"1\",\r\n \"dot1q-tag\": {\r\n \"tag-type\": \"dot1q-types:c-vlan\",\r\n \"vlan-id\": \"any\"\r\n }\r\n }\r\n ]\r\n },\r\n \"match\": {\r\n \"vlan-tagged\": {\r\n \"match-exact-tags\": \"true\"\r\n }\r\n },\r\n \"enabled\": \"true\"\r\n }\r\n ]\r\n}"
+ }
+ ]
+}
diff --git a/infra/translate-api/Readme.adoc b/infra/translate-api/Readme.adoc
new file mode 100644
index 000000000..9e208744a
--- /dev/null
+++ b/infra/translate-api/Readme.adoc
@@ -0,0 +1,9 @@
+= Honeycomb translation layer API
+
+Extensible API for translation between Binding Aware data and actual device data.
+Consists of readers and writers responsible for communication with the device.
+
+Provides registry of readers and writers for the data layer.
+For every supported YANG model there should be at least one reader and writer registered.
+Readers and writers form two tree structures matching corresponding YANG models.
+Readers and writers can be attached to any non-leaf YANG node. \ No newline at end of file
diff --git a/infra/translate-api/pom.xml b/infra/translate-api/pom.xml
new file mode 100644
index 000000000..45f00fe19
--- /dev/null
+++ b/infra/translate-api/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>impl-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>translate-api</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-artifacts</artifactId>
+ <version>2.0.2-Beryllium-SR2</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-api</artifactId>
+ </dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java
new file mode 100644
index 000000000..cff766e2a
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/MappingContext.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate;
+
+import com.google.common.base.Optional;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Mapping context is persisted storage where mapping matadata are stored.
+ * A snapshot is created for each transaction to provide consistent view over context data.
+ * After a transaction is successfully finished, objects added to this context are propagated to backing storage.
+ */
+public interface MappingContext extends AutoCloseable {
+
+ /**
+ * Read any mapping context data
+ *
+ * @param currentId Id of an object to read
+ *
+ * @return Relevant mapping context data
+ */
+ <T extends DataObject> Optional<T> read(@Nonnull final InstanceIdentifier<T> currentId);
+
+ /**
+ * Delete the node at specified path.
+ *
+ * @param path Node path
+ */
+ void delete(InstanceIdentifier<?> path);
+
+ /**
+ * Merge the specified data with the currently-present data
+ * at specified path.
+ *
+ * @param path Node path
+ * @param data Data to be merged
+ */
+ <T extends DataObject> void merge(InstanceIdentifier<T> path, T data);
+
+ /**
+ * Replace the data at specified path with supplied data.
+ *
+ * @param path Node path
+ * @param data New node data
+ */
+ <T extends DataObject> void put(InstanceIdentifier<T> path, T data);
+
+ @Override
+ void close();
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModifiableSubtreeManagerRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModifiableSubtreeManagerRegistryBuilder.java
new file mode 100644
index 000000000..591a9e9bb
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModifiableSubtreeManagerRegistryBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate;
+
+import java.util.Collection;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Registry builder where {@link SubtreeManager}s can be added with or without relationships between them.
+ * The relationships express what the order of execution should be.
+ */
+public interface ModifiableSubtreeManagerRegistryBuilder<S extends SubtreeManager<? extends DataObject>> {
+
+ /**
+ * Add a handler responsible for writing only a single complex node.
+ */
+ ModifiableSubtreeManagerRegistryBuilder<S> add(@Nonnull S handler);
+
+ /**
+ * Add a handler responsible for writing multiple complex nodes within a subtree its responsible for. Identifiers for
+ * subtree nodes handled by a single handler have to be relative from {@link DataObject} that represents subtree
+ * root.
+ */
+ ModifiableSubtreeManagerRegistryBuilder<S> subtreeAdd(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull S handler);
+
+ /**
+ * Add a handler and make sure it will be executed before handler identifier by relatedType is executed.
+ */
+ ModifiableSubtreeManagerRegistryBuilder<S> addBefore(@Nonnull S handler,
+ @Nonnull InstanceIdentifier<?> relatedType);
+
+ ModifiableSubtreeManagerRegistryBuilder<S> addBefore(@Nonnull S handler,
+ @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+
+ ModifiableSubtreeManagerRegistryBuilder<S> subtreeAddBefore(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull S handler,
+ @Nonnull InstanceIdentifier<?> relatedType);
+
+ ModifiableSubtreeManagerRegistryBuilder<S> subtreeAddBefore(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull S handler,
+ @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+
+ /**
+ * Add a handler and make sure it will be executed after handler identifier by relatedType is executed.
+ */
+ ModifiableSubtreeManagerRegistryBuilder<S> addAfter(@Nonnull S handler,
+ @Nonnull InstanceIdentifier<?> relatedType);
+
+ ModifiableSubtreeManagerRegistryBuilder<S> addAfter(@Nonnull S handler,
+ @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+
+ ModifiableSubtreeManagerRegistryBuilder<S> subtreeAddAfter(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull S handler,
+ @Nonnull InstanceIdentifier<?> relatedType);
+
+ ModifiableSubtreeManagerRegistryBuilder<S> subtreeAddAfter(@Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull S handler,
+ @Nonnull Collection<InstanceIdentifier<?>> relatedTypes);
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java
new file mode 100644
index 000000000..cb2d4fde0
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationCache.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate;
+
+import com.google.common.collect.Maps;
+import java.util.HashMap;
+
+/**
+ * Simple context class that provides transient storage during one or more read/write operations
+ */
+public class ModificationCache implements AutoCloseable {
+
+ protected final HashMap<Object, Object> map;
+
+ public ModificationCache() {
+ map = Maps.newHashMap();
+ }
+
+ public Object get(final Object o) {
+ return map.get(o);
+ }
+
+ public boolean containsKey(final Object o) {
+ return map.containsKey(o);
+ }
+
+ public Object put(final Object o, final Object o2) {
+ return map.put(o, o2);
+ }
+
+ @Override
+ public void close() {
+ map.clear();
+ }
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java
new file mode 100644
index 000000000..2c039aba0
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/ModificationContext.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Common context for both writes and reads
+ */
+public interface ModificationContext extends AutoCloseable {
+
+ /**
+ * Get key value transient storage for customizers. Is cleared for each new transaction.
+ *
+ * @return Context for customizers
+ */
+ @Nonnull
+ ModificationCache getModificationCache();
+
+ /**
+ * Get persistent storage for mapping context. This context survives a modification.
+ *
+ * @return Mapping context accessor
+ */
+ @Nonnull
+ MappingContext getMappingContext();
+
+ @Override
+ void close();
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManager.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManager.java
new file mode 100644
index 000000000..40842763d
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManager.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Base identifiable subtree manager(reader, writer etc.)
+ *
+ * @param <D> Specific DataObject derived type, that is managed by this manager
+ */
+@Beta
+public interface SubtreeManager<D extends DataObject> {
+
+ /**
+ * Gets the type of node managed by this reader.
+ *
+ * @return Absolute instance identifier for managed type
+ */
+ @Nonnull
+ InstanceIdentifier<D> getManagedDataObjectType();
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManagerRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManagerRegistryBuilder.java
new file mode 100644
index 000000000..2434deddb
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/SubtreeManagerRegistryBuilder.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate;
+
+public interface SubtreeManagerRegistryBuilder<R> {
+
+ R build();
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/TranslationException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/TranslationException.java
new file mode 100644
index 000000000..ee0049977
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/TranslationException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Base exception for the translation layer
+ */
+@Beta
+public class TranslationException extends Exception {
+
+ public TranslationException(final String s) {
+ super(s);
+ }
+
+ public TranslationException(final String s, final Throwable cause) {
+ super(s, cause);
+ }
+
+ public TranslationException(final Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ListReader.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ListReader.java
new file mode 100644
index 000000000..13a7a55da
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ListReader.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.read;
+
+import com.google.common.annotations.Beta;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * List reader, allowing read of all the elements.
+ *
+ * @param <D> Specific DataObject derived type, that is handled by this reader
+ */
+@Beta
+public interface ListReader
+ <D extends DataObject & Identifiable<K>, K extends Identifier<D>, B extends Builder<D>> extends Reader<D, B> {
+
+ /**
+ * Read all elements in this list.
+ *
+ * @param id Wildcarded identifier of list managed by this reader
+ * @param ctx Read context
+ *
+ * @return List of all entries in this list
+ * @throws ReadFailedException if read was unsuccessful
+ */
+ @Nonnull
+ List<D> readList(@Nonnull final InstanceIdentifier<D> id, @Nonnull final ReadContext ctx)
+ throws ReadFailedException;
+
+ /**
+ * Get IDs for all entries in the list.
+ */
+ List<K> getAllIds(@Nonnull InstanceIdentifier<D> id, @Nonnull ReadContext ctx)
+ throws ReadFailedException;
+
+ /**
+ * Merge read data into provided parent builder.
+ */
+ void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<D> readData);
+
+ @Override
+ default void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue) {
+ merge(parentBuilder, Collections.singletonList(readValue));
+ }
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java
new file mode 100644
index 000000000..e3ddd420c
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadContext.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.read;
+
+import io.fd.honeycomb.v3po.translate.ModificationContext;
+
+/**
+ * Context providing information about current state of DataTree to readers
+ */
+public interface ReadContext extends ModificationContext {
+
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadFailedException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadFailedException.java
new file mode 100644
index 000000000..51d4fcb5a
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReadFailedException.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.read;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Thrown when a reader or customizer is not able to read data for the given id.
+ */
+public class ReadFailedException extends TranslationException {
+
+ private final InstanceIdentifier<?> failedId;
+
+ /**
+ * Constructs an ReadFailedException given data id and exception cause.
+ *
+ * @param failedId instance identifier of the data object that could not be read
+ * @param cause the cause of read failure
+ */
+ public ReadFailedException(@Nonnull final InstanceIdentifier<?> failedId, final Throwable cause) {
+ super("Failed to read " + failedId, cause);
+ this.failedId = checkNotNull(failedId, "failedId should not be null");
+ }
+
+ /**
+ * Constructs an ReadFailedException given data id.
+ *
+ * @param failedId instance identifier of the data object that could not be read
+ */
+ public ReadFailedException(@Nonnull final InstanceIdentifier<?> failedId) {
+ this(failedId, null);
+ }
+
+ /**
+ * Returns id of the data object that could not be read.
+ *
+ * @return data object instance identifier
+ */
+ @Nonnull
+ public InstanceIdentifier<?> getFailedId() {
+ return failedId;
+ }
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/Reader.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/Reader.java
new file mode 100644
index 000000000..d0bf0dea1
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/Reader.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.read;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.SubtreeManager;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Base reader, responsible for translation between DataObjects and any other side.
+ *
+ * @param <D> Specific DataObject derived type, that is handled by this reader
+ */
+@Beta
+public interface Reader<D extends DataObject, B extends Builder<D>> extends SubtreeManager<D> {
+
+ // TODO make async
+
+ /**
+ * Reads data identified by id
+ *
+ * @param id unique identifier of subtree to be read. The subtree must contain managed data object type. For
+ * identifiers pointing below node managed by this reader, it's reader's responsibility to filter out the
+ * right node or to delegate the read to a child reader.
+ * @param ctx Read context
+ *
+ * @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list
+ * @throws ReadFailedException if read was unsuccessful
+ */
+ @Nonnull
+ Optional<? extends DataObject> read(@Nonnull InstanceIdentifier<? extends DataObject> id,
+ @Nonnull ReadContext ctx) throws ReadFailedException;
+
+ /**
+ * Fill in current node's attributes
+ *
+ * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present.
+ * @param builder Builder object for current node where the read attributes must be placed
+ * @param ctx Current read context
+ */
+ void readCurrentAttributes(@Nonnull InstanceIdentifier<D> id,
+ @Nonnull B builder,
+ @Nonnull ReadContext ctx) throws ReadFailedException;
+
+ /**
+ * Return new instance of a builder object for current node
+ *
+ * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present.
+ * @return Builder object for current node type
+ */
+ @Nonnull
+ B getBuilder(InstanceIdentifier<D> id);
+
+ /**
+ * Merge read data into provided parent builder.
+ */
+ void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue);
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReaderFactory.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReaderFactory.java
new file mode 100644
index 000000000..61472f8d2
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/ReaderFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.read;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder;
+import javax.annotation.Nonnull;
+
+/**
+ * Factory producing readers for {@link ModifiableReaderRegistryBuilder}.
+ */
+@Beta
+public interface ReaderFactory extends AutoCloseable {
+
+ /**
+ * Initialize 1 or more readers and add them to provided registry.
+ */
+ void init(@Nonnull ModifiableReaderRegistryBuilder registry);
+
+ @Override
+ default void close() {
+ // NOOP TODO allow unregister
+ }
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ModifiableReaderRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ModifiableReaderRegistryBuilder.java
new file mode 100644
index 000000000..c2eba4f72
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ModifiableReaderRegistryBuilder.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.read.registry;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.ModifiableSubtreeManagerRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Mutable registry that allows adding new readers.
+ */
+@Beta
+public interface ModifiableReaderRegistryBuilder
+ extends ModifiableSubtreeManagerRegistryBuilder<Reader<? extends DataObject, ? extends Builder<?>>> {
+
+ // TODO we should be able to add structural/reflexive readers automatically in the registry builder, we just need builder class
+ // We would need generated class loading strategy instance and then load builder classes relying on naming + package conventions of Binding spec
+ /**
+ * Add a structural reader that performs no read operation on its own, just fills in the hierarchy.
+ */
+ <D extends DataObject> void addStructuralReader(@Nonnull InstanceIdentifier<D> id,
+ @Nonnull Class<? extends Builder<D>> builderType);
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistry.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistry.java
new file mode 100644
index 000000000..308ec238d
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistry.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.read.registry;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Simple delegating reader suitable as a holder for all other root readers, providing readAll feature.
+ */
+@Beta
+public interface ReaderRegistry {
+
+ /**
+ * Performs read on all registered root readers and merges the results into a Multimap. Keys represent identifiers
+ * for root DataObjects from the data tree modeled by YANG.
+ *
+ * @param ctx Read context
+ *
+ * @return multimap that preserves deterministic iteration order across non-distinct key values
+ * @throws ReadFailedException if read was unsuccessful
+ */
+ @Nonnull
+ Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll(@Nonnull final ReadContext ctx)
+ throws ReadFailedException;
+
+ /**
+ * Reads data identified by id.
+ *
+ * @param id unique identifier of subtree to be read. The subtree must contain managed data object type. For
+ * identifiers pointing below node managed by this reader, it's reader's responsibility to filter out the
+ * right node or to delegate the read to a child reader.
+ * @param ctx Read context
+ *
+ * @return List of DataObjects identified by id. If the ID points to a single node, it will be wrapped in a list
+ * @throws ReadFailedException if read was unsuccessful
+ */
+ @Nonnull
+ Optional<? extends DataObject> read(@Nonnull InstanceIdentifier<? extends DataObject> id,
+ @Nonnull ReadContext ctx)
+ throws ReadFailedException;
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistryBuilder.java
new file mode 100644
index 000000000..ff95d6248
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/read/registry/ReaderRegistryBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.read.registry;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.SubtreeManagerRegistryBuilder;
+
+/**
+ * Builder for reader registries.
+ */
+@Beta
+public interface ReaderRegistryBuilder extends SubtreeManagerRegistryBuilder<ReaderRegistry> {
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java
new file mode 100644
index 000000000..0d891ecba
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/DataObjectUpdate.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Simple wrapper for BA id + data before and after state. Does not allow both before and after to be null.
+ */
+public class DataObjectUpdate {
+
+ @Nonnull
+ private final InstanceIdentifier<?> id;
+ @Nullable
+ private final DataObject dataBefore;
+ @Nullable
+ private final DataObject dataAfter;
+
+ private DataObjectUpdate(@Nonnull final InstanceIdentifier<?> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter) {
+ this.id = checkNotNull(id);
+ this.dataAfter = dataAfter;
+ this.dataBefore = dataBefore;
+ }
+
+ public DataObject getDataBefore() {
+ return dataBefore;
+ }
+
+ public DataObject getDataAfter() {
+ return dataAfter;
+ }
+
+ public InstanceIdentifier<?> getId() {
+ return id;
+ }
+
+ public static DataObjectUpdate create(@Nonnull final InstanceIdentifier<?> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter) {
+ checkArgument(!(dataBefore == null && dataAfter == null), "Both before and after data are null");
+ if (dataBefore != null) {
+ checkArgument(id.getTargetType().isAssignableFrom(dataBefore.getClass()));
+ }
+ if (dataAfter != null) {
+ checkArgument(id.getTargetType().isAssignableFrom(dataAfter.getClass()));
+ }
+
+ return dataAfter == null
+ ? new DataObjectDelete(id, dataBefore)
+ : new DataObjectUpdate(id, dataBefore, dataAfter);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DataObjectUpdate that = (DataObjectUpdate) o;
+
+ return id.equals(that.id);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "DataObjectUpdate{" + "id=" + id
+ + ", dataBefore=" + dataBefore
+ + ", dataAfter=" + dataAfter
+ + '}';
+ }
+
+ public DataObjectUpdate reverse() {
+ return DataObjectUpdate.create(id, dataAfter, dataBefore);
+ }
+
+ public static class DataObjectDelete extends DataObjectUpdate {
+
+ private DataObjectDelete(@Nonnull final InstanceIdentifier<?> id,
+ @Nullable final DataObject dataBefore) {
+ super(id, dataBefore, null);
+ }
+ }
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ListWriter.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ListWriter.java
new file mode 100644
index 000000000..f29289d2d
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/ListWriter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+
+/**
+ * List writer, responsible for translation between a list of DataObjects and any other side.
+ * Handling all update operations(create, update, delete)
+ *
+ * @param <D> Specific DataObject derived type, that is handled by this writer
+ * @param <K> Identifier/key for D
+ */
+@Beta
+public interface ListWriter<D extends DataObject & Identifiable<K>, K extends Identifier<D>> extends Writer<D> {
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java
new file mode 100644
index 000000000..3433b3f34
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteContext.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.ModificationContext;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Context providing information about current state of DataTree to writers
+ */
+@Beta
+public interface WriteContext extends ModificationContext {
+
+ /**
+ * Read any config data object before current modification was applied
+ *
+ * @param currentId Id of an object to read
+ *
+ * @return Data before the modification was applied
+ */
+ <T extends DataObject> Optional<T> readBefore(@Nonnull final InstanceIdentifier<T> currentId);
+
+ /**
+ * Read any config data object from current modification
+ *
+ * @param currentId Id of an object to read
+ *
+ * @return Data from the modification
+ */
+ <T extends DataObject> Optional<T> readAfter(@Nonnull final InstanceIdentifier<T> currentId);
+
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteFailedException.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteFailedException.java
new file mode 100644
index 000000000..10a664fcf
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriteFailedException.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Thrown when a writer or customizer is not able to write/update/delete data .
+ */
+public class WriteFailedException extends TranslationException {
+
+ private final InstanceIdentifier<?> failedId;
+
+ /**
+ * Constructs an WriteFailedException given data id, exception detail message and exception cause.
+ *
+ * @param failedId instance identifier of the data object that could not be read
+ * @param cause the cause of read failure
+ * @param message the exception detail message
+ */
+ public WriteFailedException(@Nonnull final InstanceIdentifier<?> failedId,
+ @Nonnull final String message,
+ @Nonnull final Throwable cause) {
+ super(message, cause);
+ this.failedId = checkNotNull(failedId, "failedId should not be null");
+ }
+
+ /**
+ * Constructs an WriteFailedException given data id.
+ *
+ * @param failedId instance identifier of the data object that could not be written
+ */
+ public WriteFailedException(@Nonnull final InstanceIdentifier<?> failedId,
+ @Nonnull final String message) {
+ super(message);
+ this.failedId = checkNotNull(failedId, "failedId should not be null");
+ }
+
+ /**
+ * Constructs an WriteFailedException given data id and exception cause.
+ *
+ * @param failedId instance identifier of the data object that could not be read
+ * @param cause the cause of read failure
+ */
+ public WriteFailedException(@Nonnull final InstanceIdentifier<?> failedId,
+ @Nonnull final Throwable cause) {
+ super(cause);
+ this.failedId = checkNotNull(failedId, "failedId should not be null");
+ }
+
+ /**
+ * Returns id of the data object that could not be written.
+ *
+ * @return data object instance identifier
+ */
+ @Nonnull
+ public InstanceIdentifier<?> getFailedId() {
+ return failedId;
+ }
+
+
+ /**
+ * Delete specific write failed exception
+ */
+ public static class DeleteFailedException extends WriteFailedException {
+
+ public DeleteFailedException(@Nonnull final InstanceIdentifier<?> failedId, @Nonnull final Throwable cause) {
+ super(failedId, getMsg(failedId), cause);
+ }
+
+ private static String getMsg(@Nonnull final InstanceIdentifier<?> failedId) {
+ return String.format("Failed to delete data at: %s", failedId);
+ }
+
+ public DeleteFailedException(@Nonnull final InstanceIdentifier<?> failedId) {
+ super(failedId, getMsg(failedId));
+ }
+ }
+
+ /**
+ * Create specific write failed exception
+ */
+ public static class CreateFailedException extends WriteFailedException {
+
+ private final DataObject data;
+
+ public CreateFailedException(@Nonnull final InstanceIdentifier<?> failedId,
+ @Nonnull final DataObject data,
+ @Nonnull final Throwable cause) {
+ super(failedId, getMsg(failedId, data), cause);
+ this.data = checkNotNull(data, "data");
+ }
+
+ private static String getMsg(final @Nonnull InstanceIdentifier<?> failedId, final DataObject data) {
+ return String.format("Failed to create data: %s at: %s", data, failedId);
+ }
+
+ public CreateFailedException(@Nonnull final InstanceIdentifier<?> failedId,
+ @Nonnull final DataObject data) {
+ super(failedId, getMsg(failedId, data));
+ this.data = checkNotNull(data, "data");
+ }
+
+ public DataObject getData() {
+ return data;
+ }
+ }
+
+ /**
+ * Update specific write failed exception
+ */
+ public static class UpdateFailedException extends WriteFailedException {
+
+ private final DataObject dataBefore;
+ private final DataObject dataAfter;
+
+ public UpdateFailedException(@Nonnull final InstanceIdentifier<?> failedId,
+ @Nonnull final DataObject dataBefore,
+ @Nonnull final DataObject dataAfter,
+ @Nonnull final Throwable cause) {
+ super(failedId, getMsg(failedId, dataBefore, dataAfter), cause);
+ this.dataBefore = checkNotNull(dataBefore, "dataBefore");
+ this.dataAfter = checkNotNull(dataAfter, "dataAfter");
+ }
+
+ private static String getMsg(final @Nonnull InstanceIdentifier<?> failedId, final DataObject dataBefore,
+ final DataObject dataAfter) {
+ return String.format("Failed to update data from: %s to: %s, at: %s", dataBefore, dataAfter, failedId);
+ }
+
+ public UpdateFailedException(@Nonnull final InstanceIdentifier<?> failedId,
+ @Nonnull final DataObject dataBefore,
+ @Nonnull final DataObject dataAfter) {
+ super(failedId, getMsg(failedId, dataBefore, dataAfter));
+ this.dataBefore = checkNotNull(dataBefore, "dataBefore");
+ this.dataAfter = checkNotNull(dataAfter, "dataAfter");
+ }
+
+ public DataObject getDataBefore() {
+ return dataBefore;
+ }
+
+ public DataObject getDataAfter() {
+ return dataAfter;
+ }
+ }
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/Writer.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/Writer.java
new file mode 100644
index 000000000..77abe341c
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/Writer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.SubtreeManager;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Base writer, responsible for translation between DataObjects and any other side. Handling all update operations(create,
+ * update, delete)
+ *
+ * @param <D> Specific DataObject derived type, that is handled by this writer
+ */
+@Beta
+public interface Writer<D extends DataObject> extends SubtreeManager<D> {
+
+ /**
+ * Handle update operation. U from CRUD.
+ *
+ * @param id Identifier of data being written
+ * @param dataBefore Old data
+ * @param dataAfter New, updated data
+ * @param ctx Write context enabling writer to get information about candidate data as well as current data
+ * @throws WriteFailedException if update failed
+ */
+ void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter,
+ @Nonnull final WriteContext ctx) throws WriteFailedException;
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java
new file mode 100644
index 000000000..ffc76a0e4
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/WriterFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.write.registry.ModifiableWriterRegistryBuilder;
+import javax.annotation.Nonnull;
+
+/**
+ * Factory producing writers for {@link ModifiableWriterRegistryBuilder}.
+ */
+@Beta
+public interface WriterFactory extends AutoCloseable {
+
+ /**
+ * Initialize 1 or more writers and add them to provided registry.
+ */
+ void init(@Nonnull ModifiableWriterRegistryBuilder registry);
+
+ @Override
+ default void close() {
+ // NOOP TODO allow unregister
+ }
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/ModifiableWriterRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/ModifiableWriterRegistryBuilder.java
new file mode 100644
index 000000000..8670a5059
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/ModifiableWriterRegistryBuilder.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write.registry;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.ModifiableSubtreeManagerRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Mutable registry that allows adding new writers.
+ */
+@Beta
+public interface ModifiableWriterRegistryBuilder
+ extends ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> {
+
+}
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistry.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistry.java
new file mode 100644
index 000000000..439a85410
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistry.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Special {@link Writer} capable of performing bulk updates.
+ */
+@Beta
+public interface WriterRegistry {
+
+ /**
+ * Performs bulk update.
+ *
+ * @throws BulkUpdateException in case bulk update fails
+ * @throws TranslationException in case some other error occurs while processing update request
+ */
+ void update(@Nonnull DataObjectUpdates updates,
+ @Nonnull WriteContext ctx) throws TranslationException;
+
+ /**
+ * Simple DTO containing updates for {@link WriterRegistry}. Currently only deletes and updates (create + update)
+ * are distinguished.
+ */
+ @Beta
+ final class DataObjectUpdates {
+
+ private final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates;
+ private final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes;
+
+ /**
+ * Create new instance.
+ *
+ * @param updates All updates indexed by their unkeyed {@link InstanceIdentifier}
+ * @param deletes All deletes indexed by their unkeyed {@link InstanceIdentifier}
+ */
+ public DataObjectUpdates(@Nonnull final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
+ @Nonnull final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes) {
+ this.deletes = deletes;
+ this.updates = updates;
+ }
+
+ public Multimap<InstanceIdentifier<?>, DataObjectUpdate> getUpdates() {
+ return updates;
+ }
+
+ public Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> getDeletes() {
+ return deletes;
+ }
+
+ public boolean isEmpty() {
+ return updates.isEmpty() && deletes.isEmpty();
+ }
+
+ @Override
+ public String toString() {
+ return "DataObjectUpdates{" + "updates=" + updates + ", deletes=" + deletes + '}';
+ }
+
+ /**
+ * Get a {@link Set} containing all update types from both updates as well as deletes.
+ */
+ public Set<InstanceIdentifier<?>> getTypeIntersection() {
+ return Sets.union(deletes.keySet(), updates.keySet());
+ }
+
+ /**
+ * Check whether there is only a single type of data object to be updated.
+ *
+ * @return true if there is only a single type of updates (update + delete)
+ */
+ public boolean containsOnlySingleType() {
+ return getTypeIntersection().size() == 1;
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ final DataObjectUpdates that = (DataObjectUpdates) other;
+
+ if (!updates.equals(that.updates)) {
+ return false;
+ }
+ return deletes.equals(that.deletes);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = updates.hashCode();
+ result = 31 * result + deletes.hashCode();
+ return result;
+ }
+
+ }
+
+ /**
+ * Thrown when bulk update failed.
+ */
+ @Beta
+ class BulkUpdateException extends TranslationException {
+
+ private final Reverter reverter;
+ private final Set<InstanceIdentifier<?>> failedIds;
+
+ /**
+ * Constructs an BulkUpdateException.
+ * @param failedIds instance identifiers of the data objects that were not processed during bulk update.
+ * @param cause the cause of bulk update failure
+ */
+ public BulkUpdateException(@Nonnull final Set<InstanceIdentifier<?>> failedIds,
+ @Nonnull final Reverter reverter,
+ @Nonnull final Throwable cause) {
+ super("Bulk update failed at: " + failedIds, cause);
+ this.failedIds = failedIds;
+ this.reverter = checkNotNull(reverter, "reverter should not be null");
+ }
+
+ /**
+ * Reverts changes that were successfully applied during bulk update before failure occurred.
+ *
+ * @throws Reverter.RevertFailedException if revert fails
+ */
+ public void revertChanges() throws Reverter.RevertFailedException {
+ reverter.revert();
+ }
+
+ public Set<InstanceIdentifier<?>> getFailedIds() {
+ return failedIds;
+ }
+ }
+
+ /**
+ * Abstraction over revert mechanism in case of a bulk update failure.
+ */
+ @Beta
+ interface Reverter {
+
+ /**
+ * Reverts changes that were successfully applied during bulk update before failure occurred. Changes are
+ * reverted in reverse order they were applied.
+ *
+ * @throws RevertFailedException if not all of applied changes were successfully reverted
+ */
+ void revert() throws RevertFailedException;
+
+ /**
+ * Thrown when some of the changes applied during bulk update were not reverted.
+ */
+ @Beta
+ class RevertFailedException extends TranslationException {
+
+ // TODO change to list of VppDataModifications to make debugging easier
+ private final Set<InstanceIdentifier<?>> notRevertedChanges;
+
+ /**
+ * Constructs a RevertFailedException with the list of changes that were not reverted.
+ *
+ * @param notRevertedChanges list of changes that were not reverted
+ * @param cause the cause of revert failure
+ */
+ public RevertFailedException(@Nonnull final Set<InstanceIdentifier<?>> notRevertedChanges,
+ final Throwable cause) {
+ super(cause);
+ checkNotNull(notRevertedChanges, "notRevertedChanges should not be null");
+ this.notRevertedChanges = ImmutableSet.copyOf(notRevertedChanges);
+ }
+
+ /**
+ * Returns the list of changes that were not reverted.
+ *
+ * @return list of changes that were not reverted
+ */
+ @Nonnull
+ public Set<InstanceIdentifier<?>> getNotRevertedChanges() {
+ return notRevertedChanges;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistryBuilder.java b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistryBuilder.java
new file mode 100644
index 000000000..3f0289ece
--- /dev/null
+++ b/infra/translate-api/src/main/java/io/fd/honeycomb/v3po/translate/write/registry/WriterRegistryBuilder.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.write.registry;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.SubtreeManagerRegistryBuilder;
+
+/**
+ * Builder for writer registries.
+ */
+@Beta
+public interface WriterRegistryBuilder extends SubtreeManagerRegistryBuilder<WriterRegistry> {
+}
diff --git a/infra/translate-api/src/main/yang/translate-api.yang b/infra/translate-api/src/main/yang/translate-api.yang
new file mode 100644
index 000000000..796632dd2
--- /dev/null
+++ b/infra/translate-api/src/main/yang/translate-api.yang
@@ -0,0 +1,52 @@
+module translate-api {
+ yang-version 1;
+ namespace "urn:honeycomb:params:xml:ns:yang:translate:api";
+ prefix "tapi";
+
+ import config { prefix config; revision-date 2013-04-05; }
+
+ description
+ "This module contains the base YANG definitions for
+ readers/writers plugged into a honeycomb";
+
+ revision "2016-04-06" {
+ description
+ "Initial revision.";
+ }
+
+ identity honeycomb-reader-factory {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.translate.read.ReaderFactory;
+ }
+
+ identity honeycomb-reader-registry {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder;
+ }
+
+ identity honeycomb-reader-registry-builder {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistryBuilder;
+ }
+
+ identity honeycomb-writer-factory {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.translate.write.WriterFactory;
+ }
+
+ identity honeycomb-writer-registry {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.translate.write.registry.ModifiableWriterRegistryBuilder;
+ }
+
+ identity honeycomb-writer-registry-builder {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.translate.write.registry.WriterRegistryBuilder;
+ }
+
+ identity honeycomb-mapping-context {
+ base "config:service-type";
+ config:java-class io.fd.honeycomb.v3po.translate.MappingContext;
+ }
+
+} \ No newline at end of file
diff --git a/infra/translate-api/src/test/java/io/fd/honeycomb/v3po/translate/ReadFailedExceptionTest.java b/infra/translate-api/src/test/java/io/fd/honeycomb/v3po/translate/ReadFailedExceptionTest.java
new file mode 100644
index 000000000..71c0afbd3
--- /dev/null
+++ b/infra/translate-api/src/test/java/io/fd/honeycomb/v3po/translate/ReadFailedExceptionTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate;
+
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class ReadFailedExceptionTest {
+
+ @Test
+ public void testInstantiation() {
+ final InstanceIdentifier<DataObject> id = InstanceIdentifier.create(DataObject.class);
+ ReadFailedException e = new ReadFailedException(id);
+ Assert.assertEquals(id, e.getFailedId());
+ Assert.assertNull(e.getCause());
+ Assert.assertTrue(e.getMessage().contains(id.toString()));
+ }
+
+ @Test
+ public void testInstantiationWithCause() {
+ final InstanceIdentifier<DataObject> id = InstanceIdentifier.create(DataObject.class);
+ final RuntimeException cause = new RuntimeException();
+ ReadFailedException e = new ReadFailedException(id, cause);
+ Assert.assertEquals(id, e.getFailedId());
+ Assert.assertEquals(cause, e.getCause());
+ Assert.assertTrue(e.getMessage().contains(id.toString()));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testInstantiationFailed() {
+ new ReadFailedException(null);
+ }
+} \ No newline at end of file
diff --git a/infra/translate-impl/Readme.adoc b/infra/translate-impl/Readme.adoc
new file mode 100644
index 000000000..1e5c63604
--- /dev/null
+++ b/infra/translate-impl/Readme.adoc
@@ -0,0 +1,9 @@
+= Honeycomb translation layer implementation
+
+Provides composite implementations of child, list and root readers/writers.
+
+Root readers/writers can be used to provide data translation at root node level.
+List readers/writers help with data translation for nodes modeled as YANG lists.
+Child readers/writers can be used to provide data translation for non-leaf nodes.
+
+Readers and writers can be easily extended using customizers defined in the translate-spi module. \ No newline at end of file
diff --git a/infra/translate-impl/pom.xml b/infra/translate-impl/pom.xml
new file mode 100644
index 000000000..264f10956
--- /dev/null
+++ b/infra/translate-impl/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>api-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/api-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>translate-impl</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
+ <Export-Package>
+ io.fd.honeycomb.v3po.translate.impl.*
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericListReader.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericListReader.java
new file mode 100644
index 000000000..defb2e51b
--- /dev/null
+++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericListReader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.read;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.read.ListReader;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.spi.read.ListReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.util.read.AbstractGenericReader;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Composite implementation of {@link ListReader} able to place the read result into parent builder object intended
+ * for list node type.
+ * <p/>
+ * This reader checks if the IDs are wildcarded in which case it performs read of all list entries. In case the ID has a
+ * key, it reads only the specified value.
+ */
+@Beta
+@ThreadSafe
+public final class GenericListReader<C extends DataObject & Identifiable<K>, K extends Identifier<C>, B extends Builder<C>>
+ extends AbstractGenericReader<C, B> implements ListReader<C, K, B> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(GenericListReader.class);
+
+ private final ListReaderCustomizer<C, K, B> customizer;
+
+ /**
+ * Create new {@link GenericListReader}
+ *
+ * @param managedDataObjectType Class object for managed data type. Must come from a list node type.
+ * @param customizer Customizer instance to customize this generic reader
+ */
+ public GenericListReader(@Nonnull final InstanceIdentifier<C> managedDataObjectType,
+ @Nonnull final ListReaderCustomizer<C, K, B> customizer) {
+ super(managedDataObjectType);
+ this.customizer = customizer;
+ }
+
+ @Override
+ @Nonnull
+ public List<C> readList(@Nonnull final InstanceIdentifier<C> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ LOG.trace("{}: Reading all list entries", this);
+ final List<K> allIds = getAllIds(id, ctx);
+ LOG.debug("{}: Reading list entries for: {}", this, allIds);
+
+ final ArrayList<C> allEntries = new ArrayList<>(allIds.size());
+ for (K key : allIds) {
+ final InstanceIdentifier.IdentifiableItem<C, K> currentBdItem = RWUtils.getCurrentIdItem(id, key);
+ final InstanceIdentifier<C> keyedId = RWUtils.replaceLastInId(id, currentBdItem);
+ final Optional<C> read = readCurrent(keyedId, ctx);
+ if (read.isPresent()) {
+ final DataObject singleItem = read.get();
+ checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass()));
+ allEntries.add(getManagedDataObjectType().getTargetType().cast(singleItem));
+ }
+ }
+ return allEntries;
+ }
+
+ @Override
+ public List<K> getAllIds(@Nonnull final InstanceIdentifier<C> id, @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ LOG.trace("{}: Getting all list ids", this);
+ final List<K> allIds = customizer.getAllIds(id, ctx);
+ LOG.debug("{}: All list ids: {}", this, allIds);
+ return allIds;
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<C> readData) {
+ customizer.merge(builder, readData);
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ customizer.readCurrentAttributes(id, builder, ctx);
+ }
+
+ @Override
+ public B getBuilder(@Nonnull final InstanceIdentifier<C> id) {
+ return customizer.getBuilder(id);
+ }
+
+}
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericReader.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericReader.java
new file mode 100644
index 000000000..eace7fa89
--- /dev/null
+++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/read/GenericReader.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.read;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.spi.read.ReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.util.read.AbstractGenericReader;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Composite implementation of {@link Reader}.
+ */
+@Beta
+@ThreadSafe
+public final class GenericReader<C extends DataObject, B extends Builder<C>> extends AbstractGenericReader<C, B>
+ implements Reader<C, B> {
+
+ private final ReaderCustomizer<C, B> customizer;
+
+ /**
+ * Create a new {@link GenericReader}.
+ *
+ * @param id Instance identifier for managed data type
+ * @param customizer Customizer instance to customize this generic reader
+ */
+ public GenericReader(@Nonnull final InstanceIdentifier<C> id,
+ @Nonnull final ReaderCustomizer<C, B> customizer) {
+ super(id);
+ this.customizer = customizer;
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id,
+ @Nonnull final B builder,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ customizer.readCurrentAttributes(id, builder, ctx);
+ }
+
+ @Override
+ public B getBuilder(@Nonnull final InstanceIdentifier<C> id) {
+ return customizer.getBuilder(id);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue) {
+ customizer.merge(parentBuilder, readValue);
+ }
+
+}
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java
new file mode 100644
index 000000000..32daf5975
--- /dev/null
+++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.write;
+
+import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.v3po.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.util.write.AbstractGenericWriter;
+import io.fd.honeycomb.v3po.translate.write.ListWriter;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Generic list node writer with customizable behavior thanks to injected customizer.
+ */
+public final class GenericListWriter<D extends DataObject & Identifiable<K>, K extends Identifier<D>> extends
+ AbstractGenericWriter<D> implements ListWriter<D, K> {
+
+ private final WriterCustomizer<D> customizer;
+
+ public GenericListWriter(@Nonnull final InstanceIdentifier<D> type,
+ @Nonnull final ListWriterCustomizer<D, K> customizer) {
+ super(type);
+ this.customizer = customizer;
+ }
+
+ @Override
+ protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ customizer.writeCurrentAttributes(id, data, ctx);
+ }
+
+ @Override
+ protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ customizer.deleteCurrentAttributes(id, dataBefore, ctx);
+ }
+
+ @Override
+ protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+ @Nonnull final D dataAfter, @Nonnull final WriteContext ctx)
+ throws WriteFailedException {
+ customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
+ }
+
+ @Override
+ protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx)
+ throws WriteFailedException {
+ // Make sure the key is present
+ if (isWildcarded(id)) {
+ super.writeCurrent(getSpecificId(id, data), data, ctx);
+ } else {
+ super.writeCurrent(id, data, ctx);
+ }
+ }
+
+ @Override
+ protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
+ final WriteContext ctx) throws WriteFailedException {
+ // Make sure the key is present
+ if (isWildcarded(id)) {
+ super.updateCurrent(getSpecificId(id, dataBefore), dataBefore, dataAfter, ctx);
+ } else {
+ super.updateCurrent(id, dataBefore, dataAfter, ctx);
+ }
+ }
+
+ @Override
+ protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx)
+ throws WriteFailedException {
+ // Make sure the key is present
+ if (isWildcarded(id)) {
+ super.deleteCurrent(getSpecificId(id, dataBefore), dataBefore, ctx);
+ } else {
+ super.deleteCurrent(id, dataBefore, ctx);
+ }
+ }
+
+ private boolean isWildcarded(final InstanceIdentifier<D> id) {
+ return id.firstIdentifierOf(getManagedDataObjectType().getTargetType()).isWildcarded();
+ }
+
+ private InstanceIdentifier<D> getSpecificId(final InstanceIdentifier<D> currentId, final D current) {
+ return RWUtils.replaceLastInId(currentId,
+ new InstanceIdentifier.IdentifiableItem<>(currentId.getTargetType(), current.getKey()));
+ }
+}
diff --git a/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java
new file mode 100644
index 000000000..65c192ffa
--- /dev/null
+++ b/infra/translate-impl/src/main/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriter.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.write;
+
+import io.fd.honeycomb.v3po.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.v3po.translate.util.write.AbstractGenericWriter;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Generic writer with customizable behavior thanks to injected customizer.
+ */
+public final class GenericWriter<D extends DataObject> extends AbstractGenericWriter<D> {
+
+ private final WriterCustomizer<D> customizer;
+
+ public GenericWriter(@Nonnull final InstanceIdentifier<D> type,
+ @Nonnull final WriterCustomizer<D> customizer) {
+ super(type);
+ this.customizer = customizer;
+ }
+
+ @Override
+ protected void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D data,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ // TODO wrap all customizer invocations in try catch, and wrap runtime exceptions in ReadFailed
+ // TODO same for readers
+ customizer.writeCurrentAttributes(id, data, ctx);
+ }
+
+ @Override
+ protected void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final D dataBefore,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ customizer.deleteCurrentAttributes(id, dataBefore, ctx);
+ }
+
+ @Override
+ protected void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D dataBefore,
+ @Nonnull final D dataAfter,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ customizer.updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
+ }
+}
diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java
new file mode 100644
index 000000000..54a7466e1
--- /dev/null
+++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericListWriterTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.write;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import io.fd.honeycomb.v3po.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class GenericListWriterTest {
+
+ private static final InstanceIdentifier<IdentifiableDataObject>
+ DATA_OBJECT_INSTANCE_IDENTIFIER = InstanceIdentifier.create(IdentifiableDataObject.class);
+ @Mock
+ private ListWriterCustomizer<IdentifiableDataObject, DataObjectIdentifier> customizer;
+ @Mock
+ private WriteContext ctx;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ final GenericListWriter<IdentifiableDataObject, DataObjectIdentifier> writer =
+ new GenericListWriter<>(DATA_OBJECT_INSTANCE_IDENTIFIER, customizer);
+
+ final IdentifiableDataObject before = mock(IdentifiableDataObject.class);
+ final DataObjectIdentifier beforeKey = mock(DataObjectIdentifier.class);
+ when(before.getKey()).thenReturn(beforeKey);
+ final IdentifiableDataObject after = mock(IdentifiableDataObject.class);
+ final DataObjectIdentifier keyAfter = mock(DataObjectIdentifier.class);
+ when(after.getKey()).thenReturn(keyAfter);
+
+ assertEquals(DATA_OBJECT_INSTANCE_IDENTIFIER, writer.getManagedDataObjectType());
+
+ final InstanceIdentifier<IdentifiableDataObject> keyedIdBefore =
+ (InstanceIdentifier<IdentifiableDataObject>) InstanceIdentifier.create(Collections
+ .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, beforeKey)));
+ final InstanceIdentifier<IdentifiableDataObject> keyedIdAfter =
+ (InstanceIdentifier<IdentifiableDataObject>) InstanceIdentifier.create(Collections
+ .singleton(new InstanceIdentifier.IdentifiableItem<>(IdentifiableDataObject.class, keyAfter)));
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx);
+ verify(customizer).updateCurrentAttributes(keyedIdBefore, before, after, ctx);
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, null, ctx);
+ verify(customizer).deleteCurrentAttributes(keyedIdBefore, before, ctx);
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, null, after, ctx);
+ verify(customizer).writeCurrentAttributes(keyedIdAfter, after, ctx);
+ }
+
+ private abstract static class IdentifiableDataObject implements DataObject, Identifiable<DataObjectIdentifier> {}
+ private abstract static class DataObjectIdentifier implements Identifier<IdentifiableDataObject> {}
+} \ No newline at end of file
diff --git a/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java b/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java
new file mode 100644
index 000000000..87f18965b
--- /dev/null
+++ b/infra/translate-impl/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/GenericWriterTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.write;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import io.fd.honeycomb.v3po.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class GenericWriterTest {
+
+ private static final InstanceIdentifier<DataObject>
+ DATA_OBJECT_INSTANCE_IDENTIFIER = InstanceIdentifier.create(DataObject.class);
+ @Mock
+ private WriterCustomizer<DataObject> customizer;
+ @Mock
+ private WriteContext ctx;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ final GenericWriter<DataObject> writer =
+ new GenericWriter<>(DATA_OBJECT_INSTANCE_IDENTIFIER, customizer);
+
+ final DataObject before = mock(DataObject.class);
+ final DataObject after = mock(DataObject.class);
+
+ assertEquals(DATA_OBJECT_INSTANCE_IDENTIFIER, writer.getManagedDataObjectType());
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx);
+ verify(customizer).updateCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, before, after, ctx);
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, before, null, ctx);
+ verify(customizer).deleteCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, before, ctx);
+
+ writer.update(DATA_OBJECT_INSTANCE_IDENTIFIER, null, after, ctx);
+ verify(customizer).writeCurrentAttributes(DATA_OBJECT_INSTANCE_IDENTIFIER, after, ctx);
+ }
+} \ No newline at end of file
diff --git a/infra/translate-spi/Readme.adoc b/infra/translate-spi/Readme.adoc
new file mode 100644
index 000000000..755538c07
--- /dev/null
+++ b/infra/translate-spi/Readme.adoc
@@ -0,0 +1,4 @@
+= Honeycomb translation layer SPI
+
+Provides root, child and list customizer interfaces for extending readers/writers.
+Customizers can be used to implement actual data reads and writes. \ No newline at end of file
diff --git a/infra/translate-spi/pom.xml b/infra/translate-spi/pom.xml
new file mode 100644
index 000000000..f8c20140b
--- /dev/null
+++ b/infra/translate-spi/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>api-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/api-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>translate-spi</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-artifacts</artifactId>
+ <version>2.0.2-Beryllium-SR2</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-api</artifactId>
+ </dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java
new file mode 100644
index 000000000..59557554f
--- /dev/null
+++ b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ListReaderCustomizer.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.spi.read;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * CompositeListReader SPI to customize its behavior.
+ *
+ * @param <C> Specific DataObject derived type (Identifiable), that is handled by this customizer
+ * @param <K> Specific Identifier for handled type (C)
+ * @param <B> Specific Builder for handled type (C)
+ */
+@Beta
+public interface ListReaderCustomizer<C extends DataObject & Identifiable<K>, K extends Identifier<C>, B extends Builder<C>>
+ extends ReaderCustomizer<C, B> {
+
+ /**
+ * Return list with IDs of all list nodes to be read.
+ *
+ * @param id Wildcarded ID pointing to list node managed by enclosing reader
+ * @param context Read context
+ * @throws ReadFailedException if the list of IDs could not be read
+ */
+ @Nonnull
+ List<K> getAllIds(@Nonnull final InstanceIdentifier<C> id, @Nonnull final ReadContext context) throws
+ ReadFailedException;
+ // TODO does it make sense with vpp APIs ? Should we replace it with a simple readAll ?
+
+ /**
+ * Merge read data into provided parent builder.
+ */
+ void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<C> readData);
+
+ @Override
+ default void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue) {
+ merge(parentBuilder, Collections.singletonList(readValue));
+ }
+}
diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ReaderCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ReaderCustomizer.java
new file mode 100644
index 000000000..270da8beb
--- /dev/null
+++ b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/read/ReaderCustomizer.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.spi.read;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * CompositeChildReader SPI to customize its behavior.
+ *
+ * @param <C> Specific DataObject derived type (Identifiable), that is handled by this customizer
+ * @param <B> Specific Builder for handled type (C)
+ */
+@Beta
+public interface ReaderCustomizer<C extends DataObject, B extends Builder<C>> {
+
+ /**
+ * Creates new builder that will be used to build read value.
+ */
+ @Nonnull
+ B getBuilder(@Nonnull final InstanceIdentifier<C> id);
+
+ /**
+ * Adds current data (identified by id) to the provided builder.
+ *
+ * @param id id of current data object
+ * @param builder builder for creating read value
+ * @param ctx
+ * @throws ReadFailedException if read was unsuccessful
+ */
+ void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id,
+ @Nonnull final B builder,
+ @Nonnull final ReadContext ctx) throws ReadFailedException;
+
+ // FIXME need to capture parent builder type, but that's inconvenient at best, is it ok to leave it Builder<?> and
+ // cast in specific customizers ? ... probably better than adding another type parameter
+
+ /**
+ * Merge read data into provided parent builder.
+ */
+ void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue);
+
+
+}
diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/ListWriterCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/ListWriterCustomizer.java
new file mode 100644
index 000000000..41cdb94b6
--- /dev/null
+++ b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/ListWriterCustomizer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.spi.write;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+
+/**
+ * CompositeListWriter SPI to customize its behavior.
+ *
+ * @param <C> Specific DataObject derived type (Identifiable), that is handled by this customizer
+ * @param <K> Specific Identifier for handled type (C)
+ */
+@Beta
+public interface ListWriterCustomizer<C extends DataObject & Identifiable<K>, K extends Identifier<C>> extends
+ WriterCustomizer<C> {
+
+} \ No newline at end of file
diff --git a/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/WriterCustomizer.java b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/WriterCustomizer.java
new file mode 100644
index 000000000..06ce3f814
--- /dev/null
+++ b/infra/translate-spi/src/main/java/io/fd/honeycomb/v3po/translate/spi/write/WriterCustomizer.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.spi.write;
+
+import com.google.common.annotations.Beta;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * CompositeChildWriter SPI to customize its behavior.
+ *
+ * @param <D> Specific DataObject derived type (Identifiable), that is handled by this customizer
+ */
+@Beta
+public interface WriterCustomizer<D extends DataObject> {
+
+ /**
+ * Handle write operation. C from CRUD.
+ *
+ * @param id Identifier(from root) of data being written
+ * @param dataAfter New data to be written
+ * @param writeContext Write context can be used to store any useful information and then utilized by other customizers
+ *
+ * @throws WriteFailedException if write was unsuccessful
+ */
+ void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException;
+
+ /**
+ * Handle update operation. U from CRUD.
+ *
+ * @param id Identifier(from root) of data being written
+ * @param dataBefore Old data
+ * @param dataAfter New, updated data
+ * @param writeContext Write context can be used to store any useful information and then utilized by other customizers
+ *
+ * @throws WriteFailedException if update was unsuccessful
+ */
+ void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D dataBefore,
+ @Nonnull final D dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException;
+
+ /**
+ * Handle delete operation. D from CRUD.
+ *
+ * @param id Identifier(from root) of data being written
+ * @param dataBefore Old data being deleted
+ * @param writeContext Write context can be used to store any useful information and then utilized by other customizers
+ *
+ * @throws WriteFailedException if delete was unsuccessful
+ */
+ void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D dataBefore,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException;
+
+}
diff --git a/infra/translate-utils/Readme.adoc b/infra/translate-utils/Readme.adoc
new file mode 100644
index 000000000..17ebb6c6a
--- /dev/null
+++ b/infra/translate-utils/Readme.adoc
@@ -0,0 +1,3 @@
+= Honeycomb translation layer utils
+
+Provides utility classes useful in translation layer implementation. \ No newline at end of file
diff --git a/infra/translate-utils/pom.xml b/infra/translate-utils/pom.xml
new file mode 100644
index 000000000..922bd3ecc
--- /dev/null
+++ b/infra/translate-utils/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2015 Cisco and/or its affiliates.
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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">
+ <parent>
+ <groupId>io.fd.honeycomb.common</groupId>
+ <artifactId>impl-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <relativePath>../../common/impl-parent</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.fd.honeycomb</groupId>
+ <artifactId>translate-utils</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-artifacts</artifactId>
+ <version>2.0.2-Beryllium-SR2</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>mdsal-artifacts</artifactId>
+ <version>1.3.2-Beryllium-SR2</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>translate-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-binding-dom-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-codec-gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jgrapht</groupId>
+ <artifactId>jgrapht-core</artifactId>
+ <version>0.9.2</version>
+ </dependency>
+
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java
new file mode 100644
index 000000000..23a66337f
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/AbstractSubtreeManagerRegistryBuilderBuilder.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.ModifiableSubtreeManagerRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.SubtreeManager;
+import io.fd.honeycomb.v3po.translate.SubtreeManagerRegistryBuilder;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public abstract class AbstractSubtreeManagerRegistryBuilderBuilder<S extends SubtreeManager<? extends DataObject>, R>
+ implements ModifiableSubtreeManagerRegistryBuilder<S>, SubtreeManagerRegistryBuilder<R>, AutoCloseable {
+
+ // Using directed acyclic graph to represent the ordering relationships between writers
+ private final DirectedAcyclicGraph<InstanceIdentifier<?>, Order>
+ handlersRelations = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Order());
+ private final Map<InstanceIdentifier<?>, S> handlersMap = new HashMap<>();
+
+ /**
+ * Add handler without any special relationship to any other type.
+ */
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> add(@Nonnull final S handler) {
+ // Make IID wildcarded just in case
+ // + the way InstanceIdentifier.create + equals work for Identifiable items is unexpected, meaning updates would
+ // not be matched to writers in registry
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ /**
+ * Add handler without any special relationship to any other type.
+ */
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAdd(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler) {
+ add(getSubtreeHandler(handledChildren, handler));
+ return this;
+ }
+
+ private void checkWriterNotPresentYet(final InstanceIdentifier<?> targetType) {
+ Preconditions.checkArgument(!handlersMap.containsKey(targetType),
+ "Writer for type: %s already present: %s", targetType, handlersMap.get(targetType));
+ }
+
+ /**
+ * Add handler with relationship: to be executed before handler handling relatedType.
+ */
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addBefore(@Nonnull final S handler,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ handlersRelations.addVertex(wildcardedRelatedType);
+ addEdge(targetType, wildcardedRelatedType);
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addBefore(@Nonnull final S handler,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(handlersRelations::addVertex);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(type -> addEdge(targetType, type));
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddBefore(
+ @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ return addBefore(getSubtreeHandler(handledChildren, handler), relatedType);
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddBefore(
+ @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ return addBefore(getSubtreeHandler(handledChildren, handler), relatedTypes);
+ }
+
+ protected abstract S getSubtreeHandler(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler);
+
+ /**
+ * Add handler with relationship: to be executed after handler handling relatedType.
+ */
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addAfter(@Nonnull final S handler,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ final InstanceIdentifier<?> wildcardedRelatedType = RWUtils.makeIidWildcarded(relatedType);
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ handlersRelations.addVertex(wildcardedRelatedType);
+ // set edge to indicate before relationship, just reversed
+ addEdge(wildcardedRelatedType, targetType);
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> addAfter(@Nonnull final S handler,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ final InstanceIdentifier<?> targetType = RWUtils.makeIidWildcarded(handler.getManagedDataObjectType());
+ checkWriterNotPresentYet(targetType);
+ handlersRelations.addVertex(targetType);
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(handlersRelations::addVertex);
+ // set edge to indicate before relationship, just reversed
+ relatedTypes.stream()
+ .map(RWUtils::makeIidWildcarded)
+ .forEach(type -> addEdge(type, targetType));
+ handlersMap.put(targetType, handler);
+ return this;
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddAfter(
+ @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler,
+ @Nonnull final InstanceIdentifier<?> relatedType) {
+ return addAfter(getSubtreeHandler(handledChildren, handler), relatedType);
+ }
+
+ @Override
+ public AbstractSubtreeManagerRegistryBuilderBuilder<S, R> subtreeAddAfter(
+ @Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final S handler,
+ @Nonnull final Collection<InstanceIdentifier<?>> relatedTypes) {
+ return addAfter(getSubtreeHandler(handledChildren, handler), relatedTypes);
+ }
+
+
+ private void addEdge(final InstanceIdentifier<?> targetType,
+ final InstanceIdentifier<?> relatedType) {
+ try {
+ handlersRelations.addDagEdge(targetType, relatedType);
+ } catch (DirectedAcyclicGraph.CycleFoundException e) {
+ throw new IllegalArgumentException(String.format(
+ "Unable to add writer with relation: %s -> %s. Loop detected", targetType, relatedType), e);
+ }
+ }
+
+ protected ImmutableMap<InstanceIdentifier<?>, S> getMappedHandlers() {
+ final ImmutableMap.Builder<InstanceIdentifier<?>, S> builder = ImmutableMap.builder();
+ // Iterate writer types according to their relationships from graph
+ handlersRelations.iterator()
+ .forEachRemaining(writerType -> {
+ // There might be types stored just for relationship sake, no real writer, ignoring those
+ if (handlersMap.containsKey(writerType)) {
+ builder.put(writerType, handlersMap.get(writerType));
+ }
+ });
+
+ // TODO we could optimize subtree handlers, if there is a dedicated handler for a node managed by a subtree
+ // handler, recreate the subtree handler with a subset of handled child nodes
+ // This way it is not necessary to change the configuration of subtree writer, just to add a dedicated child
+ // writer. This will be needed if we ever switch to annotations for reader/writer hierarchy initialization
+
+ return builder.build();
+ }
+
+ @Override
+ public void close() throws Exception {
+ handlersMap.clear();
+ // Wrap sets into another set to avoid concurrent modification ex in graph
+ handlersRelations.removeAllEdges(Sets.newHashSet(handlersRelations.edgeSet()));
+ handlersRelations.removeAllVertices(Sets.newHashSet(handlersRelations.vertexSet()));
+ }
+
+ // Represents edges in graph
+ private class Order {}
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java
new file mode 100644
index 000000000..d9798d07d
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/JsonUtils.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util;
+
+import com.google.common.base.Charsets;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+public final class JsonUtils {
+
+ private JsonUtils() {}
+
+ /**
+ * Serialize normalized node root structure into provided output stream
+ *
+ * @throws IOException if serialized data cannot be written into provided output stream
+ */
+ public static void writeJsonRoot(@Nonnull final NormalizedNode<?, ?> rootData,
+ @Nonnull final SchemaContext schemaContext,
+ @Nonnull final OutputStream outputStream) throws IOException {
+ final JsonWriter
+ jsonWriter = createJsonWriter(outputStream, true);
+ final NormalizedNodeStreamWriter streamWriter = JSONNormalizedNodeStreamWriter
+ .createNestedWriter(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, null, jsonWriter);
+ final NormalizedNodeWriter normalizedNodeWriter =
+ NormalizedNodeWriter.forStreamWriter(streamWriter, true);
+ jsonWriter.beginObject();
+ writeChildren(normalizedNodeWriter,(ContainerNode) rootData);
+ jsonWriter.endObject();
+ jsonWriter.flush();
+ }
+
+ /**
+ * Read json serialized normalized node root structure and parse them into normalized nodes
+ *
+ * @return artificial normalized node holding all the top level nodes from provided stream as children. In case
+ * the stream is empty, empty artificial normalized node is returned
+ *
+ * @throws IllegalArgumentException if content in the provided input stream is not restore-able
+ */
+ public static ContainerNode readJsonRoot(@Nonnull final SchemaContext schemaContext,
+ @Nonnull final InputStream stream) {
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> builder =
+ Builders.containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(schemaContext.getQName()));
+ final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(builder);
+
+ final JsonParserStream jsonParser = JsonParserStream.create(writer, schemaContext);
+ final JsonReader reader = new JsonReader(new InputStreamReader(stream, Charsets.UTF_8));
+ jsonParser.parse(reader);
+
+ return builder.build();
+ }
+
+ private static void writeChildren(final NormalizedNodeWriter nnWriter, final ContainerNode data) throws IOException {
+ for(final DataContainerChild<? extends YangInstanceIdentifier.PathArgument, ?> child : data.getValue()) {
+ nnWriter.write(child);
+ }
+ }
+
+ private static JsonWriter createJsonWriter(final OutputStream entityStream, boolean prettyPrint) {
+ if (prettyPrint) {
+ return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8), 2);
+ } else {
+ return JsonWriterFactory.createJsonWriter(new OutputStreamWriter(entityStream, Charsets.UTF_8));
+ }
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
new file mode 100644
index 000000000..2a565d9f2
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/RWUtils.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import io.fd.honeycomb.v3po.translate.SubtreeManager;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class RWUtils {
+
+ private RWUtils() {}
+
+ /**
+ * Collector expecting only a single resulting item from a stream
+ */
+ public static<T> Collector<T,?,T> singleItemCollector() {
+ return Collectors.collectingAndThen(
+ Collectors.toList(),
+ list -> {
+ if (list.size() != 1) {
+ throw new IllegalStateException("Unexpected size of list: " + list + ". Single item expected");
+ }
+ return list.get(0);
+ }
+ );
+ }
+
+ /**
+ * Find next item in ID after provided type
+ */
+ @Nonnull
+ public static InstanceIdentifier.PathArgument getNextId(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final InstanceIdentifier<? extends DataObject> type) {
+ // TODO this is inefficient(maybe, depending on actual Iterable type)
+ final Iterable<InstanceIdentifier.PathArgument> pathArguments = id.getPathArguments();
+ final int i = Iterables.indexOf(pathArguments, new Predicate<InstanceIdentifier.PathArgument>() {
+ @Override
+ public boolean apply(final InstanceIdentifier.PathArgument input) {
+ return input.getType().isAssignableFrom(type.getTargetType());
+ }
+ });
+ Preconditions.checkArgument(i >= 0, "Unable to find %s type in %s", type.getTargetType(), id);
+ return Iterables.get(pathArguments, i + 1);
+ }
+
+ /**
+ * Replace last item in ID with a provided IdentifiableItem of the same type
+ */
+ @SuppressWarnings("unchecked")
+ @Nonnull
+ public static <D extends DataObject & Identifiable<K>, K extends Identifier<D>> InstanceIdentifier<D> replaceLastInId(
+ @Nonnull final InstanceIdentifier<D> id, final InstanceIdentifier.IdentifiableItem<D, K> currentBdItem) {
+
+ final Iterable<InstanceIdentifier.PathArgument> pathArguments = id.getPathArguments();
+ final Iterable<InstanceIdentifier.PathArgument> withoutCurrent =
+ Iterables.limit(pathArguments, Iterables.size(pathArguments) - 1);
+ final Iterable<InstanceIdentifier.PathArgument> concat =
+ Iterables.concat(withoutCurrent, Collections.singleton(currentBdItem));
+ return (InstanceIdentifier<D>) InstanceIdentifier.create(concat);
+ }
+
+ /**
+ * Create IdentifiableItem from target type of provided ID with provided key
+ */
+ @Nonnull
+ public static <D extends DataObject & Identifiable<K>, K extends Identifier<D>> InstanceIdentifier.IdentifiableItem<D, K> getCurrentIdItem(
+ @Nonnull final InstanceIdentifier<D> id, final K key) {
+ return new InstanceIdentifier.IdentifiableItem<>(id.getTargetType(), key);
+ }
+
+ /**
+ * Trim InstanceIdentifier at indexOf(type)
+ */
+ @SuppressWarnings("unchecked")
+ @Nonnull
+ public static <D extends DataObject> InstanceIdentifier<D> cutId(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final InstanceIdentifier<D> type) {
+ final Iterable<InstanceIdentifier.PathArgument> pathArguments = id.getPathArguments();
+ final int i = Iterables.indexOf(pathArguments, new Predicate<InstanceIdentifier.PathArgument>() {
+ @Override
+ public boolean apply(final InstanceIdentifier.PathArgument input) {
+ return input.getType().equals(type.getTargetType());
+ }
+ });
+ Preconditions.checkArgument(i >= 0, "ID %s does not contain %s", id, type);
+ return (InstanceIdentifier<D>) InstanceIdentifier.create(Iterables.limit(pathArguments, i + 1));
+ }
+
+ /**
+ * Create an ordered map from a collection, checking for duplicity in the process.
+ */
+ @Nonnull
+ public static <K, V> Map<K, V> uniqueLinkedIndex(@Nonnull final Collection<V> values, @Nonnull final Function<? super V, K> keyFunction) {
+ final Map<K, V> objectObjectLinkedHashMap = Maps.newLinkedHashMap();
+ for (V value : values) {
+ final K key = keyFunction.apply(value);
+ Preconditions.checkArgument(objectObjectLinkedHashMap.put(key, value) == null,
+ "Duplicate key detected : %s", key);
+ }
+ return objectObjectLinkedHashMap;
+ }
+
+ public static final Function<SubtreeManager<? extends DataObject>, Class<? extends DataObject>>
+ MANAGER_CLASS_FUNCTION = new Function<SubtreeManager<? extends DataObject>, Class<? extends DataObject>>() {
+ @Override
+ public Class<? extends DataObject> apply(final SubtreeManager<? extends DataObject> input) {
+ return input.getManagedDataObjectType().getTargetType();
+ }
+ };
+
+ public static final Function<SubtreeManager<? extends Augmentation<?>>, Class<? extends DataObject>>
+ MANAGER_CLASS_AUG_FUNCTION = new Function<SubtreeManager<? extends Augmentation<?>>, Class<? extends DataObject>>() {
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Class<? extends DataObject> apply(final SubtreeManager<? extends Augmentation<?>> input) {
+ final Class<? extends Augmentation<?>> targetType = input.getManagedDataObjectType().getTargetType();
+ Preconditions.checkArgument(DataObject.class.isAssignableFrom(targetType));
+ return (Class<? extends DataObject>) targetType;
+ }
+ };
+
+ /**
+ * Transform a keyed instance identifier into a wildcarded one.
+ * <p/>
+ * ! This has to be called also for wildcarded List instance identifiers
+ * due to weird behavior of equals in InstanceIdentifier !
+ */
+ @SuppressWarnings("unchecked")
+ public static <D extends DataObject> InstanceIdentifier<D> makeIidWildcarded(final InstanceIdentifier<D> id) {
+ final List<InstanceIdentifier.PathArgument> transformedPathArguments =
+ StreamSupport.stream(id.getPathArguments().spliterator(), false)
+ .map(RWUtils::cleanPathArgumentFromKeys)
+ .collect(Collectors.toList());
+ return (InstanceIdentifier<D>) InstanceIdentifier.create(transformedPathArguments);
+ }
+
+ /**
+ * Transform a keyed instance identifier into a wildcarded one, keeping keys except the last item.
+ */
+ @SuppressWarnings("unchecked")
+ public static <D extends DataObject> InstanceIdentifier<D> makeIidLastWildcarded(final InstanceIdentifier<D> id) {
+ final InstanceIdentifier.Item<D> wildcardedItem = new InstanceIdentifier.Item<>(id.getTargetType());
+ final Iterable<InstanceIdentifier.PathArgument> pathArguments = id.getPathArguments();
+ return (InstanceIdentifier<D>) InstanceIdentifier.create(
+ Iterables.concat(
+ Iterables.limit(pathArguments, Iterables.size(pathArguments) - 1),
+ Collections.singleton(wildcardedItem)));
+ }
+
+ private static InstanceIdentifier.PathArgument cleanPathArgumentFromKeys(final InstanceIdentifier.PathArgument pathArgument) {
+ return pathArgument instanceof InstanceIdentifier.IdentifiableItem<?, ?>
+ ? new InstanceIdentifier.Item<>(pathArgument.getType())
+ : pathArgument;
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java
new file mode 100644
index 000000000..728c4f80d
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/ReflectionUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util;
+
+import com.google.common.base.Optional;
+import java.lang.reflect.Method;
+import java.util.List;
+import javax.annotation.Nonnull;
+
+/**
+ * Reflection based utilities
+ */
+public final class ReflectionUtils {
+
+ private ReflectionUtils() {}
+
+ /**
+ * Find a specific method using reflection
+ *
+ * @param managedType Class object to find method in
+ * @param prefix Method name prefix used when finding the method. Case does not matter.
+ * @param paramTypes List of input argument types
+ * @param retType Return type
+ *
+ * @return Found method or Optional.absent() if there's no such method
+ */
+ @Nonnull
+ public static Optional<Method> findMethodReflex(@Nonnull final Class<?> managedType,
+ @Nonnull final String prefix,
+ @Nonnull final List<Class<?>> paramTypes,
+ @Nonnull final Class<?> retType) {
+ for (Method method : managedType.getMethods()) {
+ if (isMethodMatch(prefix, paramTypes, retType, method)) {
+ return Optional.of(method);
+ }
+ }
+
+ return Optional.absent();
+ }
+
+ private static boolean isMethodMatch(final @Nonnull String prefix,
+ final @Nonnull List<Class<?>> paramTypes,
+ final @Nonnull Class<?> retType, final Method method) {
+ if (!method.getName().toLowerCase().startsWith(prefix.toLowerCase())) {
+ return false;
+ }
+
+ final Class<?>[] parameterTypes = method.getParameterTypes();
+ if (parameterTypes.length != paramTypes.size()) {
+ return false;
+ }
+
+ for (int i = 0; i < parameterTypes.length; i++) {
+ if (!parameterTypes[i].isAssignableFrom(paramTypes.get(i))) {
+ return false;
+ }
+ }
+
+ if (!method.getReturnType().equals(retType)) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java
new file mode 100644
index 000000000..6abc3b1eb
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/TransactionMappingContext.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Binding Transaction backed mapping context.
+ */
+public class TransactionMappingContext implements MappingContext {
+
+ private final ReadWriteTransaction readWriteTransaction;
+
+ // TODO make async
+
+ public TransactionMappingContext(final ReadWriteTransaction readWriteTransaction) {
+ this.readWriteTransaction = readWriteTransaction;
+ }
+
+ @Override
+ public <T extends DataObject> Optional<T> read(@Nonnull final InstanceIdentifier<T> currentId) {
+ try {
+ return readWriteTransaction.read(LogicalDatastoreType.OPERATIONAL, currentId).checkedGet();
+ } catch (ReadFailedException e) {
+ throw new IllegalStateException("Unable to perform read", e);
+ }
+ }
+
+ @Override
+ public void delete(final InstanceIdentifier<?> path) {
+ readWriteTransaction.delete(LogicalDatastoreType.OPERATIONAL, path);
+ }
+
+ @Override
+ public <T extends DataObject> void merge(final InstanceIdentifier<T> path, T data) {
+ readWriteTransaction.merge(LogicalDatastoreType.OPERATIONAL, path, data, true);
+ }
+
+ @Override
+ public <T extends DataObject> void put(final InstanceIdentifier<T> path, T data) {
+ readWriteTransaction.put(LogicalDatastoreType.OPERATIONAL, path, data, true);
+ }
+
+ public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+ return readWriteTransaction.submit();
+ }
+
+ @Override
+ public void close() {
+ readWriteTransaction.cancel();
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java
new file mode 100644
index 000000000..9bfbc2450
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/AbstractGenericReader.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public abstract class AbstractGenericReader<D extends DataObject, B extends Builder<D>> implements Reader<D, B> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericReader.class);
+
+ private final InstanceIdentifier<D> instanceIdentifier;
+
+ protected AbstractGenericReader(final InstanceIdentifier<D> managedDataObjectType) {
+ this.instanceIdentifier = RWUtils.makeIidWildcarded(managedDataObjectType);
+ }
+
+ @Nonnull
+ @Override
+ public final InstanceIdentifier<D> getManagedDataObjectType() {
+ return instanceIdentifier;
+ }
+
+ /**
+ * @param id {@link InstanceIdentifier} pointing to current node. In case of keyed list, key must be present.
+ *
+ */
+ protected Optional<D> readCurrent(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ LOG.debug("{}: Reading current: {}", this, id);
+ final B builder = getBuilder(id);
+ // Cache empty value to determine if anything has changed later TODO cache in a field
+ final D emptyValue = builder.build();
+
+ LOG.trace("{}: Reading current attributes", this);
+ readCurrentAttributes(id, builder, ctx);
+
+ // Need to check whether anything was filled in to determine if data is present or not.
+ final D built = builder.build();
+ final Optional<D> read = built.equals(emptyValue)
+ ? Optional.absent()
+ : Optional.of(built);
+
+ LOG.debug("{}: Current node read successfully. Result: {}", this, read);
+ return read;
+ }
+
+ @Nonnull
+ @Override
+ @SuppressWarnings("unchecked")
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ LOG.trace("{}: Reading : {}", this, id);
+ checkArgument(id.getTargetType().equals(getManagedDataObjectType().getTargetType()));
+ return readCurrent((InstanceIdentifier<D>) id, ctx);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Reader[%s]", getManagedDataObjectType().getTargetType().getSimpleName());
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java
new file mode 100644
index 000000000..68aa3956e
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/BindingBrokerReader.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Simple DataBroker backed reader allowing to delegate reads to different brokers.
+ */
+public final class BindingBrokerReader<D extends DataObject, B extends Builder<D>>
+ implements Reader<D, B>, AutoCloseable {
+
+ private final InstanceIdentifier<D> instanceIdentifier;
+ private final DataBroker dataBroker;
+ private final LogicalDatastoreType datastoreType;
+ private final ReflexiveReaderCustomizer<D, B> reflexiveReaderCustomizer;
+
+ public BindingBrokerReader(final InstanceIdentifier<D> instanceIdentifier,
+ final DataBroker dataBroker,
+ final LogicalDatastoreType datastoreType,
+ final Class<B> builderClass) {
+ this.reflexiveReaderCustomizer = new ReflexiveReaderCustomizer<>(instanceIdentifier.getTargetType(), builderClass);
+ this.instanceIdentifier = instanceIdentifier;
+ this.dataBroker = dataBroker;
+ this.datastoreType = datastoreType;
+ }
+
+ @Nonnull
+ @Override
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ try (final ReadOnlyTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction()) {
+ final CheckedFuture<? extends Optional<? extends DataObject>, org.opendaylight.controller.md.sal.common.api.data.ReadFailedException>
+ read = readOnlyTransaction.read(datastoreType, id);
+ try {
+ return read.checkedGet();
+ } catch (org.opendaylight.controller.md.sal.common.api.data.ReadFailedException e) {
+ throw new ReadFailedException(id, e);
+ }
+ }
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue) {
+ reflexiveReaderCustomizer.merge(parentBuilder, readValue);
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(final InstanceIdentifier<D> id) {
+ return reflexiveReaderCustomizer.getBuilder(id);
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final B builder,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Nonnull
+ @Override
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return instanceIdentifier;
+ }
+
+ @Override
+ public void close() throws Exception {
+ // Noop
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java
new file mode 100644
index 000000000..d782bcc7f
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/KeepaliveReaderWrapper.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.ModificationCache;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import java.io.Closeable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Reader wrapper that periodically invokes a read to determine whether reads are still fully functional.
+ * In case a specific error occurs, Keep-alive failure listener gets notified.
+ */
+public final class KeepaliveReaderWrapper<D extends DataObject, B extends Builder<D>> implements Reader<D, B>, Runnable, Closeable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(KeepaliveReaderWrapper.class);
+
+ private static final NoopReadContext CTX = new NoopReadContext();
+
+ private final Reader<D, B> delegate;
+ private final Class<? extends Exception> exceptionType;
+ private final KeepaliveFailureListener failureListener;
+ private final ScheduledFuture<?> scheduledFuture;
+
+ /**
+ * Create new Keepalive wrapper
+ *
+ * @param delegate underlying reader performing actual reads
+ * @param executor scheduled executor service to schedule keepalive calls
+ * @param exception type of exception used to differentiate keepalive exception from other exceptions
+ * @param delayInSeconds number of seconds to wait between keepalive calls
+ * @param failureListener listener to be called whenever a keepalive failure is detected
+ */
+ public KeepaliveReaderWrapper(@Nonnull final Reader<D, B> delegate,
+ @Nonnull final ScheduledExecutorService executor,
+ @Nonnull final Class<? extends Exception> exception,
+ @Nonnegative final int delayInSeconds,
+ @Nonnull final KeepaliveFailureListener failureListener) {
+ this.delegate = delegate;
+ this.exceptionType = exception;
+ this.failureListener = failureListener;
+ Preconditions.checkArgument(delayInSeconds > 0, "Delay cannot be < 0");
+ LOG.debug("Starting keep-alive execution on top of: {} with delay of: {} seconds", delegate, delayInSeconds);
+ scheduledFuture = executor.scheduleWithFixedDelay(this, delayInSeconds, delayInSeconds, TimeUnit.SECONDS);
+ }
+
+ @Nonnull
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ return delegate.read(id, ctx);
+ }
+
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final B builder,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ delegate.readCurrentAttributes(id, builder, ctx);
+ }
+
+ @Nonnull
+ public B getBuilder(final InstanceIdentifier<D> id) {
+ return delegate.getBuilder(id);
+ }
+
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder,
+ @Nonnull final D readValue) {
+ delegate.merge(parentBuilder, readValue);
+ }
+
+ @Nonnull
+ @Override
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return delegate.getManagedDataObjectType();
+ }
+
+ @Override
+ public void run() {
+ LOG.trace("Invoking keepalive");
+ try {
+ final Optional<? extends DataObject> read = read(delegate.getManagedDataObjectType(), CTX);
+ LOG.debug("Keepalive executed successfully with data: {}", read);
+ } catch (Exception e) {
+ if (exceptionType.isAssignableFrom(e.getClass())) {
+ LOG.warn("Keepalive failed. Notifying listener", e);
+ failureListener.onKeepaliveFailure();
+ }
+ LOG.warn("Keepalive failed unexpectedly", e);
+ throw new IllegalArgumentException("Unexpected failure during keep-alive execution", e);
+ }
+ }
+
+ @Override
+ public void close() {
+ // Do not interrupt, it's not our executor
+ scheduledFuture.cancel(false);
+ }
+
+ /**
+ * Listener that gets called whenever keepalive fails as expected
+ */
+ public interface KeepaliveFailureListener {
+
+ void onKeepaliveFailure();
+ }
+
+ private static final class NoopMappingContext implements MappingContext {
+ @Override
+ public <T extends DataObject> Optional<T> read(@Nonnull final InstanceIdentifier<T> currentId) {
+ return Optional.absent();
+ }
+
+ @Override
+ public void delete(final InstanceIdentifier<?> path) {}
+
+ @Override
+ public <T extends DataObject> void merge(final InstanceIdentifier<T> path, final T data) {}
+
+ @Override
+ public <T extends DataObject> void put(final InstanceIdentifier<T> path, final T data) {}
+
+ @Override
+ public void close() {}
+ }
+
+ private static class NoopReadContext implements ReadContext {
+
+ private final ModificationCache modificationCache = new ModificationCache();
+
+ @Nonnull
+ @Override
+ public ModificationCache getModificationCache() {
+ return modificationCache;
+ }
+
+ @Nonnull
+ @Override
+ public MappingContext getMappingContext() {
+ return new NoopMappingContext();
+ }
+
+ @Override
+ public void close() {
+
+ }
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java
new file mode 100644
index 000000000..a4de9febb
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/NoopReaderCustomizer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.spi.read.ReaderCustomizer;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public abstract class NoopReaderCustomizer<C extends DataObject, B extends Builder<C>> implements
+ ReaderCustomizer<C, B> {
+
+ @Override
+ public void readCurrentAttributes(InstanceIdentifier<C> id, final B builder, final ReadContext context) throws
+ ReadFailedException {
+ // Noop
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveListReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveListReaderCustomizer.java
new file mode 100644
index 000000000..8ad323cc3
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveListReaderCustomizer.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.spi.read.ListReaderCustomizer;
+import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+
+/**
+ * Might be slow !
+ */
+public abstract class ReflexiveListReaderCustomizer<C extends DataObject & Identifiable<K>, K extends Identifier<C>, B extends Builder<C>>
+ extends ReflexiveReaderCustomizer<C, B>
+ implements ListReaderCustomizer<C, K, B> {
+
+
+ public ReflexiveListReaderCustomizer(final Class<C> typeClass, final Class<B> builderClass) {
+ super(typeClass, builderClass);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue) {
+ merge(parentBuilder, Collections.singletonList(readValue));
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final List<C> readData) {
+ final Optional<Method> method =
+ ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set" + getTypeClass().getSimpleName(),
+ Collections.singletonList(List.class), parentBuilder.getClass());
+
+ checkArgument(method.isPresent(), "Unable to set %s to %s", readData, parentBuilder);
+
+ try {
+ method.get().invoke(parentBuilder, readData);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Unable to set " + readData + " to " + parentBuilder, e);
+ }
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java
new file mode 100644
index 000000000..2b2d9300b
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReader.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Reader that performs no read operation on its own, just fills in the hierarchy.
+ * <p/>
+ * Might be slow due to reflection !
+ */
+public class ReflexiveReader<C extends DataObject, B extends Builder<C>> extends AbstractGenericReader<C, B> {
+
+ private final ReflexiveReaderCustomizer<C, B> customizer;
+
+ public ReflexiveReader(final InstanceIdentifier<C> identifier, final Class<B> builderClass) {
+ super(identifier);
+ this.customizer = new ReflexiveReaderCustomizer<>(identifier.getTargetType(), builderClass);
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<C> id, @Nonnull final B builder,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ customizer.readCurrentAttributes(id, builder, ctx);
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(final InstanceIdentifier<C> id) {
+ return customizer.getBuilder(id);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue) {
+ customizer.merge(parentBuilder, readValue);
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java
new file mode 100644
index 000000000..a6b9bf08e
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/ReflexiveReaderCustomizer.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Might be slow !
+ */
+class ReflexiveReaderCustomizer<C extends DataObject, B extends Builder<C>> extends NoopReaderCustomizer<C, B> {
+
+ private final Class<C> typeClass;
+ private final Class<B> builderClass;
+
+ public ReflexiveReaderCustomizer(final Class<C> typeClass, final Class<B> builderClass) {
+ this.typeClass = typeClass;
+ this.builderClass = builderClass;
+ }
+
+ protected Class<C> getTypeClass() {
+ return typeClass;
+ }
+
+ protected Class<B> getBuilderClass() {
+ return builderClass;
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(@Nonnull InstanceIdentifier<C> id) {
+ try {
+ return builderClass.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new IllegalStateException("Unable to instantiate " + builderClass, e);
+ }
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final C readValue) {
+ if (Augmentation.class.isAssignableFrom(typeClass)) {
+ mergeAugmentation(parentBuilder, (Class<? extends Augmentation<?>>) typeClass, readValue);
+ } else {
+ mergeRegular(parentBuilder, readValue);
+ }
+ }
+
+ private static void mergeRegular(@Nonnull final Builder<? extends DataObject> parentBuilder,
+ @Nonnull final DataObject readValue) {
+ final Optional<Method> method =
+ ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "set",
+ Collections.singletonList(readValue.getClass()), parentBuilder.getClass());
+
+ checkArgument(method.isPresent(), "Unable to set %s to %s", readValue, parentBuilder);
+
+ try {
+ method.get().invoke(parentBuilder, readValue);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e);
+ }
+ }
+
+ private static void mergeAugmentation(@Nonnull final Builder<? extends DataObject> parentBuilder,
+ @Nonnull final Class<? extends Augmentation<?>> typeClass,
+ @Nonnull final DataObject readValue) {
+ final Optional<Method> method =
+ ReflectionUtils.findMethodReflex(parentBuilder.getClass(), "addAugmentation",
+ Lists.newArrayList(Class.class, Augmentation.class), parentBuilder.getClass());
+
+ checkArgument(method.isPresent(), "Not possible to add augmentations to builder: %s", parentBuilder);
+ try {
+ method.get().invoke(parentBuilder, typeClass, readValue);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Unable to set " + readValue + " to " + parentBuilder, e);
+ }
+ }
+
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java
new file mode 100644
index 000000000..aa9b2dc92
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReader.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.translate.read.ListReader;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.util.read.AbstractGenericReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class CompositeReader<D extends DataObject, B extends Builder<D>> extends AbstractGenericReader<D, B> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CompositeReader.class);
+
+ private final Reader<D, B> delegate;
+ private final ImmutableMap<Class<?>, Reader<? extends DataObject, ? extends Builder<?>>> childReaders;
+
+ private CompositeReader(final Reader<D, B> reader,
+ final ImmutableMap<Class<?>, Reader<? extends DataObject, ? extends Builder<?>>> childReaders) {
+ super(reader.getManagedDataObjectType());
+ this.delegate = reader;
+ this.childReaders = childReaders;
+ }
+
+ @VisibleForTesting
+ ImmutableMap<Class<?>, Reader<? extends DataObject, ? extends Builder<?>>> getChildReaders() {
+ return childReaders;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <D extends DataObject> InstanceIdentifier<D> appendTypeToId(
+ final InstanceIdentifier<? extends DataObject> parentId, final InstanceIdentifier<D> type) {
+ final InstanceIdentifier.PathArgument t = new InstanceIdentifier.Item<>(type.getTargetType());
+ return (InstanceIdentifier<D>) InstanceIdentifier.create(Iterables.concat(
+ parentId.getPathArguments(), Collections.singleton(t)));
+ }
+
+ @Nonnull
+ @Override
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ if (shouldReadCurrent(id)) {
+ LOG.trace("{}: Reading current: {}", this, id);
+ return readCurrent((InstanceIdentifier<D>) id, ctx);
+ } else if (shouldDelegateToChild(id)) {
+ LOG.trace("{}: Reading child: {}", this, id);
+ return readSubtree(id, ctx);
+ } else {
+ // Fallback
+ LOG.trace("{}: Delegating read: {}", this, id);
+ return delegate.read(id, ctx);
+ }
+ }
+
+ private boolean shouldReadCurrent(@Nonnull final InstanceIdentifier<? extends DataObject> id) {
+ return id.getTargetType().equals(getManagedDataObjectType().getTargetType());
+ }
+
+ private boolean shouldDelegateToChild(@Nonnull final InstanceIdentifier<? extends DataObject> id) {
+ return childReaders.containsKey(RWUtils.getNextId(id, getManagedDataObjectType()).getType());
+ }
+
+ private Optional<? extends DataObject> readSubtree(final InstanceIdentifier<? extends DataObject> id,
+ final ReadContext ctx) throws ReadFailedException {
+ final InstanceIdentifier.PathArgument nextId = RWUtils.getNextId(id, getManagedDataObjectType());
+ final Reader<?, ? extends Builder<?>> nextReader = childReaders.get(nextId.getType());
+ checkArgument(nextReader != null, "Unable to read: %s. No delegate present, available readers at next level: %s",
+ id, childReaders.keySet());
+ return nextReader.read(id, ctx);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void readChildren(final InstanceIdentifier<D> id, @Nonnull final ReadContext ctx, final B builder)
+ throws ReadFailedException {
+ LOG.debug("{}: Reading children: {}", this, childReaders.keySet());
+ for (Reader child : childReaders.values()) {
+ final InstanceIdentifier childId = appendTypeToId(id, child.getManagedDataObjectType());
+
+ LOG.debug("{}: Reading child from: {}", this, child);
+ if (child instanceof ListReader) {
+ final List<? extends DataObject> list = ((ListReader) child).readList(childId, ctx);
+ ((ListReader) child).merge(builder, list);
+ } else {
+ final Optional<? extends DataObject> read = child.read(childId, ctx);
+ if (read.isPresent()) {
+ child.merge(builder, read.get());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final B builder,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ delegate.readCurrentAttributes(id, builder, ctx);
+ readChildren(id, ctx, builder);
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(final InstanceIdentifier<D> id) {
+ return delegate.getBuilder(id);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue) {
+ delegate.merge(parentBuilder, readValue);
+ }
+
+ /**
+ * Wrap a Reader as a Composite Reader.
+ */
+ static <D extends DataObject, B extends Builder<D>> Reader<D, B> createForReader(
+ @Nonnull final Reader<D, B> reader,
+ @Nonnull final ImmutableMap<Class<?>, Reader<?, ? extends Builder<?>>> childReaders) {
+
+ return (reader instanceof ListReader)
+ ? new CompositeListReader<>((ListReader) reader, childReaders)
+ : new CompositeReader<>(reader, childReaders);
+ }
+
+ private static class CompositeListReader<D extends DataObject & Identifiable<K>, B extends Builder<D>, K extends Identifier<D>>
+ extends CompositeReader<D, B>
+ implements ListReader<D, K, B> {
+
+ private final ListReader<D, K, B> delegate;
+
+ private CompositeListReader(final ListReader<D, K, B> reader,
+ final ImmutableMap<Class<?>, Reader<? extends DataObject, ? extends Builder<?>>> childReaders) {
+ super(reader, childReaders);
+ this.delegate = reader;
+ }
+
+ @Nonnull
+ @Override
+ public List<D> readList(@Nonnull final InstanceIdentifier<D> id, @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ LOG.trace("{}: Reading all list entries", this);
+ final List<K> allIds = delegate.getAllIds(id, ctx);
+ LOG.debug("{}: Reading list entries for: {}", this, allIds);
+
+ // Override read list in order to perform readCurrent + readChildren here
+ final ArrayList<D> allEntries = new ArrayList<>(allIds.size());
+ for (K key : allIds) {
+ final InstanceIdentifier.IdentifiableItem<D, K> currentBdItem = RWUtils.getCurrentIdItem(id, key);
+ final InstanceIdentifier<D> keyedId = RWUtils.replaceLastInId(id, currentBdItem);
+ final Optional<D> read = readCurrent(keyedId, ctx);
+ if (read.isPresent()) {
+ final DataObject singleItem = read.get();
+ checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(singleItem.getClass()));
+ allEntries.add(getManagedDataObjectType().getTargetType().cast(singleItem));
+ }
+ }
+ return allEntries;
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<D> readData) {
+ delegate.merge(builder, readData);
+ }
+
+ @Override
+ public List<K> getAllIds(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ return delegate.getAllIds(id, ctx);
+ }
+ }
+
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java
new file mode 100644
index 000000000..a9f606ae2
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistry.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.translate.read.ListReader;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple reader registry able to perform and aggregated read (ROOT read) on top of all provided readers. Also able to
+ * delegate a specific read to one of the delegate readers.
+ * <p/>
+ * This could serve as a utility to hold & hide all available readers in upper layers.
+ */
+public final class CompositeReaderRegistry implements ReaderRegistry {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistry.class);
+
+ private final Map<Class<? extends DataObject>, Reader<? extends DataObject, ? extends Builder<?>>> rootReaders;
+
+ /**
+ * Create new {@link CompositeReaderRegistry}.
+ *
+ * @param rootReaders List of delegate readers
+ */
+ public CompositeReaderRegistry(@Nonnull final List<Reader<? extends DataObject, ? extends Builder<?>>> rootReaders) {
+ this.rootReaders = RWUtils.uniqueLinkedIndex(checkNotNull(rootReaders), RWUtils.MANAGER_CLASS_FUNCTION);
+ }
+
+ @VisibleForTesting
+ Map<Class<? extends DataObject>, Reader<? extends DataObject, ? extends Builder<?>>> getRootReaders() {
+ return rootReaders;
+ }
+
+ @Override
+ @Nonnull
+ public Multimap<InstanceIdentifier<? extends DataObject>, ? extends DataObject> readAll(
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+
+ LOG.debug("Reading from all delegates: {}", this);
+ LOG.trace("Reading from all delegates: {}", rootReaders.values());
+
+ final Multimap<InstanceIdentifier<? extends DataObject>, DataObject> objects = LinkedListMultimap.create();
+ for (Reader<? extends DataObject, ? extends Builder<?>> rootReader : rootReaders.values()) {
+ LOG.debug("Reading from delegate: {}", rootReader);
+
+ if (rootReader instanceof ListReader) {
+ final List<? extends DataObject> listEntries =
+ ((ListReader) rootReader).readList(rootReader.getManagedDataObjectType(), ctx);
+ if (!listEntries.isEmpty()) {
+ objects.putAll(rootReader.getManagedDataObjectType(), listEntries);
+ }
+ } else {
+ final Optional<? extends DataObject> read = rootReader.read(rootReader.getManagedDataObjectType(), ctx);
+ if (read.isPresent()) {
+ objects.putAll(rootReader.getManagedDataObjectType(), Collections.singletonList(read.get()));
+ }
+ }
+ }
+
+ return objects;
+ }
+
+ @Nonnull
+ @Override
+ public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ final InstanceIdentifier.PathArgument first = checkNotNull(
+ Iterables.getFirst(id.getPathArguments(), null), "Empty id");
+ final Reader<? extends DataObject, ? extends Builder<?>> reader = rootReaders.get(first.getType());
+ checkNotNull(reader,
+ "Unable to read %s. Missing reader. Current readers for: %s", id, rootReaders.keySet());
+ LOG.debug("Reading from delegate: {}", reader);
+ return reader.read(id, ctx);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName()
+ + rootReaders.keySet().stream().map(Class::getSimpleName).collect(Collectors.toList());
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java
new file mode 100644
index 000000000..3adda713d
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilder.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import com.google.common.collect.ImmutableMap;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.read.registry.ModifiableReaderRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder;
+import io.fd.honeycomb.v3po.translate.util.read.ReflexiveReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@NotThreadSafe
+public final class CompositeReaderRegistryBuilder
+ extends AbstractSubtreeManagerRegistryBuilderBuilder<Reader<? extends DataObject, ? extends Builder<?>>, ReaderRegistry>
+ implements ModifiableReaderRegistryBuilder, ReaderRegistryBuilder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CompositeReaderRegistryBuilder.class);
+
+ @Override
+ protected Reader<? extends DataObject, ? extends Builder<?>> getSubtreeHandler(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Reader<? extends DataObject, ? extends Builder<?>> reader) {
+ return SubtreeReader.createForReader(handledChildren, reader);
+ }
+
+ @Override
+ public <D extends DataObject> void addStructuralReader(@Nonnull InstanceIdentifier<D> id,
+ @Nonnull Class<? extends Builder<D>> builderType) {
+ add(new ReflexiveReader<>(id, builderType));
+ }
+
+ /**
+ * Create {@link CompositeReaderRegistry} with Readers ordered according to submitted relationships.
+ * <p/>
+ * Note: The ordering only applies between nodes on the same level, inter-level and inter-subtree relationships are
+ * ignored.
+ */
+ @Override
+ public ReaderRegistry build() {
+ ImmutableMap<InstanceIdentifier<?>, Reader<? extends DataObject, ? extends Builder<?>>> mappedReaders =
+ getMappedHandlers();
+ LOG.debug("Building Reader registry with Readers: {}",
+ mappedReaders.keySet().stream()
+ .map(InstanceIdentifier::getTargetType)
+ .map(Class::getSimpleName)
+ .collect(Collectors.joining(", ")));
+
+ LOG.trace("Building Reader registry with Readers: {}", mappedReaders);
+ final List<InstanceIdentifier<?>> readerOrder = new ArrayList<>(mappedReaders.keySet());
+
+ // Wrap readers into composite readers recursively, collect roots and create registry
+ final TypeHierarchy typeHierarchy = TypeHierarchy.create(mappedReaders.keySet());
+ final List<Reader<? extends DataObject, ? extends Builder<?>>> orderedRootReaders =
+ typeHierarchy.getRoots().stream()
+ .map(rootId -> toCompositeReader(rootId, mappedReaders, typeHierarchy))
+ .collect(Collectors.toList());
+
+ // We are violating the ordering from mappedReaders, since we are forming a composite structure
+ // but at least order root writers
+ orderedRootReaders.sort((reader1, reader2) -> readerOrder.indexOf(reader1.getManagedDataObjectType())
+ - readerOrder.indexOf(reader2.getManagedDataObjectType()));
+
+ return new CompositeReaderRegistry(orderedRootReaders);
+ }
+
+ private Reader<? extends DataObject, ? extends Builder<?>> toCompositeReader(
+ final InstanceIdentifier<?> instanceIdentifier,
+ final ImmutableMap<InstanceIdentifier<?>, Reader<? extends DataObject, ? extends Builder<?>>> mappedReaders,
+ final TypeHierarchy typeHierarchy) {
+
+ // Order child readers according to the mappedReadersCollection
+ final ImmutableMap.Builder<Class<?>, Reader<?, ? extends Builder<?>>> childReadersMapB = ImmutableMap.builder();
+ for (InstanceIdentifier<?> childId : mappedReaders.keySet()) {
+ if (typeHierarchy.getDirectChildren(instanceIdentifier).contains(childId)) {
+ childReadersMapB.put(childId.getTargetType(), toCompositeReader(childId, mappedReaders, typeHierarchy));
+ }
+ }
+
+ final ImmutableMap<Class<?>, Reader<?, ? extends Builder<?>>> childReadersMap = childReadersMapB.build();
+ return childReadersMap.isEmpty()
+ ? mappedReaders.get(instanceIdentifier)
+ : CompositeReader.createForReader(mappedReaders.get(instanceIdentifier), childReadersMap);
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java
new file mode 100644
index 000000000..50a20656e
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReader.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.translate.read.ListReader;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.ReadFailedException;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.util.ReflectionUtils;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.Identifiable;
+import org.opendaylight.yangtools.yang.binding.Identifier;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple Reader delegate for subtree Readers (Readers handling also children nodes) providing a list of all the
+ * children nodes being handled.
+ */
+class SubtreeReader<D extends DataObject, B extends Builder<D>> implements Reader<D, B> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SubtreeReader.class);
+
+ private final Reader<D, B> delegate;
+ private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>();
+
+ private SubtreeReader(final Reader<D, B> delegate, Set<InstanceIdentifier<?>> handledTypes) {
+ this.delegate = delegate;
+ for (InstanceIdentifier<?> handledType : handledTypes) {
+ // Iid has to start with Reader's handled root type
+ checkArgument(delegate.getManagedDataObjectType().getTargetType().equals(
+ handledType.getPathArguments().iterator().next().getType()),
+ "Handled node from subtree has to be identified by an instance identifier starting from: %s."
+ + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType);
+ checkArgument(Iterables.size(handledType.getPathArguments()) > 1,
+ "Handled node from subtree identifier too short: %s", handledType);
+ handledChildTypes.add(InstanceIdentifier.create(Iterables.concat(
+ getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1))));
+ }
+ }
+
+ /**
+ * Return set of types also handled by this Reader. All of the types are children of the type managed by this Reader
+ * excluding the type of this Reader.
+ */
+ Set<InstanceIdentifier<?>> getHandledChildTypes() {
+ return handledChildTypes;
+ }
+
+ @Override
+ @Nonnull
+ public Optional<? extends DataObject> read(
+ @Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ final InstanceIdentifier<?> wildcarded = RWUtils.makeIidWildcarded(id);
+
+ // Reading entire subtree and filtering if is current reader responsible
+ if (getHandledChildTypes().contains(wildcarded)) {
+ LOG.debug("{}: Subtree node managed by this writer requested: {}. Reading current and filtering", this, id);
+ // If there's no dedicated reader, use read current
+ final InstanceIdentifier<D> currentId = RWUtils.cutId(id, getManagedDataObjectType());
+ final Optional<? extends DataObject> current = delegate.read(currentId, ctx);
+ // then perform post-reading filtering (return only requested sub-node)
+ final Optional<? extends DataObject> readSubtree = current.isPresent()
+ ? filterSubtree(current.get(), id, getManagedDataObjectType().getTargetType())
+ : current;
+
+ LOG.debug("{}: Subtree: {} read successfully. Result: {}", this, id, readSubtree);
+ return readSubtree;
+
+ // Fallback solution, try delegate, maybe it can read the ID
+ } else {
+ return delegate.read(id, ctx);
+ }
+ }
+
+ @Override
+ public void readCurrentAttributes(@Nonnull final InstanceIdentifier<D> id, @Nonnull final B builder,
+ @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ delegate.readCurrentAttributes(id, builder, ctx);
+ }
+
+ @Nonnull
+ @Override
+ public B getBuilder(final InstanceIdentifier<D> id) {
+ return delegate.getBuilder(id);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final D readValue) {
+ delegate.merge(parentBuilder, readValue);
+ }
+
+ @Nonnull
+ private static Optional<? extends DataObject> filterSubtree(@Nonnull final DataObject parent,
+ @Nonnull final InstanceIdentifier<? extends DataObject> absolutPath,
+ @Nonnull final Class<?> managedType) {
+ final InstanceIdentifier.PathArgument nextId =
+ RWUtils.getNextId(absolutPath, InstanceIdentifier.create(parent.getClass()));
+
+ final Optional<? extends DataObject> nextParent = findNextParent(parent, nextId, managedType);
+
+ if (Iterables.getLast(absolutPath.getPathArguments()).equals(nextId)) {
+ return nextParent; // we found the dataObject identified by absolutePath
+ } else if (nextParent.isPresent()) {
+ return filterSubtree(nextParent.get(), absolutPath, nextId.getType());
+ } else {
+ return nextParent; // we can't go further, return Optional.absent()
+ }
+ }
+
+ private static Optional<? extends DataObject> findNextParent(@Nonnull final DataObject parent,
+ @Nonnull final InstanceIdentifier.PathArgument nextId,
+ @Nonnull final Class<?> managedType) {
+ // TODO is there a better way than reflection ? e.g. convert into NN and filter out with a utility
+ Optional<Method> method = ReflectionUtils.findMethodReflex(managedType, "get",
+ Collections.emptyList(), nextId.getType());
+
+ if (method.isPresent()) {
+ return Optional.fromNullable(filterSingle(parent, nextId, method.get()));
+ } else {
+ // List child nodes
+ method = ReflectionUtils.findMethodReflex(managedType,
+ "get" + nextId.getType().getSimpleName(), Collections.emptyList(), List.class);
+
+ if (method.isPresent()) {
+ return filterList(parent, nextId, method.get());
+ } else {
+ throw new IllegalStateException(
+ "Unable to filter " + nextId + " from " + parent + " getters not found using reflexion");
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Optional<? extends DataObject> filterList(final DataObject parent,
+ final InstanceIdentifier.PathArgument nextId,
+ final Method method) {
+ final List<? extends DataObject> invoke = (List<? extends DataObject>) invoke(method, nextId, parent);
+
+ checkArgument(nextId instanceof InstanceIdentifier.IdentifiableItem<?, ?>,
+ "Unable to perform wildcarded read for %s", nextId);
+ final Identifier key = ((InstanceIdentifier.IdentifiableItem) nextId).getKey();
+ // TODO replace with stream().filter().findFirst() when we switch to using java's Optional instead of Guava's
+ // because now we would have to do awkward Optional transformation since findFirstReturns guava's optional
+ return Iterables.tryFind(invoke, new Predicate<DataObject>() {
+
+ @Override
+ public boolean apply(@Nullable final DataObject input) {
+ final Optional<Method> keyGetter = ReflectionUtils.findMethodReflex(nextId.getType(), "get",
+ Collections.emptyList(), key.getClass());
+ final Object actualKey;
+ actualKey = invoke(keyGetter.get(), nextId, input);
+ return key.equals(actualKey);
+ }
+ });
+ }
+
+ private static DataObject filterSingle(final DataObject parent,
+ final InstanceIdentifier.PathArgument nextId, final Method method) {
+ return nextId.getType().cast(invoke(method, nextId, parent));
+ }
+
+ private static Object invoke(final Method method,
+ final InstanceIdentifier.PathArgument nextId, final DataObject parent) {
+ try {
+ return method.invoke(parent);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new IllegalArgumentException("Unable to get " + nextId + " from " + parent, e);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return delegate.getManagedDataObjectType();
+ }
+
+ /**
+ * Wrap a Reader as a subtree Reader.
+ */
+ static <D extends DataObject, B extends Builder<D>> Reader<D, B> createForReader(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Reader<D, B> reader) {
+ return (reader instanceof ListReader)
+ ? new SubtreeListReader<>((ListReader) reader, handledChildren)
+ : new SubtreeReader<>(reader, handledChildren);
+ }
+
+ private static final class SubtreeListReader<D extends DataObject & Identifiable<K>, B extends Builder<D>, K extends Identifier<D>>
+ extends SubtreeReader<D, B> implements ListReader<D, K, B> {
+
+ private final ListReader<D, K, B> delegate;
+
+ private SubtreeListReader(final ListReader<D, K, B> delegate,
+ final Set<InstanceIdentifier<?>> handledTypes) {
+ super(delegate, handledTypes);
+ this.delegate = delegate;
+ }
+
+ @Nonnull
+ @Override
+ public List<D> readList(@Nonnull final InstanceIdentifier<D> id, @Nonnull final ReadContext ctx)
+ throws ReadFailedException {
+ return delegate.readList(id, ctx);
+ }
+
+ @Override
+ public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<D> readData) {
+ delegate.merge(builder, readData);
+ }
+
+ @Override
+ public List<K> getAllIds(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final ReadContext ctx) throws ReadFailedException {
+ return delegate.getAllIds(id, ctx);
+ }
+ }
+
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java
new file mode 100644
index 000000000..005e3bc8d
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchy.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.Iterables;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+final class TypeHierarchy {
+ private final DirectedAcyclicGraph<InstanceIdentifier<?>, Parent> hierarchy;
+
+ private TypeHierarchy(@Nonnull final DirectedAcyclicGraph<InstanceIdentifier<?>, Parent> hierarchy) {
+ this.hierarchy = hierarchy;
+ }
+
+ Set<InstanceIdentifier<?>> getAllChildren(InstanceIdentifier<?> id) {
+ final HashSet<InstanceIdentifier<?>> instanceIdentifiers = new HashSet<>();
+ for (InstanceIdentifier<?> childId : getDirectChildren(id)) {
+ instanceIdentifiers.add(childId);
+ instanceIdentifiers.addAll(getAllChildren(childId));
+ }
+ return instanceIdentifiers;
+ }
+
+ Set<InstanceIdentifier<?>> getDirectChildren(InstanceIdentifier<?> id) {
+ checkArgument(hierarchy.vertexSet().contains(id),
+ "Unknown reader: %s. Known readers: %s", id, hierarchy.vertexSet());
+
+ return hierarchy.outgoingEdgesOf(id).stream()
+ .map(hierarchy::getEdgeTarget)
+ .collect(Collectors.toSet());
+ }
+
+ Set<InstanceIdentifier<?>> getRoots() {
+ return hierarchy.vertexSet().stream()
+ .filter(vertex -> hierarchy.incomingEdgesOf(vertex).size() == 0)
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Create reader hierarchy from a flat set of instance identifiers.
+ *
+ * @param allIds Set of unkeyed instance identifiers
+ */
+ static TypeHierarchy create(@Nonnull Set<InstanceIdentifier<?>> allIds) {
+ final DirectedAcyclicGraph<InstanceIdentifier<?>, Parent>
+ readersHierarchy = new DirectedAcyclicGraph<>((sourceVertex, targetVertex) -> new Parent());
+
+ for (InstanceIdentifier<?> allId : allIds) {
+ checkArgument(!Iterables.isEmpty(allId.getPathArguments()), "Empty ID detected");
+
+ if (Iterables.size(allId.getPathArguments()) == 1) {
+ readersHierarchy.addVertex(allId);
+ }
+
+ List<InstanceIdentifier.PathArgument> pathArgs = new LinkedList<>();
+ pathArgs.add(allId.getPathArguments().iterator().next());
+
+ for (InstanceIdentifier.PathArgument pathArgument : Iterables.skip(allId.getPathArguments(), 1)) {
+ final InstanceIdentifier<?> previous = InstanceIdentifier.create(pathArgs);
+ pathArgs.add(pathArgument);
+ final InstanceIdentifier<?> current = InstanceIdentifier.create(pathArgs);
+
+ readersHierarchy.addVertex(previous);
+ readersHierarchy.addVertex(current);
+
+ try {
+ readersHierarchy.addDagEdge(previous, current);
+ } catch (DirectedAcyclicGraph.CycleFoundException e) {
+ throw new IllegalArgumentException("Loop in hierarchy detected", e);
+ }
+ }
+ }
+
+ return new TypeHierarchy(readersHierarchy);
+ }
+
+ private static final class Parent{}
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java
new file mode 100644
index 000000000..44b36edae
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/AbstractGenericWriter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractGenericWriter<D extends DataObject> implements Writer<D> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractGenericWriter.class);
+
+ private final InstanceIdentifier<D> instanceIdentifier;
+
+ protected AbstractGenericWriter(final InstanceIdentifier<D> type) {
+ this.instanceIdentifier = RWUtils.makeIidWildcarded(type);
+ }
+
+ protected void writeCurrent(final InstanceIdentifier<D> id, final D data, final WriteContext ctx)
+ throws WriteFailedException {
+ LOG.debug("{}: Writing current: {} data: {}", this, id, data);
+ writeCurrentAttributes(id, data, ctx);
+ LOG.debug("{}: Current node written successfully", this);
+ }
+
+ protected void updateCurrent(final InstanceIdentifier<D> id, final D dataBefore, final D dataAfter,
+ final WriteContext ctx) throws WriteFailedException {
+ LOG.debug("{}: Updating current: {} dataBefore: {}, datAfter: {}", this, id, dataBefore, dataAfter);
+
+ if (dataBefore.equals(dataAfter)) {
+ LOG.debug("{}: Skipping current(no update): {}", this, id);
+ // No change, ignore
+ return;
+ }
+ updateCurrentAttributes(id, dataBefore, dataAfter, ctx);
+ LOG.debug("{}: Current node updated successfully", this);
+ }
+
+ protected void deleteCurrent(final InstanceIdentifier<D> id, final D dataBefore, final WriteContext ctx)
+ throws WriteFailedException {
+ LOG.debug("{}: Deleting current: {} dataBefore: {}", this, id, dataBefore);
+ deleteCurrentAttributes(id, dataBefore, ctx);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ LOG.debug("{}: Updating : {}", this, id);
+ LOG.trace("{}: Updating : {}, from: {} to: {}", this, id, dataBefore, dataAfter);
+
+ checkArgument(idPointsToCurrent(id), "Cannot handle data: %s. Only: %s can be handled by writer: %s",
+ id, getManagedDataObjectType(), this);
+
+ if (isWrite(dataBefore, dataAfter)) {
+ writeCurrent((InstanceIdentifier<D>) id, castToManaged(dataAfter), ctx);
+ } else if (isDelete(dataBefore, dataAfter)) {
+ deleteCurrent((InstanceIdentifier<D>) id, castToManaged(dataBefore), ctx);
+ } else {
+ checkArgument(dataBefore != null && dataAfter != null, "No data to process");
+ updateCurrent((InstanceIdentifier<D>) id, castToManaged(dataBefore), castToManaged(dataAfter), ctx);
+ }
+ }
+
+ private void checkDataType(@Nonnull final DataObject dataAfter) {
+ checkArgument(getManagedDataObjectType().getTargetType().isAssignableFrom(dataAfter.getClass()));
+ }
+
+ private D castToManaged(final DataObject data) {
+ checkDataType(data);
+ return getManagedDataObjectType().getTargetType().cast(data);
+ }
+
+ private static boolean isWrite(final DataObject dataBefore,
+ final DataObject dataAfter) {
+ return dataBefore == null && dataAfter != null;
+ }
+
+ private static boolean isDelete(final DataObject dataBefore,
+ final DataObject dataAfter) {
+ return dataAfter == null && dataBefore != null;
+ }
+
+ private boolean idPointsToCurrent(final @Nonnull InstanceIdentifier<? extends DataObject> id) {
+ return id.getTargetType().equals(getManagedDataObjectType().getTargetType());
+ }
+
+ protected abstract void writeCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D data,
+ @Nonnull final WriteContext ctx) throws WriteFailedException;
+
+ protected abstract void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D dataBefore,
+ @Nonnull final WriteContext ctx) throws WriteFailedException;
+
+ protected abstract void updateCurrentAttributes(@Nonnull final InstanceIdentifier<D> id,
+ @Nonnull final D dataBefore,
+ @Nonnull final D dataAfter,
+ @Nonnull final WriteContext ctx) throws WriteFailedException;
+
+ @Nonnull
+ @Override
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return instanceIdentifier;
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("Writer[%s]", getManagedDataObjectType().getTargetType().getSimpleName());
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java
new file mode 100644
index 000000000..7c45fcd82
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/NoopWriterRegistry.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write;
+
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import javax.annotation.Nonnull;
+
+/**
+ * Empty registry that does not perform any changes. Can be used in data layer, if we want to disable passing data to
+ * translation layer.
+ */
+public class NoopWriterRegistry implements WriterRegistry, AutoCloseable {
+
+ @Override
+ public void update(@Nonnull final DataObjectUpdates updates,
+ @Nonnull final WriteContext ctx) throws TranslationException {
+ // NOOP
+ }
+
+ @Override
+ public void close() throws Exception {
+ // NOOP
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java
new file mode 100644
index 000000000..47498f594
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/TransactionWriteContext.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.ModificationCache;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+/**
+ * Transaction based WriteContext
+ */
+public final class TransactionWriteContext implements WriteContext {
+
+ private final DOMDataReadOnlyTransaction beforeTx;
+ private final DOMDataReadOnlyTransaction afterTx;
+ private final ModificationCache ctx;
+ private final BindingNormalizedNodeSerializer serializer;
+ private final MappingContext mappingContext;
+
+ public TransactionWriteContext(final BindingNormalizedNodeSerializer serializer,
+ final DOMDataReadOnlyTransaction beforeTx,
+ final DOMDataReadOnlyTransaction afterTx,
+ final MappingContext mappingContext) {
+ this.serializer = serializer;
+ // TODO do we have a BA transaction adapter ? If so, use it here and don't pass serializer
+ this.beforeTx = beforeTx;
+ this.afterTx = afterTx;
+ this.mappingContext = mappingContext;
+ this.ctx = new ModificationCache();
+ }
+
+ // TODO make this asynchronous
+
+ @Override
+ public <T extends DataObject> Optional<T> readBefore(@Nonnull final InstanceIdentifier<T> currentId) {
+ return read(currentId, beforeTx);
+ }
+
+ @Override
+ public <T extends DataObject> Optional<T> readAfter(@Nonnull final InstanceIdentifier<T> currentId) {
+ return read(currentId, afterTx);
+ }
+
+
+ private <T extends DataObject> Optional<T> read(final InstanceIdentifier<T> currentId,
+ final DOMDataReadOnlyTransaction tx) {
+ final YangInstanceIdentifier path = serializer.toYangInstanceIdentifier(currentId);
+
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> read =
+ tx.read(LogicalDatastoreType.CONFIGURATION, path);
+
+ try {
+ // TODO once the APIs are asynchronous use just Futures.transform
+ final Optional<NormalizedNode<?, ?>> optional = read.checkedGet();
+
+ if (!optional.isPresent()) {
+ return Optional.absent();
+ }
+
+ final NormalizedNode<?, ?> data = optional.get();
+ final Map.Entry<InstanceIdentifier<?>, DataObject> entry = serializer.fromNormalizedNode(path, data);
+
+ final Class<T> targetType = currentId.getTargetType();
+ checkState(targetType.isAssignableFrom(entry.getValue().getClass()),
+ "Unexpected data object type, should be: %s, but was: %s", targetType, entry.getValue().getClass());
+ return Optional.of(targetType.cast(entry.getValue()));
+ } catch (ReadFailedException e) {
+ throw new IllegalStateException("Unable to perform read", e);
+ }
+ }
+
+ @Nonnull
+ @Override
+ public ModificationCache getModificationCache() {
+ return ctx;
+ }
+
+ @Nonnull
+ @Override
+ public MappingContext getMappingContext() {
+ return mappingContext;
+ }
+
+ /**
+ * Does not close the transactions
+ */
+ @Override
+ public void close() {
+ ctx.close();
+ beforeTx.close();
+ afterTx.close();
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java
new file mode 100644
index 000000000..7433de813
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistry.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.TranslationException;
+import io.fd.honeycomb.v3po.translate.util.RWUtils;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Flat writer registry, delegating updates to writers in the order writers were submitted.
+ */
+@ThreadSafe
+final class FlatWriterRegistry implements WriterRegistry {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistry.class);
+
+ // All types handled by writers directly or as children
+ private final ImmutableSet<InstanceIdentifier<?>> handledTypes;
+
+ private final Set<InstanceIdentifier<?>> writersOrderReversed;
+ private final Set<InstanceIdentifier<?>> writersOrder;
+ private final Map<InstanceIdentifier<?>, Writer<?>> writers;
+
+ /**
+ * Create flat registry instance.
+ *
+ * @param writers immutable, ordered map of writers to use to process updates. Order of the writers has to be
+ * one in which create and update operations should be handled. Deletes will be handled in reversed
+ * order. All deletes are handled before handling all the updates.
+ */
+ FlatWriterRegistry(@Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) {
+ this.writers = writers;
+ this.writersOrderReversed = Sets.newLinkedHashSet(Lists.reverse(Lists.newArrayList(writers.keySet())));
+ this.writersOrder = writers.keySet();
+ this.handledTypes = getAllHandledTypes(writers);
+ }
+
+ private static ImmutableSet<InstanceIdentifier<?>> getAllHandledTypes(
+ @Nonnull final ImmutableMap<InstanceIdentifier<?>, Writer<?>> writers) {
+ final ImmutableSet.Builder<InstanceIdentifier<?>> handledTypesBuilder = ImmutableSet.builder();
+ for (Map.Entry<InstanceIdentifier<?>, Writer<?>> writerEntry : writers.entrySet()) {
+ final InstanceIdentifier<?> writerType = writerEntry.getKey();
+ final Writer<?> writer = writerEntry.getValue();
+ handledTypesBuilder.add(writerType);
+ if (writer instanceof SubtreeWriter) {
+ handledTypesBuilder.addAll(((SubtreeWriter<?>) writer).getHandledChildTypes());
+ }
+ }
+ return handledTypesBuilder.build();
+ }
+
+ @Override
+ public void update(@Nonnull final DataObjectUpdates updates,
+ @Nonnull final WriteContext ctx) throws TranslationException {
+ if (updates.isEmpty()) {
+ return;
+ }
+
+ // Optimization
+ if (updates.containsOnlySingleType()) {
+ // First process delete
+ singleUpdate(updates.getDeletes(), ctx);
+ // Next is update
+ singleUpdate(updates.getUpdates(), ctx);
+ } else {
+ // First process deletes
+ bulkUpdate(updates.getDeletes(), ctx, true, writersOrderReversed);
+ // Next are updates
+ bulkUpdate(updates.getUpdates(), ctx, true, writersOrder);
+ }
+
+ LOG.debug("Update successful for types: {}", updates.getTypeIntersection());
+ LOG.trace("Update successful for: {}", updates);
+ }
+
+ private void singleUpdate(@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ @Nonnull final WriteContext ctx) throws WriteFailedException {
+ if (updates.isEmpty()) {
+ return;
+ }
+
+ final InstanceIdentifier<?> singleType = updates.keySet().iterator().next();
+ LOG.debug("Performing single type update for: {}", singleType);
+ Collection<? extends DataObjectUpdate> singleTypeUpdates = updates.get(singleType);
+ Writer<?> writer = getWriter(singleType);
+
+ if (writer == null) {
+ // This node must be handled by a subtree writer, find it and call it or else fail
+ checkArgument(handledTypes.contains(singleType), "Unable to process update. Missing writers for: %s",
+ singleType);
+ writer = getSubtreeWriterResponsible(singleType);
+ singleTypeUpdates = getParentDataObjectUpdate(ctx, updates, writer);
+ }
+
+ LOG.trace("Performing single type update with writer: {}", writer);
+ for (DataObjectUpdate singleUpdate : singleTypeUpdates) {
+ writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx);
+ }
+ }
+
+ private Writer<?> getSubtreeWriterResponsible(final InstanceIdentifier<?> singleType) {
+ final Writer<?> writer;// This is slow ( minor TODO-perf )
+ writer = writers.values().stream()
+ .filter(w -> w instanceof SubtreeWriter)
+ .filter(w -> ((SubtreeWriter<?>) w).getHandledChildTypes().contains(singleType))
+ .findFirst()
+ .get();
+ return writer;
+ }
+
+ private Collection<DataObjectUpdate> getParentDataObjectUpdate(final WriteContext ctx,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Writer<?> writer) {
+ // Now read data for subtree reader root, but first keyed ID is needed and that ID can be cut from updates
+ InstanceIdentifier<?> firstAffectedChildId = ((SubtreeWriter<?>) writer).getHandledChildTypes().stream()
+ .filter(updates::containsKey)
+ .map(unkeyedId -> updates.get(unkeyedId))
+ .flatMap(doUpdates -> doUpdates.stream())
+ .map(DataObjectUpdate::getId)
+ .findFirst()
+ .get();
+
+ final InstanceIdentifier<?> parentKeyedId =
+ RWUtils.cutId(firstAffectedChildId, writer.getManagedDataObjectType());
+
+ final Optional<? extends DataObject> parentBefore = ctx.readBefore(parentKeyedId);
+ final Optional<? extends DataObject> parentAfter = ctx.readAfter(parentKeyedId);
+ return Collections.singleton(
+ DataObjectUpdate.create(parentKeyedId, parentBefore.orNull(), parentAfter.orNull()));
+ }
+
+ private void bulkUpdate(@Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ @Nonnull final WriteContext ctx,
+ final boolean attemptRevert,
+ @Nonnull final Set<InstanceIdentifier<?>> writersOrder) throws BulkUpdateException {
+ if (updates.isEmpty()) {
+ return;
+ }
+
+ LOG.debug("Performing bulk update with revert attempt: {} for: {}", attemptRevert, updates.keySet());
+
+ // Check that all updates can be handled
+ checkAllTypesCanBeHandled(updates);
+
+ // Capture all changes successfully processed in case revert is needed
+ final Set<InstanceIdentifier<?>> processedNodes = new HashSet<>();
+
+ // Iterate over all writers and call update if there are any related updates
+ for (InstanceIdentifier<?> writerType : writersOrder) {
+ Collection<? extends DataObjectUpdate> writersData = updates.get(writerType);
+ final Writer<?> writer = getWriter(writerType);
+
+ if (writersData.isEmpty()) {
+ // If there are no data for current writer, but it is a SubtreeWriter and there are updates to
+ // its children, still invoke it with its root data
+ if (writer instanceof SubtreeWriter<?> && isAffected(((SubtreeWriter<?>) writer), updates)) {
+ // Provide parent data for SubtreeWriter for further processing
+ writersData = getParentDataObjectUpdate(ctx, updates, writer);
+ } else {
+ // Skipping unaffected writer
+ // Alternative to this would be modification sort according to the order of writers
+ continue;
+ }
+ }
+
+ LOG.debug("Performing update for: {}", writerType);
+ LOG.trace("Performing update with writer: {}", writer);
+
+ for (DataObjectUpdate singleUpdate : writersData) {
+ try {
+ writer.update(singleUpdate.getId(), singleUpdate.getDataBefore(), singleUpdate.getDataAfter(), ctx);
+ processedNodes.add(singleUpdate.getId());
+ LOG.trace("Update successful for type: {}", writerType);
+ LOG.debug("Update successful for: {}", singleUpdate);
+ } catch (Exception e) {
+ LOG.error("Error while processing data change of: {} (updates={})", writerType, writersData, e);
+
+ final Reverter reverter = attemptRevert
+ ? new ReverterImpl(processedNodes, updates, writersOrder, ctx)
+ : () -> {}; // NOOP reverter
+
+ // Find out which changes left unprocessed
+ final Set<InstanceIdentifier<?>> unprocessedChanges = updates.values().stream()
+ .map(DataObjectUpdate::getId)
+ .filter(id -> !processedNodes.contains(id))
+ .collect(Collectors.toSet());
+ throw new BulkUpdateException(unprocessedChanges, reverter, e);
+ }
+ }
+ }
+ }
+
+ private void checkAllTypesCanBeHandled(
+ @Nonnull final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) {
+ if (!handledTypes.containsAll(updates.keySet())) {
+ final Sets.SetView<InstanceIdentifier<?>> missingWriters = Sets.difference(updates.keySet(), handledTypes);
+ LOG.warn("Unable to process update. Missing writers for: {}", missingWriters);
+ throw new IllegalArgumentException("Unable to process update. Missing writers for: " + missingWriters);
+ }
+ }
+
+ /**
+ * Check whether {@link SubtreeWriter} is affected by the updates.
+ *
+ * @return true if there are any updates to SubtreeWriter's child nodes (those marked by SubtreeWriter
+ * as being taken care of)
+ * */
+ private static boolean isAffected(final SubtreeWriter<?> writer,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates) {
+ return !Sets.intersection(writer.getHandledChildTypes(), updates.keySet()).isEmpty();
+ }
+
+ @Nullable
+ private Writer<?> getWriter(@Nonnull final InstanceIdentifier<?> singleType) {
+ return writers.get(singleType);
+ }
+
+ // FIXME unit test
+ private final class ReverterImpl implements Reverter {
+
+ private final Collection<InstanceIdentifier<?>> processedNodes;
+ private final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates;
+ private final Set<InstanceIdentifier<?>> revertDeleteOrder;
+ private final WriteContext ctx;
+
+ ReverterImpl(final Collection<InstanceIdentifier<?>> processedNodes,
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Set<InstanceIdentifier<?>> writersOrderOriginal,
+ final WriteContext ctx) {
+ this.processedNodes = processedNodes;
+ this.updates = updates;
+ // Use opposite ordering when executing revert
+ this.revertDeleteOrder = writersOrderOriginal == FlatWriterRegistry.this.writersOrder
+ ? FlatWriterRegistry.this.writersOrderReversed
+ : FlatWriterRegistry.this.writersOrder;
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void revert() throws RevertFailedException {
+ Multimap<InstanceIdentifier<?>, DataObjectUpdate> updatesToRevert =
+ filterAndRevertProcessed(updates, processedNodes);
+
+ LOG.info("Attempting revert for changes: {}", updatesToRevert);
+ try {
+ // Perform reversed bulk update without revert attempt
+ bulkUpdate(updatesToRevert, ctx, true, revertDeleteOrder);
+ LOG.info("Revert successful");
+ } catch (BulkUpdateException e) {
+ LOG.error("Revert failed", e);
+ throw new RevertFailedException(e.getFailedIds(), e);
+ }
+ }
+
+ /**
+ * Create new updates map, but only keep already processed changes. Switching before and after data for each
+ * update.
+ */
+ private Multimap<InstanceIdentifier<?>, DataObjectUpdate> filterAndRevertProcessed(
+ final Multimap<InstanceIdentifier<?>, ? extends DataObjectUpdate> updates,
+ final Collection<InstanceIdentifier<?>> processedNodes) {
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> filtered = HashMultimap.create();
+ for (InstanceIdentifier<?> processedNode : processedNodes) {
+ final InstanceIdentifier<?> wildcardedIid = RWUtils.makeIidWildcarded(processedNode);
+ if (updates.containsKey(wildcardedIid)) {
+ updates.get(wildcardedIid).stream()
+ .filter(dataObjectUpdate -> processedNode.contains(dataObjectUpdate.getId()))
+ .forEach(dataObjectUpdate -> filtered.put(processedNode, dataObjectUpdate.reverse()));
+ }
+ }
+ return filtered;
+ }
+ }
+
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java
new file mode 100644
index 000000000..bfac2eedd
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilder.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import io.fd.honeycomb.v3po.translate.util.AbstractSubtreeManagerRegistryBuilderBuilder;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import io.fd.honeycomb.v3po.translate.write.registry.ModifiableWriterRegistryBuilder;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistryBuilder;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Builder for {@link FlatWriterRegistry} allowing users to specify inter-writer relationships.
+ */
+@NotThreadSafe
+public final class FlatWriterRegistryBuilder
+ extends AbstractSubtreeManagerRegistryBuilderBuilder<Writer<? extends DataObject>, WriterRegistry>
+ implements ModifiableWriterRegistryBuilder, WriterRegistryBuilder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlatWriterRegistryBuilder.class);
+
+ @Override
+ protected Writer<? extends DataObject> getSubtreeHandler(final @Nonnull Set<InstanceIdentifier<?>> handledChildren,
+ final @Nonnull Writer<? extends DataObject> writer) {
+ return SubtreeWriter.createForWriter(handledChildren, writer);
+ }
+
+ /**
+ * Create FlatWriterRegistry with writers ordered according to submitted relationships.
+ */
+ @Override
+ public WriterRegistry build() {
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters = getMappedHandlers();
+ LOG.debug("Building writer registry with writers: {}",
+ mappedWriters.keySet().stream()
+ .map(InstanceIdentifier::getTargetType)
+ .map(Class::getSimpleName)
+ .collect(Collectors.joining(", ")));
+ LOG.trace("Building writer registry with writers: {}", mappedWriters);
+ return new FlatWriterRegistry(mappedWriters);
+ }
+
+ @VisibleForTesting
+ @Override
+ protected ImmutableMap<InstanceIdentifier<?>, Writer<? extends DataObject>> getMappedHandlers() {
+ return super.getMappedHandlers();
+ }
+}
diff --git a/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java
new file mode 100644
index 000000000..e395b29da
--- /dev/null
+++ b/infra/translate-utils/src/main/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.Iterables;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.WriteFailedException;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Simple writer delegate for subtree writers (writers handling also children nodes) providing a list of all the
+ * children nodes being handled.
+ */
+final class SubtreeWriter<D extends DataObject> implements Writer<D> {
+
+ private final Writer<D> delegate;
+ private final Set<InstanceIdentifier<?>> handledChildTypes = new HashSet<>();
+
+ private SubtreeWriter(final Writer<D> delegate, Set<InstanceIdentifier<?>> handledTypes) {
+ this.delegate = delegate;
+ for (InstanceIdentifier<?> handledType : handledTypes) {
+ // Iid has to start with writer's handled root type
+ checkArgument(delegate.getManagedDataObjectType().getTargetType().equals(
+ handledType.getPathArguments().iterator().next().getType()),
+ "Handled node from subtree has to be identified by an instance identifier starting from: %s."
+ + "Instance identifier was: %s", getManagedDataObjectType().getTargetType(), handledType);
+ checkArgument(Iterables.size(handledType.getPathArguments()) > 1,
+ "Handled node from subtree identifier too short: %s", handledType);
+ handledChildTypes.add(InstanceIdentifier.create(Iterables.concat(
+ getManagedDataObjectType().getPathArguments(), Iterables.skip(handledType.getPathArguments(), 1))));
+ }
+ }
+
+ /**
+ * Return set of types also handled by this writer. All of the types are children of the type managed by this
+ * writer excluding the type of this writer.
+ */
+ Set<InstanceIdentifier<?>> getHandledChildTypes() {
+ return handledChildTypes;
+ }
+
+ @Override
+ public void update(
+ @Nonnull final InstanceIdentifier<? extends DataObject> id,
+ @Nullable final DataObject dataBefore,
+ @Nullable final DataObject dataAfter, @Nonnull final WriteContext ctx) throws WriteFailedException {
+ delegate.update(id, dataBefore, dataAfter, ctx);
+ }
+
+ @Override
+ @Nonnull
+ public InstanceIdentifier<D> getManagedDataObjectType() {
+ return delegate.getManagedDataObjectType();
+ }
+
+ /**
+ * Wrap a writer as a subtree writer.
+ */
+ static Writer<?> createForWriter(@Nonnull final Set<InstanceIdentifier<?>> handledChildren,
+ @Nonnull final Writer<? extends DataObject> writer) {
+ return new SubtreeWriter<>(writer, handledChildren);
+ }
+}
diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java
new file mode 100644
index 000000000..e1f04ebe2
--- /dev/null
+++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModule.java
@@ -0,0 +1,25 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+
+import io.fd.honeycomb.v3po.translate.util.read.registry.CompositeReaderRegistryBuilder;
+
+public class DelegatingReaderRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingReaderRegistryModule {
+ public DelegatingReaderRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public DelegatingReaderRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.DelegatingReaderRegistryModule 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() {
+ final CompositeReaderRegistryBuilder flatReaderRegistryBuilder = new CompositeReaderRegistryBuilder();
+ getReaderFactoryDependency().forEach(readerFactory -> readerFactory.init(flatReaderRegistryBuilder));
+ return flatReaderRegistryBuilder;
+ }
+}
diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java
new file mode 100644
index 000000000..24d6c50b8
--- /dev/null
+++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingReaderRegistryModuleFactory.java
@@ -0,0 +1,20 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: translate-utils yang module local name: delegating-reader-registry
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Apr 06 12:32:43 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+
+public class DelegatingReaderRegistryModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingReaderRegistryModuleFactory {
+
+ @Override
+ public DelegatingReaderRegistryModule handleChangedClass(final DynamicMBeanWithInstance old) throws Exception {
+ return super.handleChangedClass(old);
+ }
+}
diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java
new file mode 100644
index 000000000..7eadde80e
--- /dev/null
+++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModule.java
@@ -0,0 +1,26 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+
+import io.fd.honeycomb.v3po.translate.util.write.registry.FlatWriterRegistryBuilder;
+
+public class DelegatingWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingWriterRegistryModule {
+ public DelegatingWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public DelegatingWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.DelegatingWriterRegistryModule 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() {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ getWriterFactoryDependency().forEach(writerFactory -> writerFactory.init(flatWriterRegistryBuilder));
+ return flatWriterRegistryBuilder;
+ }
+
+}
diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModuleFactory.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModuleFactory.java
new file mode 100644
index 000000000..ab3b820d3
--- /dev/null
+++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/DelegatingWriterRegistryModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: translate-utils yang module local name: delegating-writer-registry
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Wed Apr 06 16:32:08 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+public class DelegatingWriterRegistryModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractDelegatingWriterRegistryModuleFactory {
+
+}
diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java
new file mode 100644
index 000000000..0f72df9da
--- /dev/null
+++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModule.java
@@ -0,0 +1,38 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+
+import io.fd.honeycomb.v3po.translate.util.write.NoopWriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistryBuilder;
+
+public class NoopWriterRegistryModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractNoopWriterRegistryModule {
+ public NoopWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NoopWriterRegistryModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.NoopWriterRegistryModule 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() {
+ return new NoopWriterRegistryBuilder();
+ }
+
+ private static final class NoopWriterRegistryBuilder implements AutoCloseable, WriterRegistryBuilder {
+
+ @Override
+ public WriterRegistry build() {
+ return new NoopWriterRegistry();
+ }
+
+ @Override
+ public void close() throws Exception {
+ // Noop
+ }
+ }
+}
diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModuleFactory.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModuleFactory.java
new file mode 100644
index 000000000..57b6859f3
--- /dev/null
+++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/NoopWriterRegistryModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: translate-utils yang module local name: noop-writer-registry
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Tue Apr 12 08:41:08 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+public class NoopWriterRegistryModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractNoopWriterRegistryModuleFactory {
+
+}
diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java
new file mode 100644
index 000000000..99cf4b4e1
--- /dev/null
+++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModule.java
@@ -0,0 +1,84 @@
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class RealtimeMappingContextModule extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractRealtimeMappingContextModule {
+ public RealtimeMappingContextModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public RealtimeMappingContextModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.RealtimeMappingContextModule 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() {
+ // Provides real time CRUD on top of Context data broker
+ return new MappingContext() {
+
+ @Override
+ public <T extends DataObject> Optional<T> read(@Nonnull final InstanceIdentifier<T> currentId) {
+ try(ReadOnlyTransaction tx = getContextBindingBrokerDependency().newReadOnlyTransaction()) {
+ try {
+ return tx.read(LogicalDatastoreType.OPERATIONAL, currentId).checkedGet();
+ } catch (ReadFailedException e) {
+ throw new IllegalStateException("Unable to perform read of " + currentId, e);
+ }
+ }
+ }
+
+ @Override
+ public void delete(final InstanceIdentifier<?> path) {
+ final WriteTransaction writeTx = getContextBindingBrokerDependency().newWriteOnlyTransaction();
+ writeTx.delete(LogicalDatastoreType.OPERATIONAL, path);
+ try {
+ writeTx.submit().checkedGet();
+ } catch (TransactionCommitFailedException e) {
+ throw new IllegalStateException("Unable to perform delete of " + path, e);
+ }
+ }
+
+ @Override
+ public <T extends DataObject> void merge(final InstanceIdentifier<T> path, final T data) {
+ final WriteTransaction writeTx = getContextBindingBrokerDependency().newWriteOnlyTransaction();
+ writeTx.merge(LogicalDatastoreType.OPERATIONAL, path, data);
+ try {
+ writeTx.submit().checkedGet();
+ } catch (TransactionCommitFailedException e) {
+ throw new IllegalStateException("Unable to perform merge of " + path, e);
+ }
+ }
+
+ @Override
+ public <T extends DataObject> void put(final InstanceIdentifier<T> path, final T data) {
+ final WriteTransaction writeTx = getContextBindingBrokerDependency().newWriteOnlyTransaction();
+ writeTx.put(LogicalDatastoreType.OPERATIONAL, path, data);
+ try {
+ writeTx.submit().checkedGet();
+ } catch (TransactionCommitFailedException e) {
+ throw new IllegalStateException("Unable to perform put of " + path, e);
+ }
+ }
+
+ @Override
+ public void close() {
+ // Noop
+ }
+ };
+ }
+
+}
diff --git a/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModuleFactory.java b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModuleFactory.java
new file mode 100644
index 000000000..76647eace
--- /dev/null
+++ b/infra/translate-utils/src/main/java/org/opendaylight/yang/gen/v1/urn/honeycomb/params/xml/ns/yang/translate/utils/rev160406/RealtimeMappingContextModuleFactory.java
@@ -0,0 +1,13 @@
+/*
+* Generated file
+*
+* Generated from: yang module name: translate-utils yang module local name: realtime-mapping-context
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri Jun 03 16:04:29 CEST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406;
+public class RealtimeMappingContextModuleFactory extends org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.translate.utils.rev160406.AbstractRealtimeMappingContextModuleFactory {
+
+}
diff --git a/infra/translate-utils/src/main/yang/translate-utils.yang b/infra/translate-utils/src/main/yang/translate-utils.yang
new file mode 100644
index 000000000..b543bba0d
--- /dev/null
+++ b/infra/translate-utils/src/main/yang/translate-utils.yang
@@ -0,0 +1,95 @@
+module translate-utils {
+ yang-version 1;
+ namespace "urn:honeycomb:params:xml:ns:yang:translate:utils";
+ prefix "tutils";
+
+ import config { prefix config; revision-date 2013-04-05; }
+ import translate-api { prefix tapi; revision-date 2016-04-06; }
+ import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;}
+
+ description
+ "This module contains translation layer utilities";
+
+ revision "2016-04-06" {
+ description
+ "Initial revision.";
+ }
+
+ identity delegating-reader-registry {
+ base config:module-type;
+ config:provided-service tapi:honeycomb-reader-registry;
+ config:provided-service tapi:honeycomb-reader-registry-builder;
+ config:java-name-prefix DelegatingReaderRegistry;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case delegating-reader-registry {
+ when "/config:modules/config:module/config:type = 'delegating-reader-registry'";
+
+ list reader-factory {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity tapi:honeycomb-reader-factory;
+ }
+ }
+ }
+
+ }
+ }
+
+ identity delegating-writer-registry {
+ base config:module-type;
+ config:provided-service tapi:honeycomb-writer-registry;
+ config:provided-service tapi:honeycomb-writer-registry-builder;
+ config:java-name-prefix DelegatingWriterRegistry;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case delegating-writer-registry {
+ when "/config:modules/config:module/config:type = 'delegating-writer-registry'";
+
+ list writer-factory {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity tapi:honeycomb-writer-factory;
+ }
+ }
+ }
+
+ }
+ }
+
+ identity noop-writer-registry-builder {
+ base config:module-type;
+ config:provided-service tapi:honeycomb-writer-registry-builder;
+ config:java-name-prefix NoopWriterRegistry;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case noop-writer-registry-builder {
+ when "/config:modules/config:module/config:type = 'noop-writer-registry-builder'";
+ }
+ }
+
+ identity realtime-mapping-context {
+ base config:module-type;
+ config:provided-service tapi:honeycomb-mapping-context;
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case realtime-mapping-context {
+ when "/config:modules/config:module/config:type = 'realtime-mapping-context'";
+
+ container context-binding-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity md-sal-binding:binding-async-data-broker;
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java
new file mode 100644
index 000000000..7c93e992a
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/impl/write/util/TransactionWriteContextTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.impl.write.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+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.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+import io.fd.honeycomb.v3po.translate.MappingContext;
+import io.fd.honeycomb.v3po.translate.ModificationCache;
+import io.fd.honeycomb.v3po.translate.util.DataObjects;
+import io.fd.honeycomb.v3po.translate.util.write.TransactionWriteContext;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+public class TransactionWriteContextTest {
+
+ @Mock
+ private BindingNormalizedNodeSerializer serializer;
+ @Mock
+ private DOMDataReadOnlyTransaction beforeTx;
+ @Mock
+ private DOMDataReadOnlyTransaction afterTx;
+ @Mock
+ private Map.Entry entry;
+ @Mock
+ private MappingContext contextBroker;
+
+ private TransactionWriteContext transactionWriteContext;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+ transactionWriteContext = new TransactionWriteContext(serializer, beforeTx, afterTx, contextBroker);
+ }
+
+ @Test
+ public void testReadBeforeNoData() throws Exception {
+ when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(
+ Futures.immediateCheckedFuture(Optional.absent()));
+
+ final InstanceIdentifier<DataObjects.DataObject1> instanceId =
+ InstanceIdentifier.create(DataObjects.DataObject1.class);
+
+ final Optional<DataObjects.DataObject1> dataObjects = transactionWriteContext.readBefore(instanceId);
+ assertNotNull(dataObjects);
+ assertFalse(dataObjects.isPresent());
+
+ verify(serializer).toYangInstanceIdentifier(instanceId);
+ verify(serializer, never()).fromNormalizedNode(any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+ }
+
+ @Test
+ public void testReadBefore() throws Exception {
+ when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(
+ Futures.immediateCheckedFuture(Optional.of(mock(NormalizedNode.class))));
+
+ final InstanceIdentifier<DataObjects.DataObject1> instanceId =
+ InstanceIdentifier.create(DataObjects.DataObject1.class);
+ final YangInstanceIdentifier yangId = YangInstanceIdentifier.builder().node(QName.create("n", "d")).build();
+ when(serializer.toYangInstanceIdentifier(any(InstanceIdentifier.class))).thenReturn(yangId);
+ when(serializer.fromNormalizedNode(eq(yangId), any(NormalizedNode.class))).thenReturn(entry);
+ when(entry.getValue()).thenReturn(mock(DataObjects.DataObject1.class));
+
+ final Optional<DataObjects.DataObject1> dataObjects = transactionWriteContext.readBefore(instanceId);
+ assertNotNull(dataObjects);
+ assertTrue(dataObjects.isPresent());
+
+ verify(serializer).toYangInstanceIdentifier(instanceId);
+ verify(serializer).fromNormalizedNode(eq(yangId), any(NormalizedNode.class));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testReadBeforeFailed() throws Exception {
+ when(beforeTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(
+ Futures.immediateFailedCheckedFuture(mock(ReadFailedException.class)));
+ transactionWriteContext.readBefore(mock(InstanceIdentifier.class));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testReadAfterFailed() throws Exception {
+ when(afterTx.read(eq(LogicalDatastoreType.CONFIGURATION), any(YangInstanceIdentifier.class))).thenReturn(
+ Futures.immediateFailedCheckedFuture(mock(ReadFailedException.class)));
+ transactionWriteContext.readAfter(mock(InstanceIdentifier.class));
+ }
+
+ @Test
+ public void testGetContext() throws Exception {
+ assertNotNull(transactionWriteContext.getModificationCache());
+ }
+
+ @Test
+ public void testClose() throws Exception {
+ final ModificationCache context = transactionWriteContext.getModificationCache();
+ transactionWriteContext.close();
+ // TODO verify context was closed
+ }
+} \ No newline at end of file
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/DataObjects.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/DataObjects.java
new file mode 100644
index 000000000..d823465bd
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/DataObjects.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util;
+
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class DataObjects {
+ public interface DataObject1 extends DataObject {
+ InstanceIdentifier<DataObject1> IID = InstanceIdentifier.create(DataObject1.class);
+ }
+
+ public interface DataObject2 extends DataObject {
+ InstanceIdentifier<DataObject2> IID = InstanceIdentifier.create(DataObject2.class);
+ }
+
+ public interface DataObject3 extends DataObject {
+ InstanceIdentifier<DataObject3> IID = InstanceIdentifier.create(DataObject3.class);
+ interface DataObject31 extends DataObject, ChildOf<DataObject3> {
+ InstanceIdentifier<DataObject31> IID = DataObject3.IID.child(DataObject31.class);
+ }
+ }
+
+ public interface DataObject4 extends DataObject {
+ InstanceIdentifier<DataObject4> IID = InstanceIdentifier.create(DataObject4.class);
+ interface DataObject41 extends DataObject, ChildOf<DataObject4> {
+ InstanceIdentifier<DataObject41> IID = DataObject4.IID.child(DataObject41.class);
+ interface DataObject411 extends DataObject, ChildOf<DataObject41> {
+ InstanceIdentifier<DataObject411> IID = DataObject41.IID.child(DataObject411.class);
+ }
+ }
+
+ interface DataObject42 extends DataObject, ChildOf<DataObject4> {
+ InstanceIdentifier<DataObject42> IID = DataObject4.IID.child(DataObject42.class);
+ }
+ }
+}
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/JsonUtilsTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/JsonUtilsTest.java
new file mode 100644
index 000000000..bd48cb446
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/JsonUtilsTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.io.ByteStreams;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext;
+
+public class JsonUtilsTest {
+
+ public static final String NAMESPACE = "urn:opendaylight:params:xml:ns:yang:test:persistence";
+
+ private static final QName ROOT_QNAME = QName.create("urn:ietf:params:xml:ns:netconf:base:1.0", "data");
+ private static final QName TOP_CONTAINER_NAME = QName.create(NAMESPACE, "2015-01-05", "top-container");
+ private static final QName TOP_CONTAINER2_NAME = QName.create(NAMESPACE, "2015-01-05", "top-container2");
+ private static final QName STRING_LEAF_QNAME = QName.create(TOP_CONTAINER_NAME, "string");
+
+ private Path tmpPersistFile;
+ private EffectiveSchemaContext effectiveSchemaContext;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ tmpPersistFile = Files.createTempFile("testing-hc-persistence", "json");
+
+ final CrossSourceStatementReactor.BuildAction buildAction = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ buildAction.addSource(new YangStatementSourceImpl(getClass().getResourceAsStream("/test-persistence.yang")));
+ effectiveSchemaContext = buildAction.buildEffective();
+ }
+
+ @Test
+ public void testPersist() throws Exception {
+
+ NormalizedNode<?, ?> data = getData("testing");
+ JsonUtils.writeJsonRoot(data, effectiveSchemaContext, Files.newOutputStream(tmpPersistFile, StandardOpenOption.CREATE));
+ assertTrue(Files.exists(tmpPersistFile));
+
+ String persisted = new String(Files.readAllBytes(tmpPersistFile));
+ String expected =
+ new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/expected-persisted-output.txt")));
+
+ assertEquals(expected, persisted);
+
+ data = getData("testing2");
+ JsonUtils.writeJsonRoot(data, effectiveSchemaContext, Files.newOutputStream(tmpPersistFile, StandardOpenOption.CREATE));
+ persisted = new String(Files.readAllBytes(tmpPersistFile));
+ assertEquals(expected.replace("testing", "testing2"), persisted);
+
+ // File has to stay even after close
+ assertTrue(Files.exists(tmpPersistFile));
+ }
+
+ @Test
+ public void testRestore() throws Exception {
+ final ContainerNode normalizedNodeOptional = JsonUtils
+ .readJsonRoot(effectiveSchemaContext, getClass().getResourceAsStream("/expected-persisted-output.txt"));
+ assertEquals(getData("testing"), normalizedNodeOptional);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRestoreInvalidFile() throws Exception {
+ JsonUtils.readJsonRoot(effectiveSchemaContext, getClass().getResourceAsStream("/test-persistence.yang"));
+ }
+
+ private ContainerNode getData(final String stringValue) {
+ return Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ROOT_QNAME))
+ .withChild(Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER_NAME))
+ .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue))
+ .build())
+ .withChild(Builders.containerBuilder()
+ .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_CONTAINER2_NAME))
+ .withChild(ImmutableNodes.leafNode(STRING_LEAF_QNAME, stringValue))
+ .build())
+ .build();
+ }
+} \ No newline at end of file
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java
new file mode 100644
index 000000000..e57dcee43
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/CompositeReaderRegistryBuilderTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.read.registry.ReaderRegistry;
+import io.fd.honeycomb.v3po.translate.util.DataObjects;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class CompositeReaderRegistryBuilderTest {
+
+ private Reader<DataObjects.DataObject1, Builder<DataObjects.DataObject1>> reader1 =
+ mock(DataObjects.DataObject1.class);
+ private Reader<DataObjects.DataObject2, Builder<DataObjects.DataObject2>> reader2 =
+ mock(DataObjects.DataObject2.class);
+ private Reader<DataObjects.DataObject3, Builder<DataObjects.DataObject3>> reader3 =
+ mock(DataObjects.DataObject3.class);
+ private Reader<DataObjects.DataObject3.DataObject31, Builder<DataObjects.DataObject3.DataObject31>> reader31 =
+ mock(DataObjects.DataObject3.DataObject31.class);
+
+ private Reader<DataObjects.DataObject4, Builder<DataObjects.DataObject4>> reader4 =
+ mock(DataObjects.DataObject4.class);
+ private Reader<DataObjects.DataObject4.DataObject41, Builder<DataObjects.DataObject4.DataObject41>> reader41 =
+ mock(DataObjects.DataObject4.DataObject41.class);
+ private Reader<DataObjects.DataObject4.DataObject41.DataObject411, Builder<DataObjects.DataObject4.DataObject41.DataObject411>> reader411 =
+ mock(DataObjects.DataObject4.DataObject41.DataObject411.class);
+ private Reader<DataObjects.DataObject4.DataObject42, Builder<DataObjects.DataObject4.DataObject42>> reader42 =
+ mock(DataObjects.DataObject4.DataObject42.class);
+
+ @SuppressWarnings("unchecked")
+ private <D extends DataObject> Reader<D, Builder<D>> mock(final Class<D> dataObjectType) {
+ final Reader<D, Builder<D>> mock = Mockito.mock(Reader.class);
+ try {
+ when(mock.getManagedDataObjectType())
+ .thenReturn(((InstanceIdentifier<D>) dataObjectType.getDeclaredField("IID").get(null)));
+ } catch (IllegalAccessException | NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ return mock;
+ }
+
+ @Test
+ public void testCompositeStructure() throws Exception {
+ final CompositeReaderRegistryBuilder compositeReaderRegistryBuilder = new CompositeReaderRegistryBuilder();
+ /*
+ Composite reader structure ordered left from right
+
+ 1, 2, 3, 4
+ 31 42, 41
+ 411
+ */
+ compositeReaderRegistryBuilder.add(reader1);
+ compositeReaderRegistryBuilder.addAfter(reader2, reader1.getManagedDataObjectType());
+ compositeReaderRegistryBuilder.addAfter(reader3, reader2.getManagedDataObjectType());
+ compositeReaderRegistryBuilder.addAfter(reader31, reader1.getManagedDataObjectType());
+ compositeReaderRegistryBuilder.addAfter(reader4, reader3.getManagedDataObjectType());
+ compositeReaderRegistryBuilder.add(reader41);
+ compositeReaderRegistryBuilder.addBefore(reader42, reader41.getManagedDataObjectType());
+ compositeReaderRegistryBuilder.add(reader411);
+
+ final ReaderRegistry build = compositeReaderRegistryBuilder.build();
+
+ final Map<Class<? extends DataObject>, Reader<? extends DataObject, ? extends Builder<?>>> rootReaders =
+ ((CompositeReaderRegistry) build).getRootReaders();
+ final List<Class<? extends DataObject>> rootReaderOrder = Lists.newArrayList(rootReaders.keySet());
+
+ assertEquals(reader1.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(0));
+ assertEquals(reader2.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(1));
+ assertEquals(reader3.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(2));
+ assertEquals(reader4.getManagedDataObjectType().getTargetType(), rootReaderOrder.get(3));
+
+ assertFalse(rootReaders.get(DataObjects.DataObject1.class) instanceof CompositeReader);
+ assertFalse(rootReaders.get(DataObjects.DataObject2.class) instanceof CompositeReader);
+ assertTrue(rootReaders.get(DataObjects.DataObject3.class) instanceof CompositeReader);
+ assertTrue(rootReaders.get(DataObjects.DataObject4.class) instanceof CompositeReader);
+
+ final ImmutableMap<Class<?>, Reader<? extends DataObject, ? extends Builder<?>>> childReaders =
+ ((CompositeReader<? extends DataObject, ? extends Builder<?>>) rootReaders
+ .get(DataObjects.DataObject4.class)).getChildReaders();
+ final List<Class<?>> orderedChildReaders = Lists.newArrayList(childReaders.keySet());
+
+ assertEquals(reader42.getManagedDataObjectType().getTargetType(), orderedChildReaders.get(0));
+ assertEquals(reader41.getManagedDataObjectType().getTargetType(), orderedChildReaders.get(1));
+ assertTrue(childReaders.get(DataObjects.DataObject4.DataObject41.class) instanceof CompositeReader);
+ assertFalse(childReaders.get(DataObjects.DataObject4.DataObject42.class) instanceof CompositeReader);
+ }
+} \ No newline at end of file
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReaderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReaderTest.java
new file mode 100644
index 000000000..324d71daa
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/SubtreeReaderTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.read.ReadContext;
+import io.fd.honeycomb.v3po.translate.read.Reader;
+import io.fd.honeycomb.v3po.translate.util.DataObjects;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class SubtreeReaderTest {
+
+ @Mock
+ private Reader<DataObjects.DataObject4, Builder<DataObjects.DataObject4>> delegate;
+ @Mock
+ private Reader<DataObject1, Builder<DataObject1>> delegateLocal;
+ @Mock
+ private ReadContext ctx;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn(DataObjects.DataObject4.IID).when(delegate).getManagedDataObjectType();
+ doReturn(DataObject1.IID).when(delegateLocal).getManagedDataObjectType();
+ }
+
+ @Test
+ public void testCreate() throws Exception {
+ final Reader<DataObjects.DataObject4, Builder<DataObjects.DataObject4>> subtreeR =
+ SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate);
+
+ subtreeR.getBuilder(DataObjects.DataObject4.IID);
+ verify(delegate).getBuilder(DataObjects.DataObject4.IID);
+
+ subtreeR.getManagedDataObjectType();
+ verify(delegate, atLeastOnce()).getManagedDataObjectType();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateInvalid() throws Exception {
+ SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject1.IID), delegate);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testReadOnlySubtreeCannotFilter() throws Exception {
+ final Reader<DataObjects.DataObject4, Builder<DataObjects.DataObject4>> subtreeR =
+ SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate);
+
+ doReturn(Optional.fromNullable(mock(DataObjects.DataObject4.class))).when(delegate).read(DataObjects.DataObject4.IID, ctx);
+ subtreeR.read(DataObjects.DataObject4.DataObject41.IID, ctx);
+ }
+
+ @Test
+ public void testReadOnlySubtreeNotPresent() throws Exception {
+ final Reader<DataObjects.DataObject4, Builder<DataObjects.DataObject4>> subtreeR =
+ SubtreeReader.createForReader(Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID), delegate);
+
+ doReturn(Optional.absent()).when(delegate).read(DataObjects.DataObject4.IID, ctx);
+ assertFalse(subtreeR.read(DataObjects.DataObject4.DataObject41.IID, ctx).isPresent());
+ }
+
+ @Test
+ public void testReadOnlySubtreeChild() throws Exception {
+ final Reader<DataObject1, Builder<DataObject1>> subtreeR =
+ SubtreeReader.createForReader(Sets.newHashSet(DataObject1.DataObject11.IID), delegateLocal);
+
+ final DataObject1 mock = mock(DataObject1.class);
+ final DataObject1.DataObject11 mock11 = mock(DataObject1.DataObject11.class);
+ doReturn(mock11).when(mock).getDataObject11();
+ doReturn(Optional.fromNullable(mock)).when(delegateLocal).read(DataObject1.IID, ctx);
+ assertEquals(mock11, subtreeR.read(DataObject1.DataObject11.IID, ctx).get());
+ }
+
+ @Test
+ public void testReadEntireSubtree() throws Exception {
+ final Reader<DataObject1, Builder<DataObject1>> subtreeR =
+ SubtreeReader.createForReader(Sets.newHashSet(DataObject1.DataObject11.IID), delegateLocal);
+
+ final DataObject1 mock = mock(DataObject1.class);
+ final DataObject1.DataObject11 mock11 = mock(DataObject1.DataObject11.class);
+ doReturn(mock11).when(mock).getDataObject11();
+ doReturn(Optional.fromNullable(mock)).when(delegateLocal).read(DataObject1.IID, ctx);
+ assertEquals(mock, subtreeR.read(DataObject1.IID, ctx).get());
+ }
+
+ public abstract static class DataObject1 implements DataObject {
+ public static InstanceIdentifier<DataObject1> IID = InstanceIdentifier.create(DataObject1.class);
+
+ public abstract DataObject11 getDataObject11();
+
+ public abstract static class DataObject11 implements DataObject, ChildOf<DataObject1> {
+ public static InstanceIdentifier<DataObject11> IID = DataObject1.IID.child(DataObject11.class);
+ }
+ }
+}
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchyTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchyTest.java
new file mode 100644
index 000000000..7a664eef1
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/read/registry/TypeHierarchyTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.read.registry;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject1;
+import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject3;
+import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject4;
+import org.junit.Test;
+
+public class TypeHierarchyTest {
+
+ @Test
+ public void testHierarchy() throws Exception {
+ final TypeHierarchy typeHierarchy = TypeHierarchy.create(Sets.newHashSet(
+ DataObject4.DataObject41.DataObject411.IID,
+ DataObject4.DataObject41.IID,/* Included in previous already */
+ DataObject1.IID,
+ DataObject3.DataObject31.IID));
+
+ // Roots
+ assertThat(typeHierarchy.getRoots().size(), is(3));
+ assertThat(typeHierarchy.getRoots(), hasItems(DataObject1.IID, DataObject3.IID, DataObject4.IID));
+
+ // Leaves
+ assertThat(typeHierarchy.getDirectChildren(DataObject1.IID).size(), is(0));
+ assertThat(typeHierarchy.getDirectChildren(DataObject3.DataObject31.IID).size(), is(0));
+ assertThat(typeHierarchy.getDirectChildren(DataObject4.DataObject41.DataObject411.IID).size(), is(0));
+
+ // Intermediate leaves
+ assertThat(typeHierarchy.getDirectChildren(DataObject3.IID).size(), is(1));
+ assertThat(typeHierarchy.getDirectChildren(DataObject3.IID), hasItem(DataObject3.DataObject31.IID));
+ assertEquals(typeHierarchy.getDirectChildren(DataObject3.IID), typeHierarchy.getAllChildren(DataObject3.IID));
+
+ assertThat(typeHierarchy.getDirectChildren(DataObject4.DataObject41.IID).size(), is(1));
+ assertThat(typeHierarchy.getDirectChildren(DataObject4.DataObject41.IID), hasItem(
+ DataObject4.DataObject41.DataObject411.IID));
+ assertEquals(typeHierarchy.getDirectChildren(DataObject4.DataObject41.IID), typeHierarchy.getAllChildren(
+ DataObject4.DataObject41.IID));
+
+ assertThat(typeHierarchy.getDirectChildren(DataObject4.IID).size(), is(1));
+ assertThat(typeHierarchy.getDirectChildren(DataObject4.IID), hasItem(DataObject4.DataObject41.IID));
+ assertThat(typeHierarchy.getAllChildren(DataObject4.IID).size(), is(2));
+ assertTrue(typeHierarchy.getAllChildren(DataObject4.IID).contains(DataObject4.DataObject41.IID));
+ assertTrue(typeHierarchy.getAllChildren(DataObject4.IID).contains(DataObject4.DataObject41.DataObject411.IID));
+ }
+}
+
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java
new file mode 100644
index 000000000..743d84cbf
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryBuilderTest.java
@@ -0,0 +1,131 @@
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.util.DataObjects;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class FlatWriterRegistryBuilderTest {
+
+
+ @Test
+ public void testRelationsBefore() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ /*
+ 1 -> 2 -> 3
+ -> 4
+ */
+ flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject3.class));
+ flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject4.class));
+ flatWriterRegistryBuilder.addBefore(mockWriter(DataObjects.DataObject2.class),
+ Lists.newArrayList(DataObjects.DataObject3.IID, DataObjects.DataObject4.IID));
+ flatWriterRegistryBuilder.addBefore(mockWriter(DataObjects.DataObject1.class), DataObjects.DataObject2.IID);
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters =
+ flatWriterRegistryBuilder.getMappedHandlers();
+
+ final ArrayList<InstanceIdentifier<?>> typesInList = Lists.newArrayList(mappedWriters.keySet());
+ assertEquals(DataObjects.DataObject1.IID, typesInList.get(0));
+ assertEquals(DataObjects.DataObject2.IID, typesInList.get(1));
+ assertThat(typesInList.get(2), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID)));
+ assertThat(typesInList.get(3), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID)));
+ }
+
+ @Test
+ public void testRelationsAfter() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ /*
+ 1 -> 2 -> 3
+ -> 4
+ */
+ flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class));
+ flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject2.class), DataObjects.DataObject1.IID);
+ flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject3.class), DataObjects.DataObject2.IID);
+ flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject4.class), DataObjects.DataObject2.IID);
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters =
+ flatWriterRegistryBuilder.getMappedHandlers();
+
+ final List<InstanceIdentifier<?>> typesInList = Lists.newArrayList(mappedWriters.keySet());
+ assertEquals(DataObjects.DataObject1.IID, typesInList.get(0));
+ assertEquals(DataObjects.DataObject2.IID, typesInList.get(1));
+ assertThat(typesInList.get(2), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID)));
+ assertThat(typesInList.get(3), anyOf(equalTo(DataObjects.DataObject3.IID), equalTo(DataObjects.DataObject4.IID)));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRelationsLoop() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ /*
+ 1 -> 2 -> 1
+ */
+ flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class));
+ flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject2.class), DataObjects.DataObject1.IID);
+ flatWriterRegistryBuilder.addAfter(mockWriter(DataObjects.DataObject1.class), DataObjects.DataObject2.IID);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testAddWriterTwice() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class));
+ flatWriterRegistryBuilder.add(mockWriter(DataObjects.DataObject1.class));
+ }
+
+ @Test
+ public void testAddSubtreeWriter() throws Exception {
+ final FlatWriterRegistryBuilder flatWriterRegistryBuilder = new FlatWriterRegistryBuilder();
+ flatWriterRegistryBuilder.subtreeAdd(
+ Sets.newHashSet(DataObjects.DataObject4.DataObject41.IID,
+ DataObjects.DataObject4.DataObject41.IID),
+ mockWriter(DataObjects.DataObject4.class));
+ final ImmutableMap<InstanceIdentifier<?>, Writer<?>> mappedWriters =
+ flatWriterRegistryBuilder.getMappedHandlers();
+ final ArrayList<InstanceIdentifier<?>> typesInList = Lists.newArrayList(mappedWriters.keySet());
+
+ assertEquals(DataObjects.DataObject4.IID, typesInList.get(0));
+ assertEquals(1, typesInList.size());
+ }
+
+ @Test
+ public void testCreateSubtreeWriter() throws Exception {
+ final Writer<?> forWriter = SubtreeWriter.createForWriter(Sets.newHashSet(
+ DataObjects.DataObject4.DataObject41.IID,
+ DataObjects.DataObject4.DataObject41.DataObject411.IID,
+ DataObjects.DataObject4.DataObject42.IID),
+ mockWriter(DataObjects.DataObject4.class));
+ assertThat(forWriter, instanceOf(SubtreeWriter.class));
+ assertThat(((SubtreeWriter<?>) forWriter).getHandledChildTypes().size(), is(3));
+ assertThat(((SubtreeWriter<?>) forWriter).getHandledChildTypes(), hasItems(DataObjects.DataObject4.DataObject41.IID,
+ DataObjects.DataObject4.DataObject42.IID, DataObjects.DataObject4.DataObject41.DataObject411.IID));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateInvalidSubtreeWriter() throws Exception {
+ SubtreeWriter.createForWriter(Sets.newHashSet(
+ InstanceIdentifier.create(DataObjects.DataObject3.class).child(DataObjects.DataObject3.DataObject31.class)),
+ mockWriter(DataObjects.DataObject4.class));
+ }
+
+ @SuppressWarnings("unchecked")
+ private Writer<? extends DataObject> mockWriter(final Class<? extends DataObject> doClass)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Writer mock = mock(Writer.class);
+ when(mock.getManagedDataObjectType()).thenReturn((InstanceIdentifier<?>) doClass.getDeclaredField("IID").get(null));
+ return mock;
+ }
+
+} \ No newline at end of file
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java
new file mode 100644
index 000000000..a72cb4fa7
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/FlatWriterRegistryTest.java
@@ -0,0 +1,264 @@
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject1;
+import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject2;
+import io.fd.honeycomb.v3po.translate.util.DataObjects.DataObject3;
+import io.fd.honeycomb.v3po.translate.write.DataObjectUpdate;
+import io.fd.honeycomb.v3po.translate.write.WriteContext;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import io.fd.honeycomb.v3po.translate.write.registry.WriterRegistry;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class FlatWriterRegistryTest {
+
+ @Mock
+ private Writer<DataObject1> writer1;
+ @Mock
+ private Writer<DataObject2> writer2;
+ @Mock
+ private Writer<DataObject3> writer3;
+ @Mock
+ private WriteContext ctx;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(writer1.getManagedDataObjectType()).thenReturn(DataObject1.IID);
+ when(writer2.getManagedDataObjectType()).thenReturn(DataObject2.IID);
+ when(writer3.getManagedDataObjectType()).thenReturn(DataObject3.IID);
+ }
+
+ @Test
+ public void testMultipleUpdatesForSingleWriter() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final InstanceIdentifier<DataObject1> iid2 = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 dataObject = mock(DataObject1.class);
+ updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
+ updates.put(DataObject1.IID, DataObjectUpdate.create(iid2, dataObject, dataObject));
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+
+ verify(writer1).update(iid, dataObject, dataObject, ctx);
+ verify(writer1).update(iid2, dataObject, dataObject, ctx);
+ // Invoked when registry is being created
+ verifyNoMoreInteractions(writer1);
+ verifyZeroInteractions(writer2);
+ }
+
+ @Test
+ public void testMultipleUpdatesForMultipleWriters() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 dataObject = mock(DataObject1.class);
+ updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
+ final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
+ final DataObject2 dataObject2 = mock(DataObject2.class);
+ updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+
+ final InOrder inOrder = inOrder(writer1, writer2);
+ inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx);
+ inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx);
+
+ verifyNoMoreInteractions(writer1);
+ verifyNoMoreInteractions(writer2);
+ }
+
+ @Test
+ public void testMultipleDeletesForMultipleWriters() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 dataObject = mock(DataObject1.class);
+ deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
+ final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
+ final DataObject2 dataObject2 = mock(DataObject2.class);
+ deletes.put(DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(ImmutableMultimap.of(), deletes), ctx);
+
+ final InOrder inOrder = inOrder(writer1, writer2);
+ // Reversed order of invocation, first writer2 and then writer1
+ inOrder.verify(writer2).update(iid2, dataObject2, null, ctx);
+ inOrder.verify(writer1).update(iid, dataObject, null, ctx);
+
+ verifyNoMoreInteractions(writer1);
+ verifyNoMoreInteractions(writer2);
+ }
+
+ @Test
+ public void testMultipleUpdatesAndDeletesForMultipleWriters() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate.DataObjectDelete> deletes = HashMultimap.create();
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ final InstanceIdentifier<DataObject1> iid = InstanceIdentifier.create(DataObject1.class);
+ final DataObject1 dataObject = mock(DataObject1.class);
+ // Writer 1 delete
+ deletes.put(DataObject1.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid, dataObject, null)));
+ // Writer 1 update
+ updates.put(DataObject1.IID, DataObjectUpdate.create(iid, dataObject, dataObject));
+ final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
+ final DataObject2 dataObject2 = mock(DataObject2.class);
+ // Writer 2 delete
+ deletes.put(DataObject2.IID, ((DataObjectUpdate.DataObjectDelete) DataObjectUpdate.create(iid2, dataObject2, null)));
+ // Writer 2 update
+ updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, dataObject2, dataObject2));
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, deletes), ctx);
+
+ final InOrder inOrder = inOrder(writer1, writer2);
+ // Reversed order of invocation, first writer2 and then writer1 for deletes
+ inOrder.verify(writer2).update(iid2, dataObject2, null, ctx);
+ inOrder.verify(writer1).update(iid, dataObject, null, ctx);
+ // Then also updates are processed
+ inOrder.verify(writer1).update(iid, dataObject, dataObject, ctx);
+ inOrder.verify(writer2).update(iid2, dataObject2, dataObject2, ctx);
+
+ verifyNoMoreInteractions(writer1);
+ verifyNoMoreInteractions(writer2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMultipleUpdatesOneMissing() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ addUpdate(updates, DataObject1.class);
+ addUpdate(updates, DataObject2.class);
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+ }
+
+ @Test
+ public void testMultipleUpdatesOneFailing() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2));
+
+ // Writer1 always fails
+ doThrow(new RuntimeException()).when(writer1)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ addUpdate(updates, DataObject1.class);
+ addUpdate(updates, DataObject2.class);
+
+ try {
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+ fail("Bulk update should have failed on writer1");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ assertThat(e.getFailedIds().size(), is(2));
+ assertThat(e.getFailedIds(), hasItem(InstanceIdentifier.create(DataObject2.class)));
+ assertThat(e.getFailedIds(), hasItem(InstanceIdentifier.create(DataObject1.class)));
+ }
+ }
+
+ @Test
+ public void testMultipleUpdatesOneFailingThenRevertWithSuccess() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(
+ ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObject3.IID, writer3));
+
+ // Writer1 always fails
+ doThrow(new RuntimeException()).when(writer3)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ addUpdate(updates, DataObject1.class);
+ addUpdate(updates, DataObject3.class);
+ final InstanceIdentifier<DataObject2> iid2 = InstanceIdentifier.create(DataObject2.class);
+ final DataObject2 before2 = mock(DataObject2.class);
+ final DataObject2 after2 = mock(DataObject2.class);
+ updates.put(DataObject2.IID, DataObjectUpdate.create(iid2, before2, after2));
+
+ try {
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+ fail("Bulk update should have failed on writer1");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ assertThat(e.getFailedIds().size(), is(1));
+
+ final InOrder inOrder = inOrder(writer1, writer2, writer3);
+ inOrder.verify(writer1)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+ inOrder.verify(writer2)
+ .update(iid2, before2, after2, ctx);
+ inOrder.verify(writer3)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+
+ e.revertChanges();
+ // Revert changes. Successful updates are iterated in reverse
+ inOrder.verify(writer2)
+ .update(iid2, after2, before2, ctx);
+ inOrder.verify(writer1)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+ verifyNoMoreInteractions(writer3);
+ }
+ }
+
+ @Test
+ public void testMultipleUpdatesOneFailingThenRevertWithFail() throws Exception {
+ final FlatWriterRegistry flatWriterRegistry =
+ new FlatWriterRegistry(
+ ImmutableMap.of(DataObject1.IID, writer1, DataObject2.IID, writer2, DataObject3.IID, writer3));
+
+ // Writer1 always fails
+ doThrow(new RuntimeException()).when(writer3)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+
+ final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates = HashMultimap.create();
+ addUpdate(updates, DataObject1.class);
+ addUpdate(updates, DataObject2.class);
+ addUpdate(updates, DataObject3.class);
+
+ try {
+ flatWriterRegistry.update(new WriterRegistry.DataObjectUpdates(updates, ImmutableMultimap.of()), ctx);
+ fail("Bulk update should have failed on writer1");
+ } catch (WriterRegistry.BulkUpdateException e) {
+ // Writer1 always fails from now
+ doThrow(new RuntimeException()).when(writer1)
+ .update(any(InstanceIdentifier.class), any(DataObject.class), any(DataObject.class), any(WriteContext.class));
+ try {
+ e.revertChanges();
+ } catch (WriterRegistry.Reverter.RevertFailedException e1) {
+ assertThat(e1.getNotRevertedChanges().size(), is(1));
+ assertThat(e1.getNotRevertedChanges(), hasItem(InstanceIdentifier.create(DataObject1.class)));
+ }
+ }
+ }
+
+ private <D extends DataObject> void addUpdate(final Multimap<InstanceIdentifier<?>, DataObjectUpdate> updates,
+ final Class<D> type) throws Exception {
+ final InstanceIdentifier<D> iid = (InstanceIdentifier<D>) type.getDeclaredField("IID").get(null);
+ updates.put(iid, DataObjectUpdate.create(iid, mock(type), mock(type)));
+ }
+} \ No newline at end of file
diff --git a/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java
new file mode 100644
index 000000000..627c69c92
--- /dev/null
+++ b/infra/translate-utils/src/test/java/io/fd/honeycomb/v3po/translate/util/write/registry/SubtreeWriterTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.honeycomb.v3po.translate.util.write.registry;
+
+import static org.hamcrest.CoreMatchers.hasItem;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.Sets;
+import io.fd.honeycomb.v3po.translate.util.DataObjects;
+import io.fd.honeycomb.v3po.translate.write.Writer;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class SubtreeWriterTest {
+
+ @Mock
+ Writer<DataObjects.DataObject4> writer;
+ @Mock
+ Writer<DataObjects.DataObject4.DataObject41> writer11;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(writer.getManagedDataObjectType()).thenReturn(DataObjects.DataObject4.IID);
+ when(writer11.getManagedDataObjectType()).thenReturn(DataObjects.DataObject4.DataObject41.IID);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubtreeWriterCreationFail() throws Exception {
+ // The subtree node identified by IID.c(DataObject.class) is not a child of writer.getManagedDataObjectType
+ SubtreeWriter.createForWriter(Collections.singleton(InstanceIdentifier.create(DataObject.class)), writer);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubtreeWriterCreationFailInvalidIid() throws Exception {
+ // The subtree node identified by IID.c(DataObject.class) is not a child of writer.getManagedDataObjectType
+ SubtreeWriter.createForWriter(Collections.singleton(DataObjects.DataObject4.IID), writer);
+ }
+
+ @Test
+ public void testSubtreeWriterCreation() throws Exception {
+ final SubtreeWriter<?> forWriter = (SubtreeWriter<?>) SubtreeWriter.createForWriter(Sets.newHashSet(
+ DataObjects.DataObject4.DataObject41.IID,
+ DataObjects.DataObject4.DataObject41.DataObject411.IID,
+ DataObjects.DataObject4.DataObject42.IID),
+ writer);
+
+ assertEquals(writer.getManagedDataObjectType(), forWriter.getManagedDataObjectType());
+ assertEquals(3, forWriter.getHandledChildTypes().size());
+ }
+
+ @Test
+ public void testSubtreeWriterHandledTypes() throws Exception {
+ final SubtreeWriter<?> forWriter = (SubtreeWriter<?>) SubtreeWriter.createForWriter(Sets.newHashSet(
+ DataObjects.DataObject4.DataObject41.DataObject411.IID),
+ writer);
+
+ assertEquals(writer.getManagedDataObjectType(), forWriter.getManagedDataObjectType());
+ assertEquals(1, forWriter.getHandledChildTypes().size());
+ assertThat(forWriter.getHandledChildTypes(), hasItem(DataObjects.DataObject4.DataObject41.DataObject411.IID));
+ }
+
+} \ No newline at end of file
diff --git a/infra/translate-utils/src/test/resources/expected-persisted-output.txt b/infra/translate-utils/src/test/resources/expected-persisted-output.txt
new file mode 100644
index 000000000..f0f5902e2
--- /dev/null
+++ b/infra/translate-utils/src/test/resources/expected-persisted-output.txt
@@ -0,0 +1,8 @@
+{
+ "test-persistence:top-container": {
+ "string": "testing"
+ },
+ "test-persistence:top-container2": {
+ "string": "testing"
+ }
+} \ No newline at end of file
diff --git a/infra/translate-utils/src/test/resources/test-persistence.yang b/infra/translate-utils/src/test/resources/test-persistence.yang
new file mode 100644
index 000000000..6dca9f2d5
--- /dev/null
+++ b/infra/translate-utils/src/test/resources/test-persistence.yang
@@ -0,0 +1,22 @@
+module test-persistence {
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:test:persistence";
+ prefix "tp";
+
+ revision "2015-01-05" {
+ description "Initial revision";
+ }
+
+ container top-container {
+ leaf string {
+ type string;
+ }
+ }
+
+ container top-container2 {
+ leaf string {
+ type string;
+ }
+ }
+
+}