From a92d9d6a21a0f6ca8d2153795721f500eaf29ee9 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 8 Oct 2013 18:26:35 +0200 Subject: [PATCH] Initial code drop of netconf protocol implementation This implementation is based on infrastructure provided by the framework artifact and contains server as well as client side code. Netconf subsystem structure: Netconf-api: Api definition for client and server. Netconf-impl: Netconf server implementation. Server handles basic communication and delegates handling of rpcs to an implementation of netconf-mapping-api. Netconf-mapping-api: Api definition for pluggable rpcs handler. Implementations of this api are plugged dynamically into netconf-impl using OSGi apis. Config-netconf-connector: Implementation of netconf-mapping-api that handles netconf rpcs and delegates requests to configuration subsystem Netconf-util: Utility classes used by client and server code. Netconf-client: Netconf client implementation. Netconf-it: Integration tests for netconf. These tests verify correct cooperation of netconf server, config-netconf-connector, config subsystem and netconf client. Config-persister-api: Api definition for config persister. Config persister is a component that is responsible for storing configuration snapshots after every change to the configuration via netconf rpcs. (Pushed to config-subsystem) Config-persister-impl: Implementation of config persister that receives notifications from netconf-impl about changes to the configuration and uses pluggable adapters to store received snapshots. Config-persister-file-adapter: Implementation of config persister adapter that stores and restores config snapshots from a file. (Pushed to config-subsystem) In order to run netconf in OSGi, some configuration issues had to be resolved in config-subsystem bundles. Change-Id: I8e0421c924b0714a4d49962c4bb5ca01ef68ac78 Signed-off-by: Maros Marsalek --- opendaylight/commons/opendaylight/pom.xml | 40 +- opendaylight/config/config-api/pom.xml | 8 +- .../config/stat/ConfigProvider.java | 55 ++ .../config/config-persister-api/pom.xml | 48 ++ .../config/persist/api/Persister.java | 32 + .../persist/api/storage/StorageAdapter.java | 22 + .../config-persister-file-adapter/pom.xml | 97 +++ .../storage/file/FileStorageAdapter.java | 273 ++++++++ .../storage/file/FileStorageAdapterTest.java | 209 ++++++ opendaylight/config/pom.xml | 38 +- .../config/yang-jmx-generator-plugin/pom.xml | 19 +- .../yangjmxgenerator/plugin/JMXGenerator.java | 27 +- .../config/yang-jmx-generator/pom.xml | 7 +- .../yangjmxgenerator/RuntimeBeanEntry.java | 52 +- opendaylight/config/yang-store-impl/pom.xml | 2 +- .../databaseinteractions/chunked1.txt | 35 + .../databaseinteractions/chunked2.txt | 48 ++ .../databaseinteractions/chunked3.txt | 43 ++ .../databaseinteractions/chunked4.txt | 40 ++ .../databaseinteractions/chunked5.txt | 42 ++ .../jolokia_config_bean_response.txt | 18 + .../jolokia_lookupConfigBeans.txt | 18 + .../notused/client_commit.xml | 4 + .../notused/client_lock_candidate.xml | 8 + .../notused/client_lock_running.xml | 8 + .../notused/client_modify_candidate.xml | 20 + .../notused/client_unlock_candidate.xml | 8 + .../notused/client_unlock_running.xml | 8 + .../server_error_missing_attribute.xml | 11 + .../distribution/opendaylight/pom.xml | 133 +++- .../main/resources/configuration/config.ini | 11 + .../main/resources/configuration/logback.xml | 3 + .../netconf/config-netconf-connector/pom.xml | 157 +++++ .../AttributeIfcSwitchStatement.java | 37 + .../AbstractAttributeReadingStrategy.java | 38 + .../ArrayAttributeReadingStrategy.java | 39 ++ .../fromxml/AttributeConfigElement.java | 76 ++ .../fromxml/AttributeReadingStrategy.java | 19 + .../CompositeAttributeReadingStrategy.java | 58 ++ .../ObjectNameAttributeReadingStrategy.java | 45 ++ .../attributes/fromxml/ObjectXmlReader.java | 78 +++ .../SimpleAttributeReadingStrategy.java | 86 +++ .../AbstractAttributeMappingStrategy.java | 27 + .../ArrayAttributeMappingStrategy.java | 60 ++ .../mapping/AttributeMappingStrategy.java | 21 + .../CompositeAttributeMappingStrategy.java | 69 ++ .../attributes/mapping/ObjectMapper.java | 97 +++ .../ObjectNameAttributeMappingStrategy.java | 74 ++ .../SimpleAttributeMappingStrategy.java | 72 ++ .../AbstractAttributeResolvingStrategy.java | 24 + .../ArrayAttributeResolvingStrategy.java | 105 +++ .../resolving/AttributeResolvingStrategy.java | 22 + .../CompositeAttributeResolvingStrategy.java | 85 +++ .../ObjectNameAttributeResolvingStrategy.java | 49 ++ .../attributes/resolving/ObjectResolver.java | 104 +++ .../SimpleAttributeResolvingStrategy.java | 125 ++++ .../toxml/ArrayAttributeWritingStrategy.java | 34 + .../toxml/AttributeWritingStrategy.java | 17 + .../CompositeAttributeWritingStrategy.java | 51 ++ .../ObjectNameAttributeWritingStrategy.java | 50 ++ .../attributes/toxml/ObjectXmlWriter.java | 89 +++ .../RuntimeBeanEntryWritingStrategy.java | 64 ++ .../toxml/SimpleAttributeWritingStrategy.java | 38 + .../mapping/config/Config.java | 248 +++++++ .../mapping/config/InstanceConfig.java | 196 ++++++ .../config/InstanceConfigElementResolved.java | 61 ++ .../mapping/config/ModuleConfig.java | 84 +++ .../mapping/config/ModuleElementResolved.java | 29 + .../mapping/config/Services.java | 311 +++++++++ .../mapping/rpc/InstanceRuntimeRpc.java | 90 +++ .../mapping/rpc/ModuleRpcs.java | 58 ++ .../mapping/rpc/Rpcs.java | 33 + .../mapping/runtime/InstanceRuntime.java | 117 ++++ .../mapping/runtime/ModuleRuntime.java | 71 ++ .../mapping/runtime/Runtime.java | 78 +++ .../AbstractConfigNetconfOperation.java | 46 ++ .../operations/Commit.java | 74 ++ .../operations/Datastore.java | 36 + .../operations/DiscardChanges.java | 78 +++ .../operations/Validate.java | 105 +++ .../AbstractEditConfigStrategy.java | 45 ++ .../editconfig/DeleteEditConfigStrategy.java | 40 ++ .../operations/editconfig/EditConfig.java | 206 ++++++ .../editconfig/EditConfigStrategy.java | 21 + .../editconfig/EditConfigXmlParser.java | 140 ++++ .../editconfig/EditStrategyType.java | 82 +++ .../editconfig/MergeEditConfigStrategy.java | 60 ++ .../editconfig/NoneEditConfigStrategy.java | 28 + .../editconfig/RemoveEditConfigStrategy.java | 27 + .../editconfig/ReplaceEditConfigStrategy.java | 63 ++ .../operations/get/Get.java | 143 ++++ .../CandidateDatastoreQueryStrategy.java | 34 + .../getconfig/DatastoreQueryStrategy.java | 25 + .../operations/getconfig/GetConfig.java | 144 ++++ .../RunningDatastoreQueryStrategy.java | 24 + .../operations/runtimerpc/RuntimeRpc.java | 248 +++++++ .../runtimerpc/RuntimeRpcElementResolved.java | 107 +++ .../osgi/Activator.java | 55 ++ .../osgi/NetconfOperationProvider.java | 67 ++ .../NetconfOperationServiceFactoryImpl.java | 66 ++ .../osgi/NetconfOperationServiceImpl.java | 158 +++++ .../osgi/YangStoreServiceTracker.java | 41 ++ .../transactions/TransactionProvider.java | 168 +++++ .../confignetconfconnector/util/Util.java | 55 ++ .../NetconfMappingTest.java | 659 ++++++++++++++++++ .../ServiceTrackerTest.java | 71 ++ .../operations/ValidateTest.java | 94 +++ .../operations/editconfig/EditConfigTest.java | 136 ++++ .../ReplaceEditConfigStrategyTest.java | 64 ++ .../netconf/config-persister-impl/pom.xml | 98 +++ .../ConfigPersisterNotificationHandler.java | 317 +++++++++ .../persist/impl/NoOpStorageAdapter.java | 42 ++ .../netconf/persist/impl/PersisterImpl.java | 96 +++ .../controller/netconf/persist/impl/Util.java | 31 + .../impl/osgi/ConfigPersisterActivator.java | 83 +++ .../main/resources/netconfOp/client_hello.xml | 5 + .../src/main/resources/netconfOp/commit.xml | 3 + .../main/resources/netconfOp/editConfig.xml | 12 + .../persist/impl/PersisterImplTest.java | 132 ++++ opendaylight/netconf/netconf-api/pom.xml | 54 ++ .../api/NetconfDeserializerException.java | 28 + .../api/NetconfDocumentedException.java | 111 +++ .../netconf/api/NetconfMessage.java | 31 + .../netconf/api/NetconfOperationRouter.java | 20 + .../api/NetconfServerSessionPreferences.java | 26 + .../netconf/api/NetconfSession.java | 16 + .../netconf/api/NetconfSessionListener.java | 16 + .../api/NetconfSessionPreferences.java | 24 + .../netconf/api/NetconfTerminationReason.java | 25 + .../api/jmx/CommitJMXNotification.java | 52 ++ .../api/jmx/DefaultCommitOperationMXBean.java | 20 + .../api/jmx/NetconfJMXNotification.java | 64 ++ opendaylight/netconf/netconf-client/pom.xml | 77 ++ .../netconf/client/NetconfClient.java | 138 ++++ .../client/NetconfClientDispatcher.java | 84 +++ .../netconf/client/NetconfClientSession.java | 95 +++ .../client/NetconfClientSessionListener.java | 75 ++ .../NetconfClientSessionNegotiator.java | 72 ++ ...NetconfClientSessionNegotiatorFactory.java | 53 ++ .../src/main/resources/client_hello.xml | 5 + opendaylight/netconf/netconf-impl/pom.xml | 148 ++++ .../netconf/impl/CapabilityProviderImpl.java | 102 +++ .../DefaultCommitNotificationProducer.java | 57 ++ .../netconf/impl/NetconfServerDispatcher.java | 72 ++ .../netconf/impl/NetconfServerSession.java | 85 +++ .../impl/NetconfServerSessionListener.java | 127 ++++ .../NetconfServerSessionListenerFactory.java | 44 ++ .../impl/NetconfServerSessionNegotiator.java | 33 + ...NetconfServerSessionNegotiatorFactory.java | 93 +++ .../netconf/impl/SessionIdProvider.java | 24 + .../impl/mapping/CapabilityProvider.java | 21 + .../operations/DefaultCloseSession.java | 48 ++ .../mapping/operations/DefaultCommit.java | 144 ++++ .../mapping/operations/DefaultGetSchema.java | 119 ++++ .../impl/osgi/NetconfImplActivator.java | 82 +++ .../impl/osgi/NetconfOperationRouterImpl.java | 190 +++++ ...etconfOperationServiceFactoryListener.java | 20 + ...nfOperationServiceFactoryListenerImpl.java | 33 + ...NetconfOperationServiceFactoryTracker.java | 38 + .../osgi/NetconfOperationServiceSnapshot.java | 71 ++ .../util/DeserializerExceptionHandler.java | 51 ++ .../netconf/impl/util/NetconfUtil.java | 37 + .../main/resources/getConfig_candidate.xml | 7 + .../src/main/resources/server_hello.xml | 7 + .../netconf/impl/ConcurrentClientsTest.java | 272 ++++++++ .../netconf/impl/MessageHeaderTest.java | 40 ++ .../netconf/impl/MessageParserTest.java | 91 +++ .../impl/NetconfDispatcherImplTest.java | 50 ++ .../controller/netconf/impl/chunked1.txt | 35 + .../controller/netconf/impl/chunked2.txt | 48 ++ .../controller/netconf/impl/chunked3.txt | 43 ++ .../controller/netconf/impl/chunked4.txt | 40 ++ .../controller/netconf/impl/chunked5.txt | 42 ++ .../jolokia_config_bean_response.txt | 18 + .../jolokia_lookupConfigBeans.txt | 18 + .../netconf/impl/notused/client_commit.xml | 4 + .../impl/notused/client_lock_candidate.xml | 8 + .../impl/notused/client_lock_running.xml | 8 + .../impl/notused/client_modify_candidate.xml | 20 + .../impl/notused/client_unlock_candidate.xml | 8 + .../impl/notused/client_unlock_running.xml | 8 + .../server_error_missing_attribute.xml | 11 + .../src/test/resources/testConfigs/input.json | 10 + .../src/test/resources/testConfigs/input.xml | 7 + .../test/resources/testConfigs/inputList.xml | 9 + .../test/resources/testConfigs/inputMap.xml | 14 + .../resources/testConfigs/inputMultiple.xml | 29 + .../resources/testConfigs/inputSetter.xml | 6 + .../resources/testConfigs/inputSetterList.xml | 8 + .../resources/testConfigs/inputSetterMap.xml | 9 + .../src/test/resources/testConfigs/map.json | 134 ++++ opendaylight/netconf/netconf-it/pom.xml | 165 +++++ .../controller/netconf/it/NetconfITTest.java | 399 +++++++++++ .../src/test/resources/keystore.jks | Bin 0 -> 2251 bytes .../netconf/netconf-mapping-api/pom.xml | 53 ++ .../netconf/mapping/api/Capability.java | 26 + .../netconf/mapping/api/HandlingPriority.java | 79 +++ .../netconf/mapping/api/NetconfOperation.java | 20 + .../mapping/api/NetconfOperationFilter.java | 26 + .../api/NetconfOperationFilterChain.java | 19 + .../mapping/api/NetconfOperationService.java | 36 + .../api/NetconfOperationServiceFactory.java | 20 + opendaylight/netconf/netconf-util/pom.xml | 117 ++++ .../util/AbstractChannelInitializer.java | 76 ++ .../AbstractNetconfSessionNegotiator.java | 157 +++++ .../mapping/AbstractNetconfOperation.java | 99 +++ .../util/messages/FramingMechanism.java | 26 + .../util/messages/NetconfMessageFactory.java | 141 ++++ .../util/messages/NetconfMessageHeader.java | 85 +++ .../util/messages/NetconfMessageUtil.java | 42 ++ .../util/messages/SendErrorExceptionUtil.java | 129 ++++ .../netconf/util/osgi/NetconfConfigUtil.java | 124 ++++ .../util/xml/HardcodedNamespaceResolver.java | 59 ++ .../netconf/util/xml/XMLNetconfUtil.java | 30 + .../netconf/util/xml/XmlElement.java | 367 ++++++++++ .../netconf/util/xml/XmlNetconfConstants.java | 41 ++ .../netconf/util/xml/XmlNetconfValidator.java | 41 ++ .../controller/netconf/util/xml/XmlUtil.java | 175 +++++ .../netconf/util/messages/server_error.xml | 16 + .../src/main/resources/rfc4741.xsd | 585 ++++++++++++++++ .../netconf-util/src/main/resources/xml.xsd | 327 +++++++++ .../netconf/util/test/XmlFileLoader.java | 72 ++ .../netconfMessages/client_hello.xml | 5 + .../client_hello_with_auth.xml | 8 + .../netconfMessages/close-session.xml | 3 + .../netconfMessages/closeSession.xml | 3 + .../test/resources/netconfMessages/commit.xml | 3 + ...stClientSendsRpcReply_expectedResponse.xml | 11 + .../testClientSendsRpcReply_request.xml | 4 + ...stRpcWithoutMessageId_expectedResponse.xml | 12 + .../testRpcWithoutMessageId_request.xml | 4 + .../client_get_request_ConfigRegistry.xml | 8 + .../confg_subsystem_expected_reply.xml | 29 + .../netconfMessages/discardChanges.xml | 3 + .../resources/netconfMessages/editConfig.xml | 117 ++++ .../editConfig_expectedResult.xml | 19 + .../editConfig_merge_threadfactory.xml | 27 + .../editConfig_merge_yang-test.xml | 23 + .../netconfMessages/editConfig_none.xml | 109 +++ .../netconfMessages/editConfig_remove.xml | 43 ++ .../editConfig_replace_default.xml | 27 + .../editConfig_replace_default_ex.xml | 21 + .../editConfig_replace_module.xml | 16 + .../editConfig_replace_module_ex.xml | 16 + .../resources/netconfMessages/edit_config.xml | 10 + .../test/resources/netconfMessages/get.xml | 3 + .../resources/netconfMessages/getConfig.xml | 7 + .../netconfMessages/getConfig_candidate.xml | 7 + .../netconfMessages/get_schema-no-version.xml | 6 + .../resources/netconfMessages/get_schema.xml | 10 + .../client_hello_with_session_id.xml | 6 + .../client_hello_with_wrong_namespace.xml | 5 + .../mount/editConfig_merge_threadfactory.xml | 68 ++ .../netconfMessages/mount/mount12002.xml | 7 + .../netconfMessages/mount/mount12003.xml | 7 + .../netconfMessages/mount/unmount12002.xml | 7 + .../editConfig_differentNamespaceTO.xml | 117 ++++ ...editConfig_sameAttrDifferentNamespaces.xml | 118 ++++ ...Config_sameAttrDifferentNamespacesList.xml | 117 ++++ ...Config_typeNameConfigAttributeMatching.xml | 116 +++ .../test/resources/netconfMessages/rpc.xml | 8 + .../resources/netconfMessages/rpcInner.xml | 7 + .../netconfMessages/rpcInnerInner.xml | 16 + .../threadpool-edit-config.xml | 23 + .../unrecognised/editConfig_unrecognised1.xml | 36 + .../unrecognised/editConfig_unrecognised2.xml | 37 + .../unrecognised/editConfig_unrecognised3.xml | 38 + .../unrecognised/editConfig_unrecognised4.xml | 39 ++ .../unrecognised/editConfig_unrecognised5.xml | 39 ++ .../unrecognised/editConfig_unrecognised6.xml | 36 + .../unrecognised/editConfig_unrecognised7.xml | 117 ++++ .../unrecognised/editConfig_unrecognised8.xml | 117 ++++ .../resources/netconfMessages/validate.xml | 7 + opendaylight/netconf/pom.xml | 148 ++++ pom.xml | 1 + 275 files changed, 17912 insertions(+), 62 deletions(-) create mode 100644 opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/stat/ConfigProvider.java create mode 100644 opendaylight/config/config-persister-api/pom.xml create mode 100644 opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java create mode 100644 opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java create mode 100644 opendaylight/config/config-persister-file-adapter/pom.xml create mode 100644 opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java create mode 100644 opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml create mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml create mode 100755 opendaylight/netconf/config-netconf-connector/pom.xml create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ArrayAttributeReadingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeReadingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/CompositeAttributeReadingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectNameAttributeReadingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AbstractAttributeMappingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ArrayAttributeMappingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AttributeMappingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/CompositeAttributeMappingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectNameAttributeMappingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/SimpleAttributeMappingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AbstractAttributeResolvingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ArrayAttributeResolvingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AttributeResolvingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectNameAttributeResolvingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/SimpleAttributeResolvingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ArrayAttributeWritingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/AttributeWritingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/CompositeAttributeWritingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectNameAttributeWritingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/RuntimeBeanEntryWritingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleAttributeWritingStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Config.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfigElementResolved.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleConfig.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleElementResolved.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/InstanceRuntimeRpc.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/ModuleRpcs.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/Rpcs.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/InstanceRuntime.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/ModuleRuntime.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/Runtime.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/AbstractConfigNetconfOperation.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Commit.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Validate.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/AbstractEditConfigStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/DeleteEditConfigStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigXmlParser.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditStrategyType.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/MergeEditConfigStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/NoneEditConfigStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/RemoveEditConfigStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/CandidateDatastoreQueryStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/DatastoreQueryStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpcElementResolved.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceTracker.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/util/Util.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/ServiceTrackerTest.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/ValidateTest.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigTest.java create mode 100644 opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java create mode 100644 opendaylight/netconf/config-persister-impl/pom.xml create mode 100644 opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java create mode 100644 opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java create mode 100644 opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java create mode 100644 opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java create mode 100644 opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java create mode 100644 opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml create mode 100644 opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/commit.xml create mode 100644 opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/editConfig.xml create mode 100644 opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java create mode 100644 opendaylight/netconf/netconf-api/pom.xml create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDeserializerException.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfOperationRouter.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfServerSessionPreferences.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionListener.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionPreferences.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfTerminationReason.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/CommitJMXNotification.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/DefaultCommitOperationMXBean.java create mode 100644 opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/NetconfJMXNotification.java create mode 100644 opendaylight/netconf/netconf-client/pom.xml create mode 100644 opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java create mode 100644 opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java create mode 100644 opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java create mode 100644 opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionListener.java create mode 100644 opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java create mode 100644 opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java create mode 100644 opendaylight/netconf/netconf-client/src/main/resources/client_hello.xml create mode 100644 opendaylight/netconf/netconf-impl/pom.xml create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SessionIdProvider.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/NetconfUtil.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/resources/getConfig_candidate.xml create mode 100644 opendaylight/netconf/netconf-impl/src/main/resources/server_hello.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java create mode 100644 opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageHeaderTest.java create mode 100644 opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageParserTest.java create mode 100644 opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked1.txt create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked2.txt create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked3.txt create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked4.txt create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked5.txt create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_config_bean_response.txt create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_lookupConfigBeans.txt create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_commit.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_candidate.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_running.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_modify_candidate.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_candidate.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_running.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/server_error_missing_attribute.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.json create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputList.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMap.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMultiple.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetter.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterList.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterMap.xml create mode 100644 opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/map.json create mode 100644 opendaylight/netconf/netconf-it/pom.xml create mode 100644 opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java create mode 100644 opendaylight/netconf/netconf-it/src/test/resources/keystore.jks create mode 100644 opendaylight/netconf/netconf-mapping-api/pom.xml create mode 100644 opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java create mode 100644 opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java create mode 100644 opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperation.java create mode 100644 opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilter.java create mode 100644 opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilterChain.java create mode 100644 opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java create mode 100644 opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java create mode 100644 opendaylight/netconf/netconf-util/pom.xml create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractChannelInitializer.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/FramingMechanism.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageHeader.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageUtil.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolver.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XMLNetconfUtil.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfValidator.java create mode 100644 opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlUtil.java create mode 100644 opendaylight/netconf/netconf-util/src/main/resources/org/opendaylight/controller/netconf/util/messages/server_error.xml create mode 100644 opendaylight/netconf/netconf-util/src/main/resources/rfc4741.xsd create mode 100644 opendaylight/netconf/netconf-util/src/main/resources/xml.xsd create mode 100644 opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/test/XmlFileLoader.java create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/close-session.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/closeSession.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/commit.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_expectedResponse.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_request.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_expectedResponse.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_request.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/client_get_request_ConfigRegistry.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/confg_subsystem_expected_reply.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/discardChanges.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_expectedResult.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_threadfactory.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_yang-test.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_remove.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default_ex.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module_ex.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/edit_config.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema-no-version.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_session_id.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_wrong_namespace.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/editConfig_merge_threadfactory.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12002.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12003.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/unmount12002.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpc.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInner.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/threadpool-edit-config.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised1.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml create mode 100644 opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/validate.xml create mode 100644 opendaylight/netconf/pom.xml diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 223abcd114..296250d945 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -54,7 +54,7 @@ 2.2.0.RELEASE 2.10 -Xmx1024m -XX:MaxPermSize=256m - 0.5.8 + 0.5.9-SNAPSHOT 14.0.1 5.0.0 2010.09.24.1 @@ -70,7 +70,8 @@ 2.3.7 4.8.1 0.2.0-SNAPSHOT - 0.5.8 + 0.5.9-SNAPSHOT + 0.6.0-SNAPSHOT 0.4.1-SNAPSHOT 0.2.1-SNAPSHOT @@ -634,11 +635,44 @@ ${bgpcep.version} + + org.opendaylight.bgpcep + framework + ${bgpcep.version} + + + + + io.netty + netty-handler + 4.0.9.Final + + + io.netty + netty-codec + 4.0.9.Final + + + io.netty + netty-buffer + 4.0.9.Final + + + io.netty + netty-transport + 4.0.9.Final + + + io.netty + netty-common + 4.0.9.Final + + org.opendaylight.yangtools yang-binding - ${yangtools.version} + ${yangtools.binding.version} org.opendaylight.yangtools diff --git a/opendaylight/config/config-api/pom.xml b/opendaylight/config/config-api/pom.xml index 858a8020d0..4dbc31f062 100644 --- a/opendaylight/config/config-api/pom.xml +++ b/opendaylight/config/config-api/pom.xml @@ -23,6 +23,10 @@ concepts 0.2.0-SNAPSHOT + + org.osgi + org.osgi.core + @@ -34,7 +38,8 @@ javax.management, - org.opendaylight.protocol.concepts + org.opendaylight.protocol.concepts, + org.osgi.framework, org.opendaylight.controller.config.api, @@ -43,6 +48,7 @@ org.opendaylight.controller.config.api.jmx, org.opendaylight.controller.config.api.jmx.constants, org.opendaylight.controller.config.api.runtime, + org.opendaylight.controller.config.stat, diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/stat/ConfigProvider.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/stat/ConfigProvider.java new file mode 100644 index 0000000000..3a81061881 --- /dev/null +++ b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/stat/ConfigProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.stat; + +import org.osgi.framework.BundleContext; + +/** + * Subset of {@link org.osgi.framework.BundleContext} + */ +public interface ConfigProvider { + /** + * Returns the value of the specified property. If the key is not found in + * the Framework properties, the system properties are then searched. The + * method returns {@code null} if the property is not found. + * + *

+ * All bundles must have permission to read properties whose names start + * with "org.osgi.". + * + * @param key + * The name of the requested property. + * @return The value of the requested property, or {@code null} if the + * property is undefined. + * @throws SecurityException + * If the caller does not have the appropriate + * {@code PropertyPermission} to read the property, and the Java + * Runtime Environment supports permissions. + */ + String getProperty(String key); + + public static class ConfigProviderImpl implements ConfigProvider { + private final BundleContext context; + + public ConfigProviderImpl(BundleContext context) { + this.context = context; + } + + @Override + public String getProperty(String key) { + return context.getProperty(key); + } + + @Override + public String toString() { + return "ConfigProviderImpl{" + "context=" + context + '}'; + } + } + +} diff --git a/opendaylight/config/config-persister-api/pom.xml b/opendaylight/config/config-persister-api/pom.xml new file mode 100644 index 0000000000..867c12c18c --- /dev/null +++ b/opendaylight/config/config-persister-api/pom.xml @@ -0,0 +1,48 @@ + + 4.0.0 + + config-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + .. + + config-persister-api + ${project.artifactId} + bundle + + + + ${project.groupId} + config-util + 0.2.1-SNAPSHOT + + + com.google.guava + guava + + + + + + + org.apache.felix + maven-bundle-plugin + + + + com.google.common.base, + org.w3c.dom, + org.osgi.framework, + org.opendaylight.controller.config.stat + + + org.opendaylight.controller.config.persist.api, + org.opendaylight.controller.config.persist.api.storage, + + + + + + + diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java new file mode 100644 index 0000000000..f9c9301b88 --- /dev/null +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.persist.api; + +import com.google.common.base.Optional; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Set; + +/** + * Base interface for persister implementation. + */ +public interface Persister extends Closeable { + + void persistConfig(ConfigSnapshotHolder configSnapshotHolder) throws IOException; + + Optional loadLastConfig() throws IOException; + + public static interface ConfigSnapshotHolder { + + String getConfigSnapshot(); + + Set getCapabilities(); + } +} diff --git a/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java new file mode 100644 index 0000000000..447504027e --- /dev/null +++ b/opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/storage/StorageAdapter.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.persist.api.storage; + +import org.opendaylight.controller.config.persist.api.Persister; +import org.opendaylight.controller.config.stat.ConfigProvider; + +/** + * Plugins for {@link org.opendaylight.controller.config.persist.api.Persister} + * must implement this interface. + */ +public interface StorageAdapter extends Persister { + + void setProperties(ConfigProvider configProvider); + +} diff --git a/opendaylight/config/config-persister-file-adapter/pom.xml b/opendaylight/config/config-persister-file-adapter/pom.xml new file mode 100644 index 0000000000..51fcf8641a --- /dev/null +++ b/opendaylight/config/config-persister-file-adapter/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + config-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + .. + + config-persister-file-adapter + ${project.artifactId} + bundle + + + + + ${project.groupId} + config-persister-api + ${project.version} + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + + + org.opendaylight.bgpcep + mockito-configuration + 0.2.0-SNAPSHOT + test + + + + + + + + org.codehaus.groovy.maven + gmaven-plugin + + + generate-sources + + execute + + + + System.setProperty("osgiversion", "${project.version}".replace('-', '.')) + + + + + + + org.apache.felix + maven-bundle-plugin + + + ${project.groupId}.config-persister-impl;bundle-version=${osgiversion} + + org.opendaylight.controller.config.persister.storage.adapter + + + org.osgi.framework, + com.google.common.base, + com.google.common.collect, + com.google.common.io, + javax.xml.parsers, + javax.xml.transform, + javax.xml.transform.dom, + javax.xml.transform.stream, + org.apache.commons.lang3, + org.opendaylight.controller.config.persist.api, + org.opendaylight.controller.config.stat, + org.opendaylight.controller.config.persist.api.storage, + org.slf4j, + org.w3c.dom, + org.xml.sax, + + + + + + + + diff --git a/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java new file mode 100644 index 0000000000..a866743b0d --- /dev/null +++ b/opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.persist.storage.file; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import com.google.common.io.Files; +import org.apache.commons.lang3.StringUtils; +import org.opendaylight.controller.config.persist.api.storage.StorageAdapter; +import org.opendaylight.controller.config.stat.ConfigProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Set; + +/** + * StorageAdapter that stores configuration in a plan file. + */ +public class FileStorageAdapter implements StorageAdapter { + private static final Logger logger = LoggerFactory.getLogger(FileStorageAdapter.class); + + // TODO prefix properties + + private static final Charset ENCODING = Charsets.UTF_8; + + public static final String FILE_STORAGE_PROP = "fileStorage"; + public static final String NUMBER_OF_BACKUPS = "numberOfBackups"; + + private static final String SEPARATOR_E_PURE = "//END OF CONFIG"; + private static final String SEPARATOR_E = newLine(SEPARATOR_E_PURE); + + private static final String SEPARATOR_M_PURE = "//END OF SNAPSHOT"; + private static final String SEPARATOR_M = newLine(SEPARATOR_M_PURE); + + private static final String SEPARATOR_S = newLine("//START OF CONFIG"); + + private static final String SEPARATOR_SL_PURE = "//START OF CONFIG-LAST"; + private static final String SEPARATOR_SL = newLine(SEPARATOR_SL_PURE); + + private static Integer numberOfStoredBackups; + private File storage; + + @Override + public void setProperties(ConfigProvider configProvider) { + File storage = extractStorageFileFromProperties(configProvider); + logger.debug("Using file {}", storage.getAbsolutePath()); + // Create file if it does not exist + File parentFile = storage.getAbsoluteFile().getParentFile(); + if (parentFile.exists() == false) { + logger.debug("Creating parent folders {}", parentFile); + parentFile.mkdirs(); + } + if (storage.exists() == false) { + logger.debug("Storage file does not exist, creating empty file"); + try { + boolean result = storage.createNewFile(); + if (result == false) + throw new RuntimeException("Unable to create storage file " + storage); + } catch (IOException e) { + throw new RuntimeException("Unable to create storage file " + storage, e); + } + } + if (numberOfStoredBackups == 0) { + throw new RuntimeException(NUMBER_OF_BACKUPS + + " property should be either set to positive value, or ommited. Can not be set to 0."); + } + setFileStorage(storage); + + } + + @VisibleForTesting + void setFileStorage(File storage) { + this.storage = storage; + } + + @VisibleForTesting + void setNumberOfBackups(Integer numberOfBackups) { + numberOfStoredBackups = numberOfBackups; + } + + private static File extractStorageFileFromProperties(ConfigProvider configProvider) { + String fileStorageProperty = configProvider.getProperty(FILE_STORAGE_PROP); + Preconditions.checkNotNull(fileStorageProperty, "Unable to find " + FILE_STORAGE_PROP + + " in received properties :" + configProvider); + File result = new File(fileStorageProperty); + String numberOfBAckupsAsString = configProvider.getProperty(NUMBER_OF_BACKUPS); + if (numberOfBAckupsAsString != null) { + numberOfStoredBackups = Integer.valueOf(numberOfBAckupsAsString); + } else { + numberOfStoredBackups = Integer.MAX_VALUE; + } + + return result; + } + + private static String newLine(String string) { + return string + "\n"; + } + + @Override + public void persistConfig(ConfigSnapshotHolder holder) throws IOException { + Preconditions.checkNotNull(storage, "Storage file is null"); + + String content = Files.toString(storage, ENCODING); + if (numberOfStoredBackups == Integer.MAX_VALUE) { + resetLastConfig(content); + persistLastConfig(holder); + } else { + if (numberOfStoredBackups == 1) { + Files.write("", storage, ENCODING); + persistLastConfig(holder); + } else { + int count = StringUtils.countMatches(content, SEPARATOR_S); + if ((count + 1) < numberOfStoredBackups) { + resetLastConfig(content); + persistLastConfig(holder); + } else { + String contentSubString = StringUtils.substringBefore(content, SEPARATOR_E); + contentSubString = contentSubString.concat(SEPARATOR_E_PURE); + content = StringUtils.substringAfter(content, contentSubString); + resetLastConfig(content); + persistLastConfig(holder); + } + } + } + } + + private void resetLastConfig(String content) throws IOException { + content = content.replaceFirst(SEPARATOR_SL, SEPARATOR_S); + Files.write(content, storage, ENCODING); + } + + private void persistLastConfig(ConfigSnapshotHolder holder) throws IOException { + Files.append(SEPARATOR_SL, storage, ENCODING); + String snapshotAsString = holder.getConfigSnapshot(); + Files.append(newLine(snapshotAsString), storage, ENCODING); + Files.append(SEPARATOR_M, storage, ENCODING); + Files.append(toStringCaps(holder.getCapabilities()), storage, ENCODING); + Files.append(SEPARATOR_E, storage, ENCODING); + } + + private CharSequence toStringCaps(Set capabilities) { + StringBuilder b = new StringBuilder(); + for (String capability : capabilities) { + b.append(newLine(capability)); + } + return b.toString(); + } + + @Override + public Optional loadLastConfig() throws IOException { + Preconditions.checkNotNull(storage, "Storage file is null"); + + if (!storage.exists()) { + return Optional.absent(); + } + + final LineProcessor lineProcessor = new LineProcessor(); + String result = Files.readLines(storage, ENCODING, lineProcessor); + + try { + if (lineProcessor.getConfigSnapshot().isPresent() == false) { + return Optional.absent(); + } else { + return Optional. of(new PersistedConfigImpl(lineProcessor.getConfigSnapshot(), + lineProcessor.getCapabilities())); + } + + } catch (ParserConfigurationException | SAXException e) { + throw new IOException("Unable to load last config ", e); + } + } + + private static final class LineProcessor implements com.google.common.io.LineProcessor { + + private boolean inLastConfig, inLastSnapshot; + private final StringBuffer snapshotBuffer = new StringBuffer(); + private final Set caps = Sets.newHashSet(); + + @Override + public String getResult() { + return null; + } + + @Override + public boolean processLine(String line) throws IOException { + if (inLastConfig && line.equals(SEPARATOR_E_PURE)) { + inLastConfig = false; + return false; + } + + if (inLastConfig && line.equals(SEPARATOR_M_PURE)) { + inLastSnapshot = false; + return true; + } + + if (inLastConfig) { + if (inLastSnapshot) { + snapshotBuffer.append(line); + snapshotBuffer.append(System.lineSeparator()); + } else { + caps.add(line); + } + } + + if (line.equals(SEPARATOR_SL_PURE)) { + inLastConfig = true; + inLastSnapshot = true; + } + + return true; + } + + Optional getConfigSnapshot() throws IOException, SAXException, ParserConfigurationException { + final String xmlContent = snapshotBuffer.toString(); + if (xmlContent == null || xmlContent.equals("")) { + return Optional.absent(); + } else + return Optional.of(xmlContent); + } + + Set getCapabilities() throws IOException, SAXException, ParserConfigurationException { + return caps; + } + + } + + @Override + public void close() throws IOException { + + } + + @Override + public String toString() { + return "FileStorageAdapter [storage=" + storage + "]"; + } + + private class PersistedConfigImpl implements ConfigSnapshotHolder { + + private final String snapshot; + private final Set caps; + + public PersistedConfigImpl(Optional configSnapshot, Set capabilities) { + this.snapshot = configSnapshot.get(); + this.caps = capabilities; + } + + @Override + public String getConfigSnapshot() { + return snapshot; + } + + @Override + public Set getCapabilities() { + return caps; + } + } + +} diff --git a/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java b/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java new file mode 100644 index 0000000000..acb44ba441 --- /dev/null +++ b/opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.config.persist.storage.file; + +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Sets; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.config.persist.api.Persister; + +import java.io.File; +import java.nio.file.Files; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; + +public class FileStorageAdapterTest { + + private static int i; + private File file; + + @Before + public void setUp() throws Exception { + file = Files.createTempFile("testFilePersist", ".txt").toFile(); + if (!file.exists()) + return; + com.google.common.io.Files.write("", file, Charsets.UTF_8); + i = 1; + } + + @Test + public void testFileAdapter() throws Exception { + FileStorageAdapter storage = new FileStorageAdapter(); + storage.setFileStorage(file); + storage.setNumberOfBackups(Integer.MAX_VALUE); + final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() { + @Override + public String getConfigSnapshot() { + return createConfig(); + } + + @Override + public Set getCapabilities() { + return createCaps(); + } + }; + storage.persistConfig(holder); + + storage.persistConfig(holder); + + Collection readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8), + new Predicate() { + + @Override + public boolean apply(String input) { + if (input.equals("")) + return false; + return true; + } + }); + assertEquals(14, readLines.size()); + + Optional lastConf = storage.loadLastConfig(); + assertTrue(lastConf.isPresent()); + assertEquals("2", + lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); + assertEquals(createCaps(), lastConf.get().getCapabilities()); + } + + private Set createCaps() { + Set caps = Sets.newHashSet(); + + caps.add("cap1"); + caps.add("cap2"); + caps.add("capaaaa as dasfasdf s2"); + return caps; + } + + @Test + public void testFileAdapterOneBackup() throws Exception { + FileStorageAdapter storage = new FileStorageAdapter(); + storage.setFileStorage(file); + storage.setNumberOfBackups(1); + final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() { + @Override + public String getConfigSnapshot() { + return createConfig(); + } + + @Override + public Set getCapabilities() { + return createCaps(); + } + }; + storage.persistConfig(holder); + + storage.persistConfig(holder); + + Collection readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8), + new Predicate() { + + @Override + public boolean apply(String input) { + if (input.equals("")) + return false; + return true; + } + }); + assertEquals(7, readLines.size()); + + Optional lastConf = storage.loadLastConfig(); + assertTrue(lastConf.isPresent()); + assertEquals("2", + lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); + } + + @Test + public void testFileAdapterOnlyTwoBackups() throws Exception { + FileStorageAdapter storage = new FileStorageAdapter(); + storage.setFileStorage(file); + storage.setNumberOfBackups(2); + final Persister.ConfigSnapshotHolder holder = new Persister.ConfigSnapshotHolder() { + @Override + public String getConfigSnapshot() { + return createConfig(); + } + + @Override + public Set getCapabilities() { + return createCaps(); + } + }; + storage.persistConfig(holder); + + storage.persistConfig(holder); + storage.persistConfig(holder); + + Collection readLines = Collections2.filter(com.google.common.io.Files.readLines(file, Charsets.UTF_8), + new Predicate() { + + @Override + public boolean apply(String input) { + if (input.equals("")) + return false; + return true; + } + }); + + assertEquals(14, readLines.size()); + + Optional lastConf = storage.loadLastConfig(); + assertTrue(lastConf.isPresent()); + assertEquals("3", + lastConf.get().getConfigSnapshot().replaceAll("\\s", "")); + assertFalse(readLines.contains(holder.getConfigSnapshot())); + } + + @Test + public void testNoLastConfig() throws Exception { + File file = Files.createTempFile("testFilePersist", ".txt").toFile(); + if (!file.exists()) + return; + FileStorageAdapter storage = new FileStorageAdapter(); + storage.setFileStorage(file); + + Optional elementOptional = storage.loadLastConfig(); + assertThat(elementOptional.isPresent(), is(false)); + } + + @Test(expected = NullPointerException.class) + public void testNoProperties() throws Exception { + FileStorageAdapter storage = new FileStorageAdapter(); + storage.loadLastConfig(); + } + + @Test(expected = NullPointerException.class) + public void testNoProperties2() throws Exception { + FileStorageAdapter storage = new FileStorageAdapter(); + storage.persistConfig(new Persister.ConfigSnapshotHolder() { + @Override + public String getConfigSnapshot() { + return Mockito.mock(String.class); + } + + @Override + public Set getCapabilities() { + return Collections. emptySet(); + } + } ); + } + + static String createConfig() { + return "" + i++ + ""; + } + +} diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index 6484c30274..1812fbb449 100755 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -22,6 +22,8 @@ config-api config-manager config-util + config-persister-api + config-persister-file-adapter yang-jmx-generator yang-jmx-generator-plugin yang-jmx-generator-it @@ -40,7 +42,6 @@ 0.6.2.201302030002 1.7.2 1.1.1 - 5.0.0 0.5.9-SNAPSHOT 0.6.0-SNAPSHOT ${project.build.directory}/generated-sources/config @@ -270,6 +271,39 @@ gmaven-plugin 1.0 + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.opendaylight.yangtools + + + yang-maven-plugin + + + [0.5.7-SNAPSHOT,) + + + + generate-sources + + + + + + + + + + + @@ -277,7 +311,7 @@ nexus.opendaylight.org - http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot + http://nexus.opendaylight.org/content/repositories/opendaylight.release true diff --git a/opendaylight/config/yang-jmx-generator-plugin/pom.xml b/opendaylight/config/yang-jmx-generator-plugin/pom.xml index 1079ce9fd7..81d55efeaf 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/pom.xml +++ b/opendaylight/config/yang-jmx-generator-plugin/pom.xml @@ -9,51 +9,61 @@ yang-jmx-generator-plugin + org.slf4j slf4j-api + org.opendaylight.controller yang-jmx-generator 0.2.1-SNAPSHOT + org.opendaylight.yangtools yang-maven-plugin-spi ${opendaylight.yang.version} + org.opendaylight.yangtools - binding-generator-impl + binding-type-provider ${opendaylight.binding.version} + org.eclipse.jdt core 3.3.0-v_771 test + org.freemarker freemarker 2.3.20 + ${project.groupId} config-api 0.2.1-SNAPSHOT + commons-io commons-io + com.googlecode.slf4j-maven-plugin-log slf4j-maven-plugin-log 1.0.0 + com.google.guava guava @@ -66,21 +76,24 @@ test test-jar + org.eclipse jdt 3.3.0-v20070607-1300 test + org.opendaylight.bgpcep mockito-configuration 0.2.0-SNAPSHOT test + - org.apache.commons - commons-lang3 + org.apache.commons + commons-lang3 diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java index 743ffba739..e40ca8b18b 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/JMXGenerator.java @@ -7,17 +7,11 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.plugin; -import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.apache.commons.io.FileUtils; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; @@ -35,11 +29,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.impl.StaticLoggerBinder; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * This class interfaces with yang-maven-plugin. Gets parsed yang modules in diff --git a/opendaylight/config/yang-jmx-generator/pom.xml b/opendaylight/config/yang-jmx-generator/pom.xml index dce382ddb7..67a1c80b9c 100644 --- a/opendaylight/config/yang-jmx-generator/pom.xml +++ b/opendaylight/config/yang-jmx-generator/pom.xml @@ -18,16 +18,19 @@ org.slf4j slf4j-api + org.opendaylight.yangtools - binding-generator-util + binding-generator-spi ${opendaylight.binding.version} org.opendaylight.yangtools - binding-generator-spi + binding-generator-util ${opendaylight.binding.version} + + com.google.guava guava diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java index 6d1eca1193..d07e20bebb 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java @@ -7,20 +7,9 @@ */ package org.opendaylight.controller.config.yangjmxgenerator; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; @@ -28,22 +17,12 @@ import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.RpcDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; -import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; -import org.opendaylight.yangtools.yang.model.api.UsesNode; +import org.opendaylight.yangtools.yang.model.api.*; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import com.google.common.collect.Lists; +import java.util.*; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; /** * Holds information about runtime bean to be generated. There are two kinds of @@ -295,8 +274,8 @@ public class RuntimeBeanEntry { + "Error occured in " + rpcDefinition); } List parameters = new ArrayList<>(); - for (DataSchemaNode childNode : rpcDefinition.getInput() - .getChildNodes()) { + for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput() + .getChildNodes())) { if (childNode.isAddedByUses() == false) { // skip // refined // context-instance @@ -318,6 +297,17 @@ public class RuntimeBeanEntry { attributes, rpcs); } + private static Collection sortAttributes(Set childNodes) { + final TreeSet dataSchemaNodes = new TreeSet<>(new Comparator() { + @Override + public int compare(DataSchemaNode o1, DataSchemaNode o2) { + return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName()); + } + }); + dataSchemaNodes.addAll(childNodes); + return dataSchemaNodes; + } + private static boolean isInnerStateBean(DataSchemaNode child) { for (UnknownSchemaNode unknownSchemaNode : child .getUnknownSchemaNodes()) { diff --git a/opendaylight/config/yang-store-impl/pom.xml b/opendaylight/config/yang-store-impl/pom.xml index 69531c61b6..97285c1f22 100644 --- a/opendaylight/config/yang-store-impl/pom.xml +++ b/opendaylight/config/yang-store-impl/pom.xml @@ -32,7 +32,7 @@ org.opendaylight.yangtools - binding-generator-impl + binding-type-provider ${opendaylight.binding.version} diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt new file mode 100644 index 0000000000..aad72393cb --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt @@ -0,0 +1,35 @@ + +#24 + + + 14 + fred + + < +#2 +/r +#3 +pc> +## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt new file mode 100644 index 0000000000..a36a85ea1f --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt @@ -0,0 +1,48 @@ + +#22 + + + + + +#18 + + + +#19 + + +#8 + + +#77 + + + + + +## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt new file mode 100644 index 0000000000..d9dc43d620 --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt @@ -0,0 +1,43 @@ + +#43 + + +#26 + + + +#35 + + + +#39 + + < +#40 +top xmlns="http://example.com/schema/1.2 +#26 +/config"> + + +#36 + + f +#56 +red + + + +#28 + + + + +## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt new file mode 100644 index 0000000000..0b8a102dff --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt @@ -0,0 +1,40 @@ + +#17 + + + + + + +#43 + + + +#16 + + +#22 + + + +## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt new file mode 100644 index 0000000000..f8f3c4d7c2 --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt @@ -0,0 +1,42 @@ + +#43 + + + + + < +#4 +/tar +#18 +get> + + +#41 + + + +#29 + Ethernet0/0 + 1500 + +#61 + + + + + +## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt new file mode 100644 index 0000000000..2ae32efb65 --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt @@ -0,0 +1,18 @@ +curl http://localhost:17777/jolokia/read/org.opendaylight.controller:instanceName=fixed1,type=ConfigBean,interfaceName=testing-threadpool | jsonpp +{ + "request": { + "mbean": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean", + "type": "read" + }, + "status": 200, + "timestamp": 1362416252, + "value": { + "ExportedInterfaces": [ + "testing-threadpool", + "modifiable-threadpool" + ], + "ImplementationName": "fixed", + "ThreadCount": 10, + "TriggerNewInstanceCreation": false + } +} \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt new file mode 100644 index 0000000000..2ae705a54f --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt @@ -0,0 +1,18 @@ +$ curl 'http://localhost:17777/jolokia/exec/org.opendaylight.controller:type=ConfigRegistry/lookupConfigBeans()' | jsonpp +{ + "request": { + "mbean": "org.opendaylight.controller:type=ConfigRegistry", + "operation": "lookupConfigBeans()", + "type": "exec" + }, + "status": 200, + "timestamp": 1362417043, + "value": [ + { + "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean" + }, + { + "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean" + } + ] +} diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml new file mode 100644 index 0000000000..6eca609b6c --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml new file mode 100644 index 0000000000..6a9ed639d8 --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml new file mode 100644 index 0000000000..2d66c45906 --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml new file mode 100644 index 0000000000..ce67845de1 --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml @@ -0,0 +1,20 @@ + + + + + + none + test-then-set + stop-on-error + + + + 7 + + + + + \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml new file mode 100644 index 0000000000..dd6fe1ba1e --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml new file mode 100644 index 0000000000..f94af4698d --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml new file mode 100644 index 0000000000..c70184e2b4 --- /dev/null +++ b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml @@ -0,0 +1,11 @@ + + + rpc + missing-attribute + error + + message-id + rpc + + + diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index a11f93b83d..1b747f6ce8 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -121,7 +121,7 @@ 0.1.1-SNAPSHOT - org.opendaylight.controller config-api @@ -172,7 +172,56 @@ logback-config ${config.version} - --> + + org.opendaylight.controller + config-persister-api + ${config.version} + + + org.opendaylight.controller + config-persister-file-adapter + ${config.version} + + + + + + org.opendaylight.controller + netconf-api + ${config.version} + + + org.opendaylight.controller + netconf-impl + ${config.version} + + + org.opendaylight.controller + netconf-util + ${config.version} + + + org.opendaylight.controller + netconf-client + ${config.version} + + + org.opendaylight.controller + netconf-mapping-api + ${config.version} + + + org.opendaylight.controller + config-netconf-connector + ${config.version} + + + org.opendaylight.controller + config-persister-impl + ${config.version} + + + @@ -940,8 +989,35 @@ org.opendaylight.bgpcep util + + org.opendaylight.bgpcep + framework + - + + + io.netty + netty-handler + + + io.netty + netty-codec + + + io.netty + netty-buffer + + + io.netty + netty-transport + + + io.netty + netty-common + + + + org.opendaylight.controller clustering.test @@ -982,6 +1058,57 @@ yang-binding 0.6.0-SNAPSHOT + + org.opendaylight.yangtools + binding-type-provider + 0.6.0-SNAPSHOT + + + org.opendaylight.yangtools + binding-generator-util + ${yangtools.binding.version} + + + org.opendaylight.yangtools + binding-model-api + ${yangtools.binding.version} + + + org.opendaylight.yangtools + binding-generator-spi + ${yangtools.binding.version} + + + commons-lang + commons-lang + 2.4 + + + + org.opendaylight.yangtools.thirdparty + antlr-runtime-osgi + 4.0-SNAPSHOT + + + org.opendaylight.yangtools.thirdparty + xtend-lib-osgi + 2.4.2-SNAPSHOT + + + org.opendaylight.yangtools + yang-parser-api + ${yangtools.version} + + + org.opendaylight.yangtools + yang-model-util + ${yangtools.version} + + + org.opendaylight.yangtools + yang-parser-impl + ${yangtools.version} + org.opendaylight.yangtools yang-common diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini index ac68652455..4ad7cf184b 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini @@ -13,6 +13,17 @@ osgi.bundles=\ reference\:file\:../lib/jersey-json-1.17.jar@2:start,\ reference\:file\:../lib/jersey-server-1.17.jar@2:start +# Netconf startup configuration +netconf.tcp.address=127.0.0.1 +netconf.tcp.port=8383 + +#netconf.tls.address=127.0.0.1 +#netconf.tls.port=8384 +#netconf.tls.keystore= +#netconf.tls.keystore.password= + +netconf.config.persister.storageAdapterClass=org.opendaylight.controller.netconf.persist.impl.NoOpStorageAdapter + # Set Default start level for framework osgi.bundles.defaultStartLevel=4 # Extra packages to import from the boot class loader diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml b/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml index 50a87fb568..7d04a12a14 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/logback.xml @@ -42,6 +42,9 @@ + + + diff --git a/opendaylight/netconf/config-netconf-connector/pom.xml b/opendaylight/netconf/config-netconf-connector/pom.xml new file mode 100755 index 0000000000..0386378321 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/pom.xml @@ -0,0 +1,157 @@ + + 4.0.0 + + + netconf-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + + config-netconf-connector + ${project.artifactId} + bundle + + + + + ${project.groupId} + config-api + + + ${project.groupId} + netconf-api + ${project.version} + + + org.slf4j + slf4j-api + + + com.google.guava + guava + + + ${project.groupId} + + yang-jmx-generator + + + ${project.groupId} + + config-util + + + ${project.groupId} + netconf-util + ${project.version} + + + ${project.groupId} + + yang-store-api + + + ${project.groupId} + netconf-mapping-api + ${project.version} + + + org.osgi + org.osgi.core + + + + ${project.groupId} + netconf-util + ${project.version} + test + test-jar + + + + ${project.groupId} + + yang-test + test + + + ${project.groupId} + + config-manager + ${config.version} + test + test-jar + + + ${project.groupId} + + config-manager + test + + + ${project.groupId} + netconf-impl + ${project.version} + test + + + ${project.groupId} + + yang-store-impl + test + + + org.opendaylight.bgpcep + mockito-configuration + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.controller.netconf.confignetconfconnector.osgi.Activator + + + org.opendaylight.controller.netconf.confignetconfconnector.mapping.*, + org.opendaylight.controller.netconf.confignetconfconnector.operations.*, + org.opendaylight.controller.netconf.confignetconfconnector.transactions, + org.opendaylight.controller.netconf.confignetconfconnector.util, + org.opendaylight.controller.netconf.confignetconfconnector.osgi, + org.opendaylight.controller.config.util, + + + com.google.common.base, + com.google.common.collect, + javax.annotation, + javax.management, + javax.management.openmbean, + org.opendaylight.controller.config.api, + org.opendaylight.controller.config.api.jmx, + org.opendaylight.controller.config.yang.store.api, + org.opendaylight.controller.config.yangjmxgenerator, + org.opendaylight.controller.config.yangjmxgenerator.attribute, + org.opendaylight.controller.netconf.api, + org.opendaylight.controller.netconf.mapping.api, + org.opendaylight.controller.netconf.util.mapping, + org.opendaylight.controller.netconf.util.xml, + org.opendaylight.yangtools.yang.common, + org.opendaylight.yangtools.yang.model.api, + org.osgi.framework, + org.osgi.util.tracker, + org.slf4j, + org.w3c.dom + + + + + + + + + + diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java new file mode 100644 index 0000000000..5e3f542214 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/AttributeIfcSwitchStatement.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes; + +import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; + +public abstract class AttributeIfcSwitchStatement { + + public T switchAttribute(AttributeIfc attributeIfc) { + + if (attributeIfc instanceof JavaAttribute) { + return caseJavaAttribute((JavaAttribute) attributeIfc); + } else if (attributeIfc instanceof DependencyAttribute) { + return caseDependencyAttribute((DependencyAttribute) attributeIfc); + } else if (attributeIfc instanceof ListAttribute) { + return caseListAttribute((ListAttribute) attributeIfc); + } else if (attributeIfc instanceof TOAttribute) { + return caseTOAttribute((TOAttribute) attributeIfc); + } + + throw new IllegalArgumentException("Unknown attribute type " + attributeIfc.getClass() + ", " + attributeIfc); + } + + protected abstract T caseJavaAttribute(JavaAttribute attributeIfc); + + protected abstract T caseDependencyAttribute(DependencyAttribute attributeIfc); + + protected abstract T caseTOAttribute(TOAttribute attributeIfc); + + protected abstract T caseListAttribute(ListAttribute attributeIfc); +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java new file mode 100644 index 0000000000..2ba1b61b80 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AbstractAttributeReadingStrategy.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import java.util.List; + +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.netconf.util.xml.XmlElement; + +public abstract class AbstractAttributeReadingStrategy implements AttributeReadingStrategy { + + private final A attributeIfc; + + public AbstractAttributeReadingStrategy(A attributeIfc) { + this.attributeIfc = attributeIfc; + } + + public A getAttributeIfc() { + return attributeIfc; + } + + @Override + public AttributeConfigElement readElement(List configNodes) { + if (configNodes.size() == 0) + return AttributeConfigElement.createNullValue(attributeIfc); + + return readElementHook(configNodes); + } + + abstract AttributeConfigElement readElementHook(List configNodes); + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ArrayAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ArrayAttributeReadingStrategy.java new file mode 100644 index 0000000000..f07e74035c --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ArrayAttributeReadingStrategy.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import com.google.common.collect.Lists; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.netconf.util.xml.XmlElement; + +import java.util.List; + +public class ArrayAttributeReadingStrategy extends AbstractAttributeReadingStrategy { + + private final AttributeReadingStrategy innerStrategy; + + /** + * @param attributeIfc + * @param innerStrategy + */ + public ArrayAttributeReadingStrategy(AttributeIfc attributeIfc, AttributeReadingStrategy innerStrategy) { + super(attributeIfc); + this.innerStrategy = innerStrategy; + } + + @Override + AttributeConfigElement readElementHook(List configNodes) { + List innerList = Lists.newArrayList(); + for (int i = 0; i < configNodes.size(); i++) { + innerList.add(innerStrategy.readElement(Lists.newArrayList(configNodes.get(i))).getValue()); + } + return AttributeConfigElement.create(getAttributeIfc(), innerList); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java new file mode 100644 index 0000000000..fa249da7f2 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeConfigElement.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import javax.management.openmbean.OpenType; + +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy; + +import com.google.common.base.Optional; + +/** + * Parsed xml element containing configuration for one attribute of an instance + * of some module. Contains default value extracted from yang file. + */ +public class AttributeConfigElement { + private final Object dafaultValue; + private final Object value; + + private Optional resolvedValue; + private Object resolvedDefaultValue; + private String jmxName; + + public AttributeConfigElement(Object dafaultValue, Object value) { + this.dafaultValue = dafaultValue; + this.value = value; + } + + public void setJmxName(String jmxName) { + this.jmxName = jmxName; + } + + public String getJmxName() { + return jmxName; + } + + public void resolveValue(AttributeResolvingStrategy> attributeResolvingStrategy, + String attrName) { + resolvedValue = attributeResolvingStrategy.parseAttribute(attrName, value); + Optional resolvedDefault = attributeResolvingStrategy.parseAttribute(attrName, dafaultValue); + resolvedDefaultValue = resolvedDefault.isPresent() ? resolvedDefault.get() : null; + + } + + public static AttributeConfigElement create(AttributeIfc attributeIfc, Object value) { + return new AttributeConfigElement(attributeIfc.getNullableDefault(), value); + } + + public static AttributeConfigElement createNullValue(AttributeIfc attributeIfc) { + return new AttributeConfigElement(attributeIfc.getNullableDefault(), null); + } + + public Object getValue() { + return value; + } + + public Optional getResolvedValue() { + return resolvedValue; + } + + public Object getResolvedDefaultValue() { + return resolvedDefaultValue; + } + + @Override + public String toString() { + return "AttributeConfigElement [dafaultValue=" + dafaultValue + ", value=" + value + "]"; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeReadingStrategy.java new file mode 100644 index 0000000000..6dae839148 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/AttributeReadingStrategy.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import java.util.List; + +import org.opendaylight.controller.netconf.util.xml.XmlElement; + +public interface AttributeReadingStrategy { + + public abstract AttributeConfigElement readElement(List element); + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/CompositeAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/CompositeAttributeReadingStrategy.java new file mode 100644 index 0000000000..1cfb74d652 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/CompositeAttributeReadingStrategy.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; +import org.opendaylight.controller.netconf.util.xml.XmlElement; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class CompositeAttributeReadingStrategy extends AbstractAttributeReadingStrategy { + + private final Map innerStrategies; + + public CompositeAttributeReadingStrategy(TOAttribute attributeIfc, + Map innerStrategies) { + super(attributeIfc); + this.innerStrategies = innerStrategies; + } + + @Override + AttributeConfigElement readElementHook(List configNodes) { + + Preconditions.checkState(configNodes.size() == 1, "This element should be present only once %s", configNodes); + + XmlElement complexElement = configNodes.get(0); + + Map innerMap = Maps.newHashMap(); + + Map inner = getAttributeIfc().getYangPropertiesToTypesMap(); + + List recognisedChildren = Lists.newArrayList(); + for (Entry innerAttrEntry : inner.entrySet()) { + List childItem = complexElement.getChildElementsWithSameNamespace(innerAttrEntry.getKey()); + recognisedChildren.addAll(childItem); + + AttributeConfigElement resolvedInner = innerStrategies.get(innerAttrEntry.getKey()).readElement(childItem); + + innerMap.put(innerAttrEntry.getKey(), resolvedInner.getValue()); + } + + complexElement.checkUnrecognisedElements(recognisedChildren); + + return AttributeConfigElement.create(getAttributeIfc(), innerMap); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectNameAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectNameAttributeReadingStrategy.java new file mode 100644 index 0000000000..bdb9391e2b --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectNameAttributeReadingStrategy.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import com.google.common.base.Preconditions; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; + +import java.util.List; + +public class ObjectNameAttributeReadingStrategy extends AbstractAttributeReadingStrategy { + + public ObjectNameAttributeReadingStrategy(DependencyAttribute attributeIfc) { + super(attributeIfc); + } + + @Override + AttributeConfigElement readElementHook(List configNodes) { + + XmlElement firstChild = configNodes.get(0); + Preconditions.checkState(configNodes.size() == 1, "This element should be present only once " + firstChild + + " but was " + configNodes.size()); + + Preconditions.checkNotNull(firstChild, "Element %s should be present", firstChild); + return AttributeConfigElement.create(getAttributeIfc(), resolve(firstChild)); + } + + private ObjectNameAttributeMappingStrategy.MappedDependency resolve(XmlElement firstChild) { + XmlElement typeElement = firstChild.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY); + String serviceName = typeElement.getTextContent(); + XmlElement nameElement = firstChild.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY); + String dependencyName = nameElement.getTextContent(); + + return new ObjectNameAttributeMappingStrategy.MappedDependency(serviceName, dependencyName); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java new file mode 100644 index 0000000000..8663de7365 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/ObjectXmlReader.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.SimpleType; +import java.util.Map; +import java.util.Map.Entry; + +public class ObjectXmlReader extends AttributeIfcSwitchStatement { + + private String key; + + public Map prepareReading(Map yangToAttrConfig) { + Map strategies = Maps.newHashMap(); + + for (Entry attributeEntry : yangToAttrConfig.entrySet()) { + AttributeReadingStrategy strat = prepareReadingStrategy(attributeEntry.getKey(), attributeEntry.getValue()); + strategies.put(attributeEntry.getKey(), strat); + } + return strategies; + } + + private AttributeReadingStrategy prepareReadingStrategy(String key, AttributeIfc attributeIfc) { + this.key = key; + return switchAttribute(attributeIfc); + } + + @Override + protected AttributeReadingStrategy caseJavaAttribute(JavaAttribute attributeIfc) { + if (attributeIfc.getOpenType() instanceof SimpleType) + return new SimpleAttributeReadingStrategy(attributeIfc); + else if (attributeIfc.getOpenType() instanceof ArrayType) { + SimpleAttributeReadingStrategy innerStrategy = new SimpleAttributeReadingStrategy( + ((ArrayType) attributeIfc.getOpenType()).getElementOpenType()); + return new ArrayAttributeReadingStrategy(attributeIfc, innerStrategy); + } + throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class + + " or " + ArrayType.class); + } + + @Override + protected AttributeReadingStrategy caseDependencyAttribute(DependencyAttribute attributeIfc) { + return new ObjectNameAttributeReadingStrategy(attributeIfc); + } + + @Override + protected AttributeReadingStrategy caseTOAttribute(TOAttribute attributeIfc) { + Map inner = attributeIfc.getYangPropertiesToTypesMap(); + Map innerStrategies = Maps.newHashMap(); + + for (Entry innerAttrEntry : inner.entrySet()) { + AttributeReadingStrategy innerStrat = prepareReadingStrategy(innerAttrEntry.getKey(), + innerAttrEntry.getValue()); + innerStrategies.put(innerAttrEntry.getKey(), innerStrat); + } + + return new CompositeAttributeReadingStrategy(attributeIfc, innerStrategies); + } + + @Override + protected AttributeReadingStrategy caseListAttribute(ListAttribute attributeIfc) { + AttributeIfc innerAttr = attributeIfc.getInnerAttribute(); + AttributeReadingStrategy innerStrategy = prepareReadingStrategy(key, innerAttr); + return new ArrayAttributeReadingStrategy(attributeIfc, innerStrategy); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java new file mode 100644 index 0000000000..c5c287ffe2 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/fromxml/SimpleAttributeReadingStrategy.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml; + +import com.google.common.base.Preconditions; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.netconf.util.xml.XmlElement; + +import javax.management.openmbean.OpenType; +import java.util.List; + +public class SimpleAttributeReadingStrategy extends AbstractAttributeReadingStrategy { + + public SimpleAttributeReadingStrategy(AttributeIfc attributeIfc) { + super(attributeIfc); + } + + /** + * @param elementOpenType + */ + public SimpleAttributeReadingStrategy(OpenType elementOpenType) { + super(new AttributeIfcWrapper(elementOpenType)); + } + + @Override + AttributeConfigElement readElementHook(List configNodes) { + XmlElement xmlElement = configNodes.get(0); + Preconditions.checkState(configNodes.size() == 1, "This element should be present only once " + xmlElement + + " but was " + configNodes.size()); + + String textContent = xmlElement.getTextContent(); + + Preconditions.checkNotNull(textContent, "This element should contain text %s", xmlElement); + return AttributeConfigElement.create(getAttributeIfc(), textContent); + } + + /** + * Wrapper for JavaAttribute inner element attributes (in case JavaAttribute + * is array) + */ + static class AttributeIfcWrapper implements AttributeIfc { + + private final OpenType elementOpenType; + + public AttributeIfcWrapper(OpenType elementOpenType) { + this.elementOpenType = elementOpenType; + } + + @Override + public String getAttributeYangName() { + return null; + } + + @Override + public String getNullableDescription() { + return null; + } + + @Override + public String getNullableDefault() { + return null; + } + + @Override + public String getUpperCaseCammelCase() { + return null; + } + + @Override + public String getLowerCaseCammelCase() { + return null; + } + + @Override + public OpenType getOpenType() { + return elementOpenType; + } + + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AbstractAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AbstractAttributeMappingStrategy.java new file mode 100644 index 0000000000..0d5fcb99b8 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AbstractAttributeMappingStrategy.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping; + +import javax.management.openmbean.OpenType; + +public abstract class AbstractAttributeMappingStrategy> implements + AttributeMappingStrategy { + + private final O attrOpenType; + + public AbstractAttributeMappingStrategy(O attributeIfc) { + this.attrOpenType = attributeIfc; + } + + @Override + public O getOpenType() { + return attrOpenType; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ArrayAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ArrayAttributeMappingStrategy.java new file mode 100644 index 0000000000..30436bb42f --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ArrayAttributeMappingStrategy.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping; + +import java.lang.reflect.Array; +import java.util.List; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.OpenType; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +public class ArrayAttributeMappingStrategy extends AbstractAttributeMappingStrategy, ArrayType> { + + private final AttributeMappingStrategy> innerElementStrategy; + + public ArrayAttributeMappingStrategy(ArrayType arrayType, + AttributeMappingStrategy> innerElementStrategy) { + super(arrayType); + this.innerElementStrategy = innerElementStrategy; + } + + @Override + public Optional> mapAttribute(Object value) { + if (value == null) + return Optional.absent(); + + Preconditions.checkArgument(value.getClass().isArray(), "Value has to be instanceof Array "); + + List retVal = Lists.newArrayList(); + + for (int i = 0; i < Array.getLength(value); i++) { + Object innerValue = Array.get(value, i); + // String expectedClassName = + // getOpenType().getElementOpenType().getClassName(); + // String realClassName = value.getClass().getName(); + + // Preconditions.checkState(realClassName.contains(expectedClassName), + // "Element in collection/array should be of type " + + // expectedClassName + " but was " + // + realClassName + " for attribute: " + getOpenType()); + + Optional mapAttribute = innerElementStrategy.mapAttribute(innerValue); + + if (mapAttribute.isPresent()) + retVal.add(mapAttribute.get()); + } + + return Optional.of(retVal); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AttributeMappingStrategy.java new file mode 100644 index 0000000000..51dfc5873f --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/AttributeMappingStrategy.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping; + +import com.google.common.base.Optional; + +import javax.management.openmbean.OpenType; + +public interface AttributeMappingStrategy> { + + O getOpenType(); + + Optional mapAttribute(Object o); + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/CompositeAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/CompositeAttributeMappingStrategy.java new file mode 100644 index 0000000000..252b13bf68 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/CompositeAttributeMappingStrategy.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; + +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import java.util.Map; +import java.util.Set; + +public class CompositeAttributeMappingStrategy extends + AbstractAttributeMappingStrategy, CompositeType> { + + private final Map>> innerStrategies; + private final Map jmxToJavaNameMapping; + + public CompositeAttributeMappingStrategy(CompositeType compositeType, + Map>> innerStrategies, + Map jmxToJavaNameMapping) { + super(compositeType); + this.innerStrategies = innerStrategies; + this.jmxToJavaNameMapping = jmxToJavaNameMapping; + } + + @Override + public Optional> mapAttribute(Object value) { + if (value == null) + return Optional.absent(); + + Util.checkType(value, CompositeDataSupport.class); + + CompositeDataSupport compositeData = (CompositeDataSupport) value; + CompositeType currentType = compositeData.getCompositeType(); + CompositeType expectedType = getOpenType(); + + Set expectedCompositeTypeKeys = expectedType.keySet(); + Set currentCompositeTypeKeys = currentType.keySet(); + Preconditions.checkArgument(expectedCompositeTypeKeys.equals(currentCompositeTypeKeys), + "Composite type mismatch, expected composite type with attributes " + expectedCompositeTypeKeys + + " but was " + currentCompositeTypeKeys); + + Map retVal = Maps.newHashMap(); + + for (String jmxName : jmxToJavaNameMapping.keySet()) { + String innerAttrJmxName = jmxName; + Object innerValue = compositeData.get(innerAttrJmxName); + + AttributeMappingStrategy> attributeMappingStrategy = innerStrategies + .get(innerAttrJmxName); + Optional mapAttribute = attributeMappingStrategy.mapAttribute(innerValue); + if (mapAttribute.isPresent()) + retVal.put(jmxToJavaNameMapping.get(innerAttrJmxName), mapAttribute.get()); + } + + return Optional.of(retVal); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java new file mode 100644 index 0000000000..52b6801cc0 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping; + +import java.util.Map; +import java.util.Map.Entry; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; + +import com.google.common.collect.Maps; + +public class ObjectMapper extends AttributeIfcSwitchStatement>> { + + private final Services dependencyTracker; + + public ObjectMapper(Services depTracker) { + this.dependencyTracker = depTracker; + } + + public Map>> prepareMapping( + Map configDefinition) { + Map>> strategies = Maps.newHashMap(); + + for (Entry attrEntry : configDefinition.entrySet()) { + strategies.put(attrEntry.getKey(), prepareStrategy(attrEntry.getValue())); + } + + return strategies; + } + + private AttributeMappingStrategy> prepareStrategy(AttributeIfc attributeIfc) { + return switchAttribute(attributeIfc); + } + + private Map createJmxToYangMapping(TOAttribute attributeIfc) { + Map retVal = Maps.newHashMap(); + for (Entry entry : attributeIfc.getJmxPropertiesToTypesMap().entrySet()) { + retVal.put(entry.getKey(), (entry.getValue()).getAttributeYangName()); + } + return retVal; + } + + @Override + protected AttributeMappingStrategy> caseJavaAttribute(JavaAttribute attributeIfc) { + + if (attributeIfc.getOpenType() instanceof SimpleType) + return new SimpleAttributeMappingStrategy((SimpleType) attributeIfc.getOpenType()); + else if (attributeIfc.getOpenType() instanceof ArrayType) { + ArrayType arrayType = (ArrayType) attributeIfc.getOpenType(); + AttributeMappingStrategy> innerStrategy = new SimpleAttributeMappingStrategy( + (SimpleType) arrayType.getElementOpenType()); + return new ArrayAttributeMappingStrategy(arrayType, innerStrategy); + } + throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class + + " or " + ArrayType.class); + } + + @Override + protected AttributeMappingStrategy> caseDependencyAttribute( + DependencyAttribute attributeIfc) { + String serviceName = attributeIfc.getDependency().getSie().getQName().getLocalName(); + return new ObjectNameAttributeMappingStrategy((SimpleType) attributeIfc.getOpenType(), dependencyTracker, + serviceName); + } + + @Override + protected AttributeMappingStrategy> caseTOAttribute(TOAttribute attributeIfc) { + Map>> innerStrategies = Maps.newHashMap(); + + for (Entry innerAttrEntry : attributeIfc.getJmxPropertiesToTypesMap().entrySet()) { + innerStrategies.put(innerAttrEntry.getKey(), prepareStrategy(innerAttrEntry.getValue())); + } + + return new CompositeAttributeMappingStrategy((CompositeType) attributeIfc.getOpenType(), innerStrategies, + createJmxToYangMapping(attributeIfc)); + } + + @Override + protected AttributeMappingStrategy> caseListAttribute(ListAttribute attributeIfc) { + return new ArrayAttributeMappingStrategy(attributeIfc.getOpenType(), + prepareStrategy(attributeIfc.getInnerAttribute())); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectNameAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectNameAttributeMappingStrategy.java new file mode 100644 index 0000000000..1febf02a2d --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectNameAttributeMappingStrategy.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; + +import javax.management.ObjectName; +import javax.management.openmbean.SimpleType; + +public class ObjectNameAttributeMappingStrategy extends + AbstractAttributeMappingStrategy> { + + private final Services tracker; + private final String serviceName; + + public ObjectNameAttributeMappingStrategy(SimpleType openType, Services dependencyTracker, String serviceName) { + super(openType); + this.tracker = dependencyTracker; + this.serviceName = serviceName; + } + + @Override + public Optional mapAttribute(Object value) { + if (value == null) + return Optional.absent(); + + String expectedClass = getOpenType().getClassName(); + String realClass = value.getClass().getName(); + Preconditions.checkArgument(realClass.equals(expectedClass), "Type mismatch, expected " + expectedClass + + " but was " + realClass); + Util.checkType(value, ObjectName.class); + + ObjectName on = (ObjectName) value; + String refName = tracker.addServiceEntry(serviceName, on); + + return Optional.of(new MappedDependency(serviceName, refName)); + } + + public static class MappedDependency { + private final String serviceName, refName; + + public MappedDependency(String serviceName, String refName) { + this.serviceName = serviceName; + this.refName = refName; + } + + public String getServiceName() { + return serviceName; + } + + public String getRefName() { + return refName; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("MappedDependency{"); + sb.append("serviceName='").append(serviceName).append('\''); + sb.append(", refName='").append(refName).append('\''); + sb.append('}'); + return sb.toString(); + } + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/SimpleAttributeMappingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/SimpleAttributeMappingStrategy.java new file mode 100644 index 0000000000..d92b7c432d --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/SimpleAttributeMappingStrategy.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; + +import javax.management.openmbean.SimpleType; +import java.util.Date; +import java.util.Map; + +public class SimpleAttributeMappingStrategy extends AbstractAttributeMappingStrategy> { + + public SimpleAttributeMappingStrategy(SimpleType openType) { + super(openType); + } + + @Override + public Optional mapAttribute(Object value) { + if (value == null) + return Optional.absent(); + + String expectedClass = getOpenType().getClassName(); + String realClass = value.getClass().getName(); + Preconditions.checkArgument(realClass.equals(expectedClass), "Type mismatch, expected " + expectedClass + + " but was " + realClass); + + WriterPlugin prefferedPlugin = writerPlugins.get(value.getClass().getCanonicalName()); + prefferedPlugin = prefferedPlugin == null ? writerPlugins.get(DEFAULT_WRITER_PLUGIN) : prefferedPlugin; + return Optional.of(prefferedPlugin.writeObject(value)); + } + + private static final String DEFAULT_WRITER_PLUGIN = "default"; + private static final Map writerPlugins = Maps.newHashMap(); + static { + writerPlugins.put(DEFAULT_WRITER_PLUGIN, new DefaultWriterPlugin()); + writerPlugins.put(Date.class.getCanonicalName(), new DatePlugin()); + } + + /** + * Custom writer plugins must implement this interface. + */ + static interface WriterPlugin { + String writeObject(Object value); + } + + static class DefaultWriterPlugin implements WriterPlugin { + + @Override + public String writeObject(Object value) { + return value.toString(); + } + } + + static class DatePlugin implements WriterPlugin { + + @Override + public String writeObject(Object value) { + Preconditions.checkArgument(value instanceof Date, "Attribute must be Date"); + return Util.writeDate((Date) value); + } + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AbstractAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AbstractAttributeResolvingStrategy.java new file mode 100644 index 0000000000..2808d5dfff --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AbstractAttributeResolvingStrategy.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving; + +import javax.management.openmbean.OpenType; + +abstract class AbstractAttributeResolvingStrategy> implements AttributeResolvingStrategy { + private final O openType; + + public AbstractAttributeResolvingStrategy(O openType) { + this.openType = openType; + } + + @Override + public O getOpenType() { + return openType; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ArrayAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ArrayAttributeResolvingStrategy.java new file mode 100644 index 0000000000..adee8bec58 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ArrayAttributeResolvingStrategy.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import java.lang.reflect.Array; +import java.util.List; + +final class ArrayAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy> { + + private final AttributeResolvingStrategy> innerTypeResolvingStrategy; + + private static final Logger logger = LoggerFactory.getLogger(ArrayAttributeResolvingStrategy.class); + + public ArrayAttributeResolvingStrategy(AttributeResolvingStrategy> innerTypeResolved, + ArrayType openType) { + super(openType); + this.innerTypeResolvingStrategy = innerTypeResolved; + } + + @Override + public Optional parseAttribute(String attrName, Object value) { + if (value == null) { + return Optional.absent(); + } + + Util.checkType(value, List.class); + List valueList = (List) value; + + Class innerTypeClass = null; + + if (innerTypeResolvingStrategy.getOpenType() instanceof CompositeType) { + innerTypeClass = CompositeDataSupport.class; + } else + try { + innerTypeClass = Class.forName(getOpenType().getElementOpenType().getClassName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to locate class for " + + getOpenType().getElementOpenType().getClassName(), e); + } + + Object parsedArray = null; + + if (getOpenType().isPrimitiveArray()) { + Class primitiveType = getPrimitiveType(innerTypeClass); + parsedArray = Array.newInstance(primitiveType, valueList.size()); + } else + parsedArray = Array.newInstance(innerTypeClass, valueList.size()); + + int i = 0; + for (Object innerValue : valueList) { + Optional parsedElement = innerTypeResolvingStrategy.parseAttribute(attrName + "_" + i, innerValue); + + if (!parsedElement.isPresent()) + continue; + + Array.set(parsedArray, i, parsedElement.get()); + // parsedArray[i] = parsedElement.get(); + i++; + } + + logger.debug("Attribute {} : {} parsed to type {} as {}", attrName, value, getOpenType(), + toStringArray(parsedArray)); + + return Optional.of(parsedArray); + } + + private static String toStringArray(Object array) { + StringBuilder build = new StringBuilder(array.toString()); + build.append(" ["); + for (int i = 0; i < Array.getLength(array); i++) { + build.append(Array.get(array, i).toString()); + build.append(","); + } + build.append("]]"); + return build.toString(); + } + + private static Class getPrimitiveType(Class innerTypeClass) { + try { + return (Class) innerTypeClass.getField("TYPE").get(null); + } catch (Exception e) { + throw new RuntimeException("Unable to determine primitive type to " + innerTypeClass); + } + } + + @Override + public String toString() { + return "ResolvedArrayTypeAttributeType [innerTypeResolved=" + innerTypeResolvingStrategy + "]"; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AttributeResolvingStrategy.java new file mode 100644 index 0000000000..0bb274ae41 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/AttributeResolvingStrategy.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving; + +import com.google.common.base.Optional; + +import javax.management.openmbean.OpenType; + +/** + * Create real object from String or Map that corresponds to given opentype. + */ +public interface AttributeResolvingStrategy> { + O getOpenType(); + + Optional parseAttribute(String attrName, Object value); +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java new file mode 100644 index 0000000000..a85f3064cf --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/CompositeAttributeResolvingStrategy.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import java.util.Map; + +final class CompositeAttributeResolvingStrategy extends + AbstractAttributeResolvingStrategy { + private final Map>> innerTypes; + private final Map yangToJavaAttrMapping; + + private static final Logger logger = LoggerFactory.getLogger(CompositeAttributeResolvingStrategy.class); + + CompositeAttributeResolvingStrategy(Map>> innerTypes, + CompositeType openType, Map yangToJavaAttrMapping) { + super(openType); + this.innerTypes = innerTypes; + this.yangToJavaAttrMapping = yangToJavaAttrMapping; + } + + @Override + public String toString() { + return "ResolvedCompositeAttribute [" + innerTypes + "]"; + } + + @Override + public Optional parseAttribute(String attrName, Object value) { + + if (value == null) { + return Optional.absent(); + } + + Util.checkType(value, Map.class); + Map valueMap = (Map) value; + + Map items = Maps.newHashMap(); + Map> openTypes = Maps.newHashMap(); + + for (Object innerAttrName : innerTypes.keySet()) { + Preconditions.checkState(innerAttrName instanceof String, "Attribute name must be string"); + String innerAttrNameStr = (String) innerAttrName; + + AttributeResolvingStrategy> attributeResolvingStrategy = innerTypes + .get(innerAttrName); + + Object valueToParse = valueMap.get(innerAttrName); + + Optional parsedInnerValue = attributeResolvingStrategy.parseAttribute(innerAttrNameStr, valueToParse); + + openTypes.put(innerAttrNameStr, attributeResolvingStrategy.getOpenType()); + + items.put(yangToJavaAttrMapping.get(innerAttrNameStr), + parsedInnerValue.isPresent() ? parsedInnerValue.get() : null); + } + + CompositeDataSupport parsedValue = null; + try { + parsedValue = new CompositeDataSupport(getOpenType(), items); + } catch (OpenDataException e) { + throw new RuntimeException("An error occured during restoration of composite type " + this + + " for attribute " + attrName + " from value " + value, e); + } + + logger.debug("Attribute {} : {} parsed to type {} as {}", attrName, value, getOpenType(), parsedValue); + + return Optional.of(parsedValue); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectNameAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectNameAttributeResolvingStrategy.java new file mode 100644 index 0000000000..5469015a23 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectNameAttributeResolvingStrategy.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving; + +import com.google.common.base.Optional; +import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services.ServiceInstance; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.ObjectName; +import javax.management.openmbean.SimpleType; + +public class ObjectNameAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy> { + + private final Services serviceTracker; + private static final Logger logger = LoggerFactory.getLogger(ObjectNameAttributeResolvingStrategy.class); + + ObjectNameAttributeResolvingStrategy(Services serviceTracker) { + super(SimpleType.OBJECTNAME); + this.serviceTracker = serviceTracker; + } + + @Override + public Optional parseAttribute(String attrName, Object value) { + if (value == null) { + return Optional.absent(); + } + + Util.checkType(value, ObjectNameAttributeMappingStrategy.MappedDependency.class); + + ObjectNameAttributeMappingStrategy.MappedDependency mappedDep = (ObjectNameAttributeMappingStrategy.MappedDependency) value; + ServiceInstance byRefName = serviceTracker.getByServiceAndRefName(mappedDep.getServiceName(), + mappedDep.getRefName()); + ObjectName on = ObjectNameUtil.createReadOnlyModuleON(byRefName.getModuleName(), byRefName.getInstanceName()); + logger.debug("Attribute {} : {} parsed to type {}", attrName, value, getOpenType()); + return Optional.of(on); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java new file mode 100644 index 0000000000..386047165e --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/ObjectResolver.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving; + +import java.util.Map; +import java.util.Map.Entry; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + +import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; + +public class ObjectResolver extends AttributeIfcSwitchStatement>> { + + private final Services serviceTracker; + private OpenType openType; + + public ObjectResolver(Services serviceTracker) { + this.serviceTracker = serviceTracker; + } + + public Map>> prepareResolving( + Map configDefinition) { + Map>> strategies = Maps.newHashMap(); + + for (Entry attrEntry : configDefinition.entrySet()) { + strategies.put(attrEntry.getKey(), + prepareStrategy(attrEntry.getValue(), attrEntry.getValue().getOpenType())); + } + + return strategies; + } + + private AttributeResolvingStrategy> prepareStrategy(AttributeIfc attributeIfc, + OpenType openType) { + + this.openType = openType; + return switchAttribute(attributeIfc); + } + + private Map createYangToJmxMapping(TOAttribute attributeIfc) { + Map retVal = Maps.newHashMap(); + for (Entry entry : attributeIfc.getYangPropertiesToTypesMap().entrySet()) { + retVal.put(entry.getKey(), (entry.getValue()).getLowerCaseCammelCase()); + } + return retVal; + } + + @Override + protected AttributeResolvingStrategy> caseJavaAttribute(JavaAttribute attributeIfc) { + if (attributeIfc.getOpenType() instanceof SimpleType) + return new SimpleAttributeResolvingStrategy((SimpleType) openType); + else if (attributeIfc.getOpenType() instanceof ArrayType) { + ArrayType arrayType = (ArrayType) openType; + SimpleType innerType = (SimpleType) arrayType.getElementOpenType(); + AttributeResolvingStrategy> strat = new SimpleAttributeResolvingStrategy(innerType); + return new ArrayAttributeResolvingStrategy(strat, arrayType); + } + throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class + + " or " + ArrayType.class); + } + + @Override + protected AttributeResolvingStrategy> caseDependencyAttribute( + DependencyAttribute attributeIfc) { + return new ObjectNameAttributeResolvingStrategy(serviceTracker); + } + + @Override + protected AttributeResolvingStrategy> caseTOAttribute(TOAttribute attributeIfc) { + CompositeType compositeType = (CompositeType) openType; + Map>> innerMap = Maps.newHashMap(); + for (String innerName : compositeType.keySet()) { + Preconditions.checkState(attributeIfc instanceof TOAttribute, "Unexpected state, " + attributeIfc + + " should be instance of " + TOAttribute.class.getName()); + AttributeIfc innerAttributeIfc = attributeIfc.getJmxPropertiesToTypesMap().get(innerName); + innerMap.put(innerAttributeIfc.getAttributeYangName(), + prepareStrategy(innerAttributeIfc, compositeType.getType(innerName))); + } + return new CompositeAttributeResolvingStrategy(innerMap, compositeType, createYangToJmxMapping(attributeIfc)); + } + + @Override + protected AttributeResolvingStrategy> caseListAttribute(ListAttribute attributeIfc) { + ArrayType arrayType = (ArrayType) openType; + OpenType innerType = arrayType.getElementOpenType(); + AttributeIfc inner = attributeIfc.getInnerAttribute(); + return new ArrayAttributeResolvingStrategy(prepareStrategy(inner, innerType), arrayType); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/SimpleAttributeResolvingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/SimpleAttributeResolvingStrategy.java new file mode 100644 index 0000000000..3b881579f1 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/resolving/SimpleAttributeResolvingStrategy.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving; + +import com.google.common.base.Optional; +import com.google.common.collect.Maps; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.openmbean.SimpleType; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.util.Date; +import java.util.Map; + +final class SimpleAttributeResolvingStrategy extends AbstractAttributeResolvingStrategy> { + + private static final Logger logger = LoggerFactory.getLogger(SimpleAttributeResolvingStrategy.class); + + SimpleAttributeResolvingStrategy(SimpleType simpleType) { + super(simpleType); + } + + @Override + public String toString() { + return "ResolvedSimpleAttribute [" + getOpenType().getClassName() + "]"; + } + + @Override + public Optional parseAttribute(String attrName, Object value) { + if (value == null) { + return Optional.absent(); + } + + Class cls; + try { + cls = Class.forName(getOpenType().getClassName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unable to locate class for " + getOpenType().getClassName(), e); + } + + Util.checkType(value, String.class); + + Resolver prefferedPlugin = resolverPlugins.get(cls.getCanonicalName()); + prefferedPlugin = prefferedPlugin == null ? resolverPlugins.get(DEFAULT_RESOLVERS) : prefferedPlugin; + + Object parsedValue = prefferedPlugin.resolveObject(cls, attrName, (String) value); + logger.debug("Attribute {} : {} parsed to type {} with value {}", attrName, value, getOpenType(), parsedValue); + return Optional.of(parsedValue); + } + + private static final String DEFAULT_RESOLVERS = "default"; + private static final Map resolverPlugins = Maps.newHashMap(); + + static { + resolverPlugins.put(DEFAULT_RESOLVERS, new DefaultResolver()); + resolverPlugins.put(String.class.getCanonicalName(), new StringResolver()); + resolverPlugins.put(Date.class.getCanonicalName(), new DateResolver()); + resolverPlugins.put(Character.class.getCanonicalName(), new CharResolver()); + resolverPlugins.put(BigInteger.class.getCanonicalName(), new BigIntegerResolver()); + } + + static interface Resolver { + Object resolveObject(Class type, String attrName, String value); + } + + static class DefaultResolver implements Resolver { + + @Override + public Object resolveObject(Class type, String attrName, String value) { + try { + Object parsedValue = parseObject(type, value); + return parsedValue; + } catch (Exception e) { + throw new RuntimeException("Unable to resolve attribute " + attrName + " from " + value, e); + } + } + + protected Object parseObject(Class type, String value) throws Exception { + Method method = type.getMethod("valueOf", String.class); + Object parsedValue = method.invoke(null, value); + return parsedValue; + } + } + + static class StringResolver extends DefaultResolver { + + @Override + protected Object parseObject(Class type, String value) throws Exception { + return value; + } + } + + static class BigIntegerResolver extends DefaultResolver { + + @Override + protected Object parseObject(Class type, String value) throws Exception { + return new BigInteger(value); + } + } + + static class CharResolver extends DefaultResolver { + + @Override + protected Object parseObject(Class type, String value) throws Exception { + return new Character(value.charAt(0)); + } + } + + static class DateResolver extends DefaultResolver { + + @Override + protected Object parseObject(Class type, String value) throws Exception { + return Util.readDate(value); + } + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ArrayAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ArrayAttributeWritingStrategy.java new file mode 100644 index 0000000000..89dc251b4e --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ArrayAttributeWritingStrategy.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml; + +import java.util.List; + +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.w3c.dom.Element; + +public class ArrayAttributeWritingStrategy implements AttributeWritingStrategy { + + private final AttributeWritingStrategy innnerStrategy; + + public ArrayAttributeWritingStrategy(AttributeWritingStrategy innerStrategy) { + this.innnerStrategy = innerStrategy; + } + + @Override + public void writeElement(Element parentElement, String namespace, Object value) { + Util.checkType(value, List.class); + + for (Object innerObject : ((List) value)) { + innnerStrategy.writeElement(parentElement, namespace, innerObject); + } + + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/AttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/AttributeWritingStrategy.java new file mode 100644 index 0000000000..d0c2b885f7 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/AttributeWritingStrategy.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml; + +import org.w3c.dom.Element; + +public interface AttributeWritingStrategy { + + void writeElement(Element parentElement, String namespace, Object value); + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/CompositeAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/CompositeAttributeWritingStrategy.java new file mode 100644 index 0000000000..d1326bde8e --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/CompositeAttributeWritingStrategy.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml; + +import java.util.Map; +import java.util.Map.Entry; + +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class CompositeAttributeWritingStrategy implements AttributeWritingStrategy { + + protected final String key; + protected final Document document; + protected final Map innerStrats; + + public CompositeAttributeWritingStrategy(Document document, String key, + Map innerStrats) { + this.document = document; + this.key = key; + this.innerStrats = innerStrats; + } + + @Override + public void writeElement(Element parentElement, String namespace, Object value) { + Util.checkType(value, Map.class); + + Element innerNode = document.createElement(key); + XmlUtil.addNamespaceAttr(innerNode, namespace); + + Map map = (Map) value; + + for (Entry innerObjectEntry : map.entrySet()) { + Util.checkType(innerObjectEntry.getKey(), String.class); + + String innerKey = (String) innerObjectEntry.getKey(); + Object innerValue = innerObjectEntry.getValue(); + + innerStrats.get(innerKey).writeElement(innerNode, namespace, innerValue); + } + parentElement.appendChild(innerNode); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectNameAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectNameAttributeWritingStrategy.java new file mode 100644 index 0000000000..b88b6722c8 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectNameAttributeWritingStrategy.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml; + +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectNameAttributeMappingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class ObjectNameAttributeWritingStrategy implements AttributeWritingStrategy { + + private final Document document; + private final String key; + + /** + * @param document + * @param key + */ + public ObjectNameAttributeWritingStrategy(Document document, String key) { + this.document = document; + this.key = key; + } + + @Override + public void writeElement(Element parentElement, String namespace, Object value) { + Util.checkType(value, ObjectNameAttributeMappingStrategy.MappedDependency.class); + Element innerNode = document.createElement(key); + XmlUtil.addNamespaceAttr(innerNode, namespace); + + String moduleName = ((ObjectNameAttributeMappingStrategy.MappedDependency) value).getServiceName(); + String refName = ((ObjectNameAttributeMappingStrategy.MappedDependency) value).getRefName(); + + final Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY, moduleName); + innerNode.appendChild(typeElement); + + final Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, refName); + innerNode.appendChild(nameElement); + + parentElement.appendChild(innerNode); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java new file mode 100644 index 0000000000..b49d20ed35 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; +import org.w3c.dom.Document; + +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.SimpleType; +import java.util.Map; +import java.util.Map.Entry; + +public class ObjectXmlWriter extends AttributeIfcSwitchStatement { + + private Document document; + private String key; + + public Map prepareWriting(Map yangToAttrConfig, + Document document) { + + Map preparedWriting = Maps.newHashMap(); + + for (Entry mappedAttributeEntry : yangToAttrConfig.entrySet()) { + String key = mappedAttributeEntry.getKey(); + AttributeIfc value = mappedAttributeEntry.getValue(); + AttributeWritingStrategy strat = prepareWritingStrategy(key, value, document); + preparedWriting.put(key, strat); + } + + return preparedWriting; + } + + private AttributeWritingStrategy prepareWritingStrategy(String key, AttributeIfc expectedAttr, Document document) { + Preconditions.checkNotNull(expectedAttr, "Mbean attributes mismatch, unable to find expected attribute for %s", + key); + this.document = document; + this.key = key; + return switchAttribute(expectedAttr); + } + + @Override + protected AttributeWritingStrategy caseJavaAttribute(JavaAttribute attributeIfc) { + + if (attributeIfc.getOpenType() instanceof SimpleType) + return new SimpleAttributeWritingStrategy(document, key); + else if (attributeIfc.getOpenType() instanceof ArrayType) { + AttributeWritingStrategy innerStrategy = new SimpleAttributeWritingStrategy(document, key); + return new ArrayAttributeWritingStrategy(innerStrategy); + } + throw new IllegalStateException(JavaAttribute.class + " can only provide open type " + SimpleType.class + + " or " + ArrayType.class); + } + + @Override + protected AttributeWritingStrategy caseDependencyAttribute(DependencyAttribute attributeIfc) { + return new ObjectNameAttributeWritingStrategy(document, key); + } + + @Override + protected AttributeWritingStrategy caseTOAttribute(TOAttribute attributeIfc) { + Map innerStrats = Maps.newHashMap(); + String currentKey = key; + for (Entry innerAttrEntry : attributeIfc.getYangPropertiesToTypesMap().entrySet()) { + + AttributeWritingStrategy innerStrategy = prepareWritingStrategy(innerAttrEntry.getKey(), + innerAttrEntry.getValue(), document); + innerStrats.put(innerAttrEntry.getKey(), innerStrategy); + } + + return new CompositeAttributeWritingStrategy(document, currentKey, innerStrats); + } + + @Override + protected AttributeWritingStrategy caseListAttribute(ListAttribute attributeIfc) { + AttributeIfc inner = attributeIfc.getInnerAttribute(); + AttributeWritingStrategy innerStrategy = prepareWritingStrategy(key, inner, document); + return new ArrayAttributeWritingStrategy(innerStrategy); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/RuntimeBeanEntryWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/RuntimeBeanEntryWritingStrategy.java new file mode 100644 index 0000000000..a67b348f9f --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/RuntimeBeanEntryWritingStrategy.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml; + +import java.util.Map; +import java.util.Map.Entry; + +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class RuntimeBeanEntryWritingStrategy extends CompositeAttributeWritingStrategy { + + public RuntimeBeanEntryWritingStrategy(Document document, String key, + Map innerStrats) { + super(document, key, innerStrats); + } + + /* + * (non-Javadoc) + * + * @see org.opendaylight.controller.config.netconf.mapping.attributes.toxml. + * AttributeWritingStrategy#writeElement(org.w3c.dom.Element, + * java.lang.Object) + */ + @Override + public void writeElement(Element parentElement, String namespace, Object value) { + Util.checkType(value, Map.class); + + Element innerNode = document.createElement(key); + + Map map = (Map) value; + + for (Entry runtimeBeanInstanceMappingEntry : map.entrySet()) { + + // wrap runtime attributes with number assigned to current runtime + // bean + Util.checkType(runtimeBeanInstanceMappingEntry.getValue(), Map.class); + Map innerMap = (Map) runtimeBeanInstanceMappingEntry.getValue(); + Element runtimeInstanceNode = document.createElement("_" + + (String) runtimeBeanInstanceMappingEntry.getKey()); + innerNode.appendChild(runtimeInstanceNode); + + for (Entry innerObjectEntry : innerMap.entrySet()) { + + Util.checkType(innerObjectEntry.getKey(), String.class); + + String innerKey = (String) innerObjectEntry.getKey(); + Object innerValue = innerObjectEntry.getValue(); + + innerStrats.get(innerKey).writeElement(runtimeInstanceNode, namespace, innerValue); + } + } + parentElement.appendChild(innerNode); + + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleAttributeWritingStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleAttributeWritingStrategy.java new file mode 100644 index 0000000000..514183be2f --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/SimpleAttributeWritingStrategy.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml; + +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class SimpleAttributeWritingStrategy implements AttributeWritingStrategy { + + private final Document document; + private final String key; + + /** + * @param document + * @param key + */ + public SimpleAttributeWritingStrategy(Document document, String key) { + this.document = document; + this.key = key; + } + + @Override + public void writeElement(Element parentElement, String namespace, Object value) { + Util.checkType(value, String.class); + Element innerNode = XmlUtil.createTextElement(document, key, (String) value); + XmlUtil.addNamespaceAttr(innerNode, namespace); + parentElement.appendChild(innerNode); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Config.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Config.java new file mode 100644 index 0000000000..b17a8a8833 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Config.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import java.util.*; +import java.util.Map.Entry; + +import static com.google.common.base.Preconditions.checkState; +import static java.lang.String.format; + +public class Config { + + private final Map> moduleConfigs; + + public Config(Map> moduleConfigs) { + this.moduleConfigs = moduleConfigs; + } + + private Map>> getMappedInstances(Set instancesToMap, + Services serviceTracker) { + Multimap moduleToInstances = mapInstancesToModules(instancesToMap); + + Map>> retVal = Maps.newLinkedHashMap(); + + for (String namespace : moduleConfigs.keySet()) { + + Map> innerRetVal = Maps.newHashMap(); + + for (Entry mbeEntry : moduleConfigs.get(namespace).entrySet()) { + + String moduleName = mbeEntry.getKey(); + Collection instances = moduleToInstances.get(moduleName); + + if (instances == null) + continue; + + innerRetVal.put(moduleName, instances); + + // All found instances add to service tracker in advance + // This way all instances will be serialized as all available + // services when get-config is triggered + // (even if they are not used as services by other onstances) + // = more user friendly + addServices(serviceTracker, instances, mbeEntry.getValue().getProvidedServices()); + + } + + retVal.put(namespace, innerRetVal); + } + return retVal; + } + + private void addServices(Services serviceTracker, Collection instances, + Collection providedServices) { + for (ObjectName instanceOn : instances) { + for (String serviceName : providedServices) { + serviceTracker.addServiceEntry(serviceName, instanceOn); + } + } + } + + private static Multimap mapInstancesToModules(Set instancesToMap) { + Multimap retVal = HashMultimap.create(); + + for (ObjectName objectName : instancesToMap) { + String factoryName = ObjectNameUtil.getFactoryName(objectName); + retVal.put(factoryName, objectName); + } + return retVal; + } + + // public Element toXml(Set instancesToMap, String namespace, + // Document document) { + // return toXml(instancesToMap, Optional.of(namespace), document); + // } + + public Element toXml(Set instancesToMap, Optional maybeNamespace, Document document, + Element dataElement) { + Services serviceTracker = new Services(); + + Map>> moduleToInstances = getMappedInstances(instancesToMap, + serviceTracker); + + Element root = dataElement; + if (maybeNamespace.isPresent()) { + XmlUtil.addNamespaceAttr(root, maybeNamespace.get()); + } + + Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY); + XmlUtil.addNamespaceAttr(modulesElement, + XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + root.appendChild(modulesElement); + for (String moduleNamespace : moduleToInstances.keySet()) { + for (Entry> moduleMappingEntry : moduleToInstances.get(moduleNamespace) + .entrySet()) { + + ModuleConfig mapping = moduleConfigs.get(moduleNamespace).get(moduleMappingEntry.getKey()); + + if (moduleMappingEntry.getValue().isEmpty()) { + addEmptyModulesCommented(document, modulesElement, moduleNamespace, moduleMappingEntry); + } else { + for (ObjectName objectName : moduleMappingEntry.getValue()) { + modulesElement + .appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace)); + } + } + + } + } + + root.appendChild(serviceTracker.toXml(serviceTracker.getMappedServices(), document)); + + return root; + } + + private void addEmptyModulesCommented(Document document, Element root, String moduleNamespace, + Entry> moduleMappingEntry) { + Element emptyModule = document.createElement(XmlNetconfConstants.MODULE_KEY); + + Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY, + moduleMappingEntry.getKey()); + emptyModule.appendChild(typeElement); + + root.appendChild(document.createComment(XmlUtil.toString(emptyModule, false))); + } + + // TODO refactor, replace string representing namespace with namespace class + // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved + // class + public Map> fromXml(XmlElement xml) { + Map> retVal = Maps.newHashMap(); + + List recognisedChildren = Lists.newArrayList(); + + Services serviceTracker = fromXmlServices(xml, recognisedChildren); + List moduleElements = fromXmlModules(xml, recognisedChildren); + + xml.checkUnrecognisedElements(recognisedChildren); + + for (XmlElement moduleElement : moduleElements) { + resolveModule(retVal, serviceTracker, moduleElement); + } + + return retVal; + } + + private List fromXmlModules(XmlElement xml, List recognisedChildren) { + Optional modulesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY, + XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + List moduleElements; + if (modulesElement.isPresent()) { + moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY); + recognisedChildren.add(modulesElement.get()); + modulesElement.get().checkUnrecognisedElements(moduleElements); + } else { + moduleElements = Lists.newArrayList(); + } + return moduleElements; + } + + private void resolveModule(Map> retVal, Services serviceTracker, + XmlElement moduleElement) { + XmlElement typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY); + Entry prefixToNamespace = typeElement.findNamespaceOfTextContent(); + String moduleNamespace = prefixToNamespace.getValue(); + XmlElement nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY); + String instanceName = nameElement.getTextContent(); + String factoryNameWithPrefix = typeElement.getTextContent(); + String prefixOrEmptyString = prefixToNamespace.getKey(); + String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString); + + ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName); + + Multimap innerMap = retVal.get(moduleNamespace); + if (innerMap == null) { + innerMap = HashMultimap.create(); + retVal.put(moduleNamespace, innerMap); + } + + ModuleElementResolved moduleElementResolved = moduleMapping.fromXml(moduleElement, serviceTracker, + instanceName, moduleNamespace); + + innerMap.put(factoryName, moduleElementResolved); + } + + private Services fromXmlServices(XmlElement xml, List recognisedChildren) { + Optional servicesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY, + XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + + Map> mappedServices; + if (servicesElement.isPresent()) { + mappedServices = Services.fromXml(servicesElement.get()); + recognisedChildren.add(servicesElement.get()); + } else { + mappedServices = new HashMap<>(); + } + + return Services.resolveServices(mappedServices); + } + + private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) { + checkState( + factoryNameWithPrefix.startsWith(prefixOrEmptyString), + format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'", + factoryNameWithPrefix, prefixOrEmptyString)); + + int factoryNameAfterPrefixIndex; + if (prefixOrEmptyString.isEmpty()) { + factoryNameAfterPrefixIndex = 0; + } else { + factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1; + } + return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex); + } + + private ModuleConfig getModuleMapping(String moduleNamespace, String instanceName, String factoryName) { + Map mappingsFromNamespace = moduleConfigs.get(moduleNamespace); + + Preconditions.checkNotNull(mappingsFromNamespace, + "Namespace %s, defined in: module %s of type %s not found, available namespaces: %s", moduleNamespace, + instanceName, factoryName, moduleConfigs.keySet()); + + ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName); + checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName); + return moduleMapping; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java new file mode 100644 index 0000000000..d1ba0b0206 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfig.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeReadingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.ObjectXmlReader; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.AttributeMappingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.ObjectMapper; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.ObjectResolver; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml.AttributeWritingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.toxml.ObjectXmlWriter; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import javax.management.openmbean.OpenType; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public final class InstanceConfig { + private static final Logger logger = LoggerFactory.getLogger(InstanceConfig.class); + + private final Map yangToAttrConfig; + private final Map jmxToAttrConfig; + private final ConfigRegistryClient configRegistryClient; + + public InstanceConfig(ConfigRegistryClient configRegistryClient, Map yangNamesToAttributes) { + this.yangToAttrConfig = yangNamesToAttributes; + this.jmxToAttrConfig = reverseMap(yangNamesToAttributes); + this.configRegistryClient = configRegistryClient; + } + + private Map getMappedConfiguration(ObjectName on, Services depTracker) { + + // TODO make field, mappingStrategies can be instantiated only once + Map>> mappingStrategies = new ObjectMapper(depTracker) + .prepareMapping(jmxToAttrConfig); + + Map toXml = Maps.newHashMap(); + + for (Entry configDefEntry : jmxToAttrConfig.entrySet()) { + + // Skip children runtime beans as they are mapped by InstanceRuntime + if (configDefEntry.getValue() instanceof RuntimeBeanEntry) + continue; + + Object value = configRegistryClient.getAttributeCurrentValue(on, configDefEntry.getKey()); + try { + AttributeMappingStrategy> attributeMappingStrategy = mappingStrategies + .get(configDefEntry.getKey()); + Optional a = attributeMappingStrategy.mapAttribute(value); + if (a.isPresent() == false) + continue; + + toXml.put(configDefEntry.getValue().getAttributeYangName(), a.get()); + } catch (Exception e) { + throw new IllegalStateException("Unable to map value " + value + " to attribute " + + configDefEntry.getKey(), e); + } + } + + return toXml; + } + + public Element toXml(ObjectName on, Services depTracker, String namespace, Document document, Element rootElement) { + + Element cfgElement = rootElement; + + Map strats = new ObjectXmlWriter().prepareWriting(yangToAttrConfig, document); + + Map mappedConfig = getMappedConfiguration(on, depTracker); + + for (Entry mappingEntry : mappedConfig.entrySet()) { + try { + strats.get(mappingEntry.getKey()).writeElement(cfgElement, namespace, mappingEntry.getValue()); + } catch (Exception e) { + throw new IllegalStateException("Unable to write value " + mappingEntry.getValue() + " for attribute " + + mappingEntry.getValue(), e); + } + } + + return cfgElement; + } + + private void resolveConfiguration(InstanceConfigElementResolved mappedConfig, Services depTracker) { + + // TODO make field, resolvingStrategies can be instantiated only once + Map>> resolvingStrategies = new ObjectResolver( + depTracker).prepareResolving(yangToAttrConfig); + + for (Entry configDefEntry : mappedConfig.getConfiguration().entrySet()) { + try { + + AttributeResolvingStrategy> attributeResolvingStrategy = resolvingStrategies + .get(configDefEntry.getKey()); + + configDefEntry.getValue().resolveValue(attributeResolvingStrategy, configDefEntry.getKey()); + configDefEntry.getValue().setJmxName( + yangToAttrConfig.get(configDefEntry.getKey()).getUpperCaseCammelCase()); + } catch (Exception e) { + throw new IllegalStateException("Unable to resolve value " + configDefEntry.getValue() + + " to attribute " + configDefEntry.getKey(), e); + } + } + } + + public InstanceConfigElementResolved fromXml(XmlElement moduleElement, Services services, String moduleNamespace) { + Map retVal = Maps.newHashMap(); + + Map strats = new ObjectXmlReader().prepareReading(yangToAttrConfig); + List recognisedChildren = Lists.newArrayList(); + + XmlElement type = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY); + XmlElement name = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY); + List typeAndName = Lists.newArrayList(type, name); + + for (Entry readStratEntry : strats.entrySet()) { + List configNodes = getConfigNodes(moduleElement, moduleNamespace, readStratEntry.getKey(), + recognisedChildren, typeAndName); + AttributeConfigElement readElement = readStratEntry.getValue().readElement(configNodes); + retVal.put(readStratEntry.getKey(), readElement); + } + + recognisedChildren.addAll(typeAndName); + moduleElement.checkUnrecognisedElements(recognisedChildren); + + // TODO: add check for conflicts between global and local edit strategy + String perInstanceEditStrategy = moduleElement.getAttribute(XmlNetconfConstants.OPERATION_ATTR_KEY, + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + + InstanceConfigElementResolved instanceConfigElementResolved = perInstanceEditStrategy.equals("") ? new InstanceConfigElementResolved( + retVal) : new InstanceConfigElementResolved(perInstanceEditStrategy, retVal); + + resolveConfiguration(instanceConfigElementResolved, services); + return instanceConfigElementResolved; + } + + private List getConfigNodes(XmlElement moduleElement, String moduleNamespace, String name, + List recognisedChildren, List typeAndName) { + List foundConfigNodes = moduleElement.getChildElementsWithinNamespace(name, moduleNamespace); + if (foundConfigNodes.isEmpty()) { + logger.debug("No config nodes {}:{} found in {}", moduleNamespace, name, moduleElement); + logger.debug("Trying lookup of config nodes without specified namespace"); + foundConfigNodes = moduleElement.getChildElementsWithinNamespace(name, + XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + // In case module type or name element is not present in config it + // would be matched with config type or name + // We need to remove config type and name from available module + // config elements + foundConfigNodes.removeAll(typeAndName); + logger.debug("Found {} config nodes {} without specified namespace in {}", foundConfigNodes.size(), name, + moduleElement); + } else { + List foundWithoutNamespaceNodes = moduleElement.getChildElementsWithinNamespace(name, + XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + foundWithoutNamespaceNodes.removeAll(typeAndName); + Preconditions.checkState(foundWithoutNamespaceNodes.isEmpty(), + "Element %s present multiple times with different namespaces: %s, %s", name, foundConfigNodes, + foundWithoutNamespaceNodes); + } + + recognisedChildren.addAll(foundConfigNodes); + return foundConfigNodes; + } + + private static Map reverseMap(Map yangNameToAttr) { + Map reversednameToAtr = Maps.newHashMap(); + + for (Entry entry : yangNameToAttr.entrySet()) { + reversednameToAtr.put(entry.getValue().getUpperCaseCammelCase(), entry.getValue()); + } + + return reversednameToAtr; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfigElementResolved.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfigElementResolved.java new file mode 100644 index 0000000000..6624fc182e --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/InstanceConfigElementResolved.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config; + +import com.google.common.base.Preconditions; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType; + +import java.util.Map; + +/** + * Parsed xml element containing whole configuration for an instance of some + * module. Contains preferred edit strategy type. + */ +public class InstanceConfigElementResolved { + + private final EditStrategyType editStrategy; + private final Map configuration; + + public InstanceConfigElementResolved(String strat, Map configuration) { + EditStrategyType valueOf = checkStrategy(strat); + this.editStrategy = valueOf; + this.configuration = configuration; + } + + EditStrategyType checkStrategy(String strat) { + EditStrategyType valueOf = EditStrategyType.valueOf(strat); + if (EditStrategyType.defaultStrategy().isEnforcing()) { + Preconditions + .checkArgument( + valueOf == EditStrategyType.defaultStrategy(), + "With " + + EditStrategyType.defaultStrategy() + + " as " + + EditConfigXmlParser.DEFAULT_OPERATION_KEY + + " operations on module elements are not permitted since the default option is restrictive"); + } + return valueOf; + } + + public InstanceConfigElementResolved(Map configuration) { + editStrategy = EditStrategyType.defaultStrategy(); + this.configuration = configuration; + } + + public EditConfigStrategy getEditStrategy() { + return editStrategy.getFittingStrategy(); + } + + public Map getConfiguration() { + return configuration; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleConfig.java new file mode 100644 index 0000000000..3a0c54754b --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleConfig.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config; + +import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import java.util.Collection; +import java.util.Collections; + +public class ModuleConfig { + + private final String moduleName; + private final InstanceConfig instanceConfig; + private final Collection providedServices; + + public ModuleConfig(String moduleName, InstanceConfig mbeanMapping, Collection providedServices) { + this.moduleName = moduleName; + this.instanceConfig = mbeanMapping; + this.providedServices = providedServices; + } + + public ModuleConfig(String key, InstanceConfig instanceConfig) { + this(key, instanceConfig, Collections. emptyList()); + } + + public InstanceConfig getMbeanMapping() { + return instanceConfig; + } + + public Collection getProvidedServices() { + return providedServices; + } + + public Element toXml(ObjectName instanceON, Services depTracker, Document document, String namespace) { + Element root = document.createElement(XmlNetconfConstants.MODULE_KEY); + // Xml.addNamespaceAttr(document, root, namespace); + + final String prefix = getPrefix(namespace); + Element typeElement = XmlUtil.createPrefixedTextElement(document, XmlNetconfConstants.TYPE_KEY, prefix, + moduleName); + XmlUtil.addPrefixedNamespaceAttr(typeElement, prefix, namespace); + // Xml.addNamespaceAttr(document, typeElement, + // XMLUtil.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + root.appendChild(typeElement); + + Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, + ObjectNameUtil.getInstanceName(instanceON)); + // Xml.addNamespaceAttr(document, nameElement, + // XMLUtil.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + root.appendChild(nameElement); + + root = instanceConfig.toXml(instanceON, depTracker, namespace, document, root); + + return root; + } + + private String getPrefix(String namespace) { + // if(namespace.contains(":")==false) + return "prefix"; + // return namespace.substring(namespace.lastIndexOf(':') + 1, + // namespace.length()); + + } + + public ModuleElementResolved fromXml(XmlElement moduleElement, Services depTracker, String instanceName, + String moduleNamespace) { + + InstanceConfigElementResolved ice = instanceConfig.fromXml(moduleElement, depTracker, moduleNamespace); + return new ModuleElementResolved(instanceName, ice); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleElementResolved.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleElementResolved.java new file mode 100644 index 0000000000..6d2936c827 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/ModuleElementResolved.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config; + +public class ModuleElementResolved { + + private final String instanceName; + private final InstanceConfigElementResolved instanceConfigElementResolved; + + public ModuleElementResolved(String instanceName, InstanceConfigElementResolved instanceConfigElementResolved) { + this.instanceName = instanceName; + this.instanceConfigElementResolved = instanceConfigElementResolved; + } + + public String getInstanceName() { + return instanceName; + } + + public InstanceConfigElementResolved getInstanceConfigElementResolved() { + return instanceConfigElementResolved; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java new file mode 100644 index 0000000000..7e4b6c2c06 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/config/Services.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config; + +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.annotation.Nullable; +import javax.management.ObjectName; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class Services { + + private static final String PROVIDER_KEY = "provider"; + private static final String NAME_KEY = "name"; + public static final String TYPE_KEY = "type"; + public static final String SERVICE_KEY = "service"; + + private long suffix = 1; + + private final Map instanceToRef = Maps.newHashMap(); + private final Map> serviceNameToRefNameToInstance = Maps + .newHashMap(); + + public String addServiceEntry(String serviceName, ObjectName on) { + + String moduleName = on.getKeyProperty("moduleFactoryName"); + String instanceName = on.getKeyProperty("instanceName"); + + return addServiceEntry(serviceName, moduleName, instanceName); + } + + public String addServiceEntry(String serviceName, String moduleName, String instanceName) { + ServiceInstance serviceInstance = new ServiceInstance(moduleName, instanceName); + serviceInstance.setServiceName(serviceName); + + String refName = instanceToRef.get(serviceInstance); + + Map refNameToInstance = serviceNameToRefNameToInstance.get(serviceName); + if (refNameToInstance == null) { + refNameToInstance = Maps.newHashMap(); + serviceNameToRefNameToInstance.put(serviceName, refNameToInstance); + } + + if (refName != null) { + if (serviceNameToRefNameToInstance.get(serviceName).containsKey(moduleName) == false) { + refNameToInstance.put(refName, serviceInstance); + } + return refName; + } else { + refName = "ref_" + instanceName; + + final Set refNamesAsSet = toSet(instanceToRef.values()); + if (refNamesAsSet.contains(refName)) { + refName = findAvailableRefName(refName, refNamesAsSet); + } + + instanceToRef.put(serviceInstance, refName); + refNameToInstance.put(refName, serviceInstance); + + return refName; + } + } + + private Set toSet(Collection values) { + Set refNamesAsSet = Sets.newHashSet(); + + for (String refName : values) { + boolean resultAdd = refNamesAsSet.add(refName); + Preconditions.checkState(resultAdd, + "Error occurred building services element, reference name {} was present twice", refName); + } + + return refNamesAsSet; + } + + public ServiceInstance getByServiceAndRefName(String serviceName, String refName) { + Map refNameToInstance = serviceNameToRefNameToInstance.get(serviceName); + Preconditions.checkArgument(refNameToInstance != null, "No serviceInstances mapped to " + serviceName + " , " + + serviceNameToRefNameToInstance.keySet()); + + ServiceInstance serviceInstance = refNameToInstance.get(refName); + Preconditions.checkArgument(serviceInstance != null, "No serviceInstance mapped to " + refName + + " under service name " + serviceName + " , " + refNameToInstance.keySet()); + return serviceInstance; + } + + // TODO hide getMappedServices, call it explicitly in toXml + + public Map> getMappedServices() { + Map> retVal = Maps.newHashMap(); + + for (String serviceName : serviceNameToRefNameToInstance.keySet()) { + + Map innerRetVal = Maps.transformValues(serviceNameToRefNameToInstance.get(serviceName), + new Function() { + @Nullable + @Override + public String apply(@Nullable ServiceInstance serviceInstance) { + return serviceInstance.toString(); + } + }); + retVal.put(serviceName, innerRetVal); + } + + return retVal; + } + + // TODO hide resolveServices, call it explicitly in fromXml + + public static Services resolveServices(Map> mappedServices) { + Services tracker = new Services(); + + for (Entry> serviceEntry : mappedServices.entrySet()) { + + String serviceName = serviceEntry.getKey(); + for (Entry refEntry : serviceEntry.getValue().entrySet()) { + + Map refNameToInstance = tracker.serviceNameToRefNameToInstance + .get(serviceName); + if (refNameToInstance == null) { + refNameToInstance = Maps.newHashMap(); + tracker.serviceNameToRefNameToInstance.put(serviceName, refNameToInstance); + } + + String refName = refEntry.getKey(); + Preconditions.checkState(false == refNameToInstance.containsKey(refName), + "Duplicate reference name to service " + refName + " under service " + serviceName); + ServiceInstance serviceInstance = ServiceInstance.fromString(refEntry.getValue()); + refNameToInstance.put(refName, serviceInstance); + + tracker.instanceToRef.put(serviceInstance, refEntry.getKey()); + } + } + return tracker; + } + + public static Map> fromXml(XmlElement xml) { + Map> retVal = Maps.newHashMap(); + + List services = xml.getChildElements(SERVICE_KEY); + xml.checkUnrecognisedElements(services); + + for (XmlElement service : services) { + + XmlElement typeElement = service.getOnlyChildElement(TYPE_KEY); + String serviceName = typeElement.getTextContent(); + + Map innerMap = Maps.newHashMap(); + retVal.put(serviceName, innerMap); + + List instances = service.getChildElements(XmlNetconfConstants.INSTANCE_KEY); + service.checkUnrecognisedElements(instances, typeElement); + + for (XmlElement instance : instances) { + XmlElement nameElement = instance.getOnlyChildElement(NAME_KEY); + String refName = nameElement.getTextContent(); + + XmlElement providerElement = instance.getOnlyChildElement(PROVIDER_KEY); + String providerName = providerElement.getTextContent(); + + instance.checkUnrecognisedElements(nameElement, providerElement); + + innerMap.put(refName, providerName); + } + } + + return retVal; + } + + private String findAvailableRefName(String refName, Set refNamesAsSet) { + String intitialRefName = refName; + + while (true) { + refName = intitialRefName + "_" + suffix++; + if (refNamesAsSet.contains(refName) == false) + return refName; + } + } + + public Element toXml(Map> mappedServices, Document document) { + Element root = document.createElement(XmlNetconfConstants.SERVICES_KEY); + XmlUtil.addNamespaceAttr(root, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + + for (Entry> serviceEntry : mappedServices.entrySet()) { + Element serviceElement = document.createElement(SERVICE_KEY); + root.appendChild(serviceElement); + + Element typeElement = XmlUtil.createTextElement(document, TYPE_KEY, serviceEntry.getKey()); + serviceElement.appendChild(typeElement); + + for (Entry instanceEntry : serviceEntry.getValue().entrySet()) { + Element instanceElement = document.createElement(XmlNetconfConstants.INSTANCE_KEY); + serviceElement.appendChild(instanceElement); + + Element nameElement = XmlUtil.createTextElement(document, NAME_KEY, instanceEntry.getKey()); + instanceElement.appendChild(nameElement); + + Element providerElement = XmlUtil.createTextElement(document, PROVIDER_KEY, instanceEntry.getValue()); + instanceElement.appendChild(providerElement); + } + } + + return root; + } + + public static final class ServiceInstance { + public ServiceInstance(String moduleName, String instanceName) { + this.moduleName = moduleName; + this.instanceName = instanceName; + } + + public static ServiceInstance fromString(String instanceId) { + instanceId = instanceId.trim(); + Matcher matcher = p.matcher(instanceId); + Preconditions.checkArgument(matcher.matches(), "Unexpected format for provider, expected " + p.toString() + + " but was " + instanceId); + String factoryName = matcher.group(1); + String instanceName = matcher.group(2); + return new ServiceInstance(factoryName, instanceName); + } + + private final String moduleName, instanceName; + private String serviceName; + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getModuleName() { + return moduleName; + } + + public String getInstanceName() { + return instanceName; + } + + private static final String blueprint = "/" + XmlNetconfConstants.CONFIG_KEY + "/" + + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "[" + + XmlNetconfConstants.NAME_KEY + "='%s']/" + XmlNetconfConstants.INSTANCE_KEY + "[" + + XmlNetconfConstants.NAME_KEY + "='%s']"; + + private static final String blueprintR = "/" + XmlNetconfConstants.CONFIG_KEY + "/" + + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\[" + + XmlNetconfConstants.NAME_KEY + "='%s'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\[" + + XmlNetconfConstants.NAME_KEY + "='%s'\\]"; + + private static final Pattern p = Pattern.compile(String.format(blueprintR, "(.+)", "(.+)")); + + @Override + public String toString() { + return String.format(blueprint, moduleName, instanceName); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((instanceName == null) ? 0 : instanceName.hashCode()); + result = prime * result + ((moduleName == null) ? 0 : moduleName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ServiceInstance other = (ServiceInstance) obj; + if (instanceName == null) { + if (other.instanceName != null) + return false; + } else if (!instanceName.equals(other.instanceName)) + return false; + if (moduleName == null) { + if (other.moduleName != null) + return false; + } else if (!moduleName.equals(other.moduleName)) + return false; + return true; + } + + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/InstanceRuntimeRpc.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/InstanceRuntimeRpc.java new file mode 100644 index 0000000000..e91357e47a --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/InstanceRuntimeRpc.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc; + +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeReadingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.ObjectXmlReader; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.AttributeResolvingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.resolving.ObjectResolver; +import org.opendaylight.controller.netconf.util.xml.XmlElement; + +import javax.management.openmbean.OpenType; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public final class InstanceRuntimeRpc { + + private final Map yangToAttrConfig; + private final Rpc rpc; + + public InstanceRuntimeRpc(Rpc rpc) { + this.yangToAttrConfig = map(rpc.getParameters()); + this.rpc = rpc; + } + + private Map map(List parameters) { + Map mapped = Maps.newHashMap(); + for (JavaAttribute javaAttribute : parameters) { + mapped.put(javaAttribute.getAttributeYangName(), javaAttribute); + } + return mapped; + } + + private void resolveConfiguration(Map mappedConfig) { + + // TODO make field, resolvingStrategies can be instantiated only once + Map>> resolvingStrategies = new ObjectResolver(null) + .prepareResolving(yangToAttrConfig); + // TODO make constructor for object resolver without service tracker + for (Entry configDefEntry : mappedConfig.entrySet()) { + try { + + AttributeResolvingStrategy> attributeResolvingStrategy = resolvingStrategies + .get(configDefEntry.getKey()); + + configDefEntry.getValue().resolveValue(attributeResolvingStrategy, configDefEntry.getKey()); + configDefEntry.getValue().setJmxName( + yangToAttrConfig.get(configDefEntry.getKey()).getUpperCaseCammelCase()); + } catch (Exception e) { + throw new IllegalStateException("Unable to resolve value " + configDefEntry.getValue() + + " to attribute " + configDefEntry.getKey(), e); + } + } + } + + public Map fromXml(XmlElement configRootNode) { + Map retVal = Maps.newHashMap(); + + Map strats = new ObjectXmlReader().prepareReading(yangToAttrConfig); + + for (Entry readStratEntry : strats.entrySet()) { + List configNodes = configRootNode.getChildElements(readStratEntry.getKey()); + AttributeConfigElement readElement = readStratEntry.getValue().readElement(configNodes); + retVal.put(readStratEntry.getKey(), readElement); + } + + resolveConfiguration(retVal); + return retVal; + } + + public String getName() { + return rpc.getName(); + } + + public String getReturnType() { + return rpc.getReturnType(); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/ModuleRpcs.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/ModuleRpcs.java new file mode 100644 index 0000000000..a081890d39 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/ModuleRpcs.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; +import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc; + +import java.util.Map; + +public final class ModuleRpcs { + + Map yangToJavaNames = Maps.newHashMap(); + Map> rpcMapping = Maps.newHashMap(); + + public void addNameMapping(RuntimeBeanEntry runtimeEntry) { + String yangName = runtimeEntry.getYangName(); + Preconditions.checkState(yangToJavaNames.containsKey(yangName) == false, + "RuntimeBean %s found twice in same namespace", yangName); + yangToJavaNames.put(yangName, runtimeEntry.getJavaNamePrefix()); + } + + public void addRpc(RuntimeBeanEntry runtimeEntry, Rpc rpc) { + String yangName = runtimeEntry.getYangName(); + Map map = rpcMapping.get(yangName); + if (map == null) { + map = Maps.newHashMap(); + rpcMapping.put(yangName, map); + } + + Preconditions.checkState(map.containsKey(rpc.getYangName()) == false, "Rpc %s for runtime bean %s added twice", + rpc.getYangName(), yangName); + map.put(rpc.getYangName(), new InstanceRuntimeRpc(rpc)); + } + + public String getRbeJavaName(String yangName) { + String javaName = yangToJavaNames.get(yangName); + Preconditions.checkState(javaName != null, + "No runtime bean entry found under yang name %s, available yang names %s", yangName, + yangToJavaNames.keySet()); + return javaName; + } + + public InstanceRuntimeRpc getRpc(String rbeName, String rpcName) { + Map rpcs = rpcMapping.get(rbeName); + Preconditions.checkState(rpcs != null, "No rpcs found for runtime bean %s", rbeName); + InstanceRuntimeRpc rpc = rpcs.get(rpcName); + Preconditions.checkState(rpc != null, "No rpc found for runtime bean %s with name %s", rbeName, rpcName); + return rpc; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/Rpcs.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/Rpcs.java new file mode 100644 index 0000000000..556541ab22 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/rpc/Rpcs.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc; + +import com.google.common.base.Preconditions; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpcElementResolved; + +import java.util.Map; + +public class Rpcs { + private final Map> mappedRpcs; + + public Rpcs(Map> mappedRpcs) { + super(); + this.mappedRpcs = mappedRpcs; + } + + public ModuleRpcs getRpcMapping(RuntimeRpcElementResolved id) { + Map modules = mappedRpcs.get(id.getNamespace()); + Preconditions.checkState(modules != null, "No modules found for namespace %s", id.getNamespace()); + ModuleRpcs rpcMapping = modules.get(id.getModuleName()); + Preconditions.checkState(modules != null, "No module %s found for namespace %s", id.getModuleName(), + id.getNamespace()); + + return rpcMapping; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/InstanceRuntime.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/InstanceRuntime.java new file mode 100644 index 0000000000..9d348d0985 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/InstanceRuntime.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Sets; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import java.util.Hashtable; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class InstanceRuntime { + + /** + * + */ + private static final String KEY_ATTRIBUTE_KEY = "key"; + + private final InstanceConfig instanceMapping; + private final Map childrenMappings; + private final Map jmxToYangChildRbeMapping; + + public InstanceRuntime(InstanceConfig instanceMapping, Map childrenMappings, + Map jmxToYangChildRbeMapping) { + this.instanceMapping = instanceMapping; + this.childrenMappings = childrenMappings; + this.jmxToYangChildRbeMapping = jmxToYangChildRbeMapping; + } + + /** + * Finds all children runtime beans, same properties and values as current + * root + any number of additional properties + */ + private Set findChildren(ObjectName innerRootBean, Set childRbeOns) { + final Hashtable wantedProperties = innerRootBean.getKeyPropertyList(); + + return Sets.newHashSet(Collections2.filter(childRbeOns, new Predicate() { + + @Override + public boolean apply(ObjectName on) { + Hashtable localProperties = on.getKeyPropertyList(); + for (Entry propertyEntry : wantedProperties.entrySet()) { + if (!localProperties.containsKey(propertyEntry.getKey())) + return false; + if (!localProperties.get(propertyEntry.getKey()).equals(propertyEntry.getValue())) + return false; + if (localProperties.size() <= wantedProperties.size()) + return false; + } + return true; + } + })); + } + + /** + * Finds next level root runtime beans, beans that have the same properties + * as current root + one additional + */ + private Set getRootBeans(Set childRbeOns, final String string, final int keyListSize) { + return Sets.newHashSet(Collections2.filter(childRbeOns, new Predicate() { + + @Override + public boolean apply(ObjectName on) { + if (on.getKeyPropertyList().size() != keyListSize + 1) + return false; + if (!on.getKeyPropertyList().containsKey(string)) + return false; + return true; + } + })); + } + + public Element toXml(ObjectName rootOn, Set childRbeOns, Document document) { + return toXml(rootOn, childRbeOns, document, null, null); + } + + public Element toXml(ObjectName rootOn, Set childRbeOns, Document document, String instanceIndex, + String keyName) { + Element xml = document.createElement(keyName == null ? XmlNetconfConstants.DATA_KEY : keyName); + // TODO namespace + xml = instanceMapping.toXml(rootOn, null, "namespace", document, xml); + + if (instanceIndex != null) { + xml.setAttribute(KEY_ATTRIBUTE_KEY, instanceIndex); + } + + for (Entry childMappingEntry : childrenMappings.entrySet()) { + Set innerRootBeans = getRootBeans(childRbeOns, childMappingEntry.getKey(), rootOn + .getKeyPropertyList().size()); + + for (ObjectName objectName : innerRootBeans) { + Set innerChildRbeOns = findChildren(objectName, childRbeOns); + String runtimeInstanceIndex = objectName.getKeyProperty(childMappingEntry.getKey()); + + String elementName = jmxToYangChildRbeMapping.get(childMappingEntry.getKey()); + xml.appendChild(childMappingEntry.getValue().toXml(objectName, innerChildRbeOns, document, + runtimeInstanceIndex, elementName)); + } + } + + return xml; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/ModuleRuntime.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/ModuleRuntime.java new file mode 100644 index 0000000000..07da65ed19 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/ModuleRuntime.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime; + +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import java.util.Collection; +import java.util.Set; + +public class ModuleRuntime { + + private final String moduleName; + private final InstanceRuntime instanceRuntime; + + public ModuleRuntime(String moduleName, InstanceRuntime instanceRuntime) { + this.moduleName = moduleName; + this.instanceRuntime = instanceRuntime; + } + + public InstanceRuntime getMbeanMapping() { + return instanceRuntime; + } + + private ObjectName findRoot(Collection runtimeBeanOns) { + for (ObjectName objectName : runtimeBeanOns) { + if (objectName.getKeyPropertyList().size() == 3) + return objectName; + } + throw new IllegalStateException("Root runtime bean not found among " + runtimeBeanOns); + } + + public Element toXml(String namespace, Multimap instances, Document document) { + Element root = document.createElement(XmlNetconfConstants.MODULE_KEY); + XmlUtil.addNamespaceAttr(root, namespace); + + Element nameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, moduleName); + root.appendChild(nameElement); + + for (String instanceName : instances.keySet()) { + Element instance = document.createElement(XmlNetconfConstants.INSTANCE_KEY); + + Element innerNameElement = XmlUtil.createTextElement(document, XmlNetconfConstants.NAME_KEY, instanceName); + instance.appendChild(innerNameElement); + + Collection runtimeBeanOns = instances.get(instanceName); + ObjectName rootName = findRoot(runtimeBeanOns); + + Set childrenRuntimeBeans = Sets.newHashSet(runtimeBeanOns); + childrenRuntimeBeans.remove(rootName); + + instance.appendChild(instanceRuntime.toXml(rootName, childrenRuntimeBeans, document)); + + root.appendChild(instance); + } + + return root; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/Runtime.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/Runtime.java new file mode 100644 index 0000000000..da281269e6 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/runtime/Runtime.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import java.util.Map; +import java.util.Set; + +public class Runtime { + + private final Map> moduleRuntimes; + + public Runtime(Map> moduleRuntimes) { + this.moduleRuntimes = moduleRuntimes; + } + + private Map> mapInstancesToModules(Set instancesToMap) { + Map> retVal = Maps.newHashMap(); + + for (ObjectName objectName : instancesToMap) { + String moduleName = ObjectNameUtil.getFactoryName(objectName); + + Multimap multimap = retVal.get(moduleName); + if (multimap == null) { + multimap = HashMultimap.create(); + retVal.put(moduleName, multimap); + } + + String instanceName = ObjectNameUtil.getInstanceName(objectName); + + multimap.put(instanceName, objectName); + } + + return retVal; + } + + public Element toXml(Set instancesToMap, Document document) { + Element root = document.createElement(XmlNetconfConstants.DATA_KEY); + + Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY); + XmlUtil.addNamespaceAttr(modulesElement, + XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); + root.appendChild(modulesElement); + + Map> moduleToInstances = mapInstancesToModules(instancesToMap); + + for (String localNamespace : moduleRuntimes.keySet()) { + for (String moduleName : moduleRuntimes.get(localNamespace).keySet()) { + Multimap instanceToRbe = moduleToInstances.get(moduleName); + + if (instanceToRbe == null) + continue; + + ModuleRuntime moduleRuntime = moduleRuntimes.get(localNamespace).get(moduleName); + Element innerXml = moduleRuntime.toXml(localNamespace, instanceToRbe, document); + modulesElement.appendChild(innerXml); + } + } + + return root; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/AbstractConfigNetconfOperation.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/AbstractConfigNetconfOperation.java new file mode 100644 index 0000000000..6689759ffb --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/AbstractConfigNetconfOperation.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations; + +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; +import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public abstract class AbstractConfigNetconfOperation extends AbstractNetconfOperation { + + protected final ConfigRegistryClient configRegistryClient; + + protected AbstractConfigNetconfOperation(ConfigRegistryClient configRegistryClient, + String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + this.configRegistryClient = configRegistryClient; + } + + @Override + protected HandlingPriority canHandle(String operationName, String operationNamespace) { + // TODO check namespace + return operationName.equals(getOperationName()) ? HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY + : HandlingPriority.CANNOT_HANDLE; + } + + protected abstract String getOperationName(); + + @Override + protected Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter) + throws NetconfDocumentedException { + return handle(document, operationElement); + } + + protected abstract Element handle(Document document, XmlElement operationElement) throws NetconfDocumentedException; +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Commit.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Commit.java new file mode 100644 index 0000000000..8820d58a98 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Commit.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations; + +import java.util.HashMap; +import java.util.Map; + +import org.opendaylight.controller.config.api.jmx.CommitStatus; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class Commit extends AbstractConfigNetconfOperation { + + private static final Logger logger = LoggerFactory.getLogger(Commit.class); + + private final TransactionProvider transactionProvider; + + public Commit(TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient, + String netconfSessionIdForReporting) { + super(configRegistryClient, netconfSessionIdForReporting); + this.transactionProvider = transactionProvider; + } + + private static void checkXml(XmlElement xml) { + xml.checkName(XmlNetconfConstants.COMMIT); + xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + } + + @Override + protected String getOperationName() { + return XmlNetconfConstants.COMMIT; + } + + @Override + protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException { + checkXml(xml); + + CommitStatus status; + try { + status = this.transactionProvider.commitTransaction(); + } catch (final IllegalStateException e) { + logger.warn("Commit failed: ", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.operation_failed.name(), + "Operation failed. Use 'get-config' or 'edit-config' before triggering 'commit' operation"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed, + ErrorSeverity.error, errorInfo); + } catch (final NetconfDocumentedException e) { + throw new NetconfDocumentedException( + "Unable to retrieve config snapshot after commit for persister, details: " + e.getMessage(), + ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error, e.getErrorInfo()); + } + logger.info("Datastore {} committed successfully: {}", Datastore.candidate, status); + + return document.createElement(XmlNetconfConstants.OK); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java new file mode 100644 index 0000000000..d736595719 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Datastore.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations; + +import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.CandidateDatastoreQueryStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.DatastoreQueryStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.RunningDatastoreQueryStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; + +public enum Datastore { + + running, candidate; + + /** + * @param source + * @param transactionProvider + * @return + */ + public static DatastoreQueryStrategy getInstanceQueryStrategy(Datastore source, + TransactionProvider transactionProvider) { + switch (source) { + case running: + return new RunningDatastoreQueryStrategy(); + case candidate: + return new CandidateDatastoreQueryStrategy(transactionProvider); + default: + throw new UnsupportedOperationException("Unimplemented datastore query strategy for " + source); + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java new file mode 100644 index 0000000000..b8fa5dd86e --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/DiscardChanges.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations; + +import java.util.HashMap; +import java.util.Map; + +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class DiscardChanges extends AbstractConfigNetconfOperation { + + public static final String DISCARD = "discard-changes"; + + private static final Logger logger = LoggerFactory.getLogger(DiscardChanges.class); + + private final TransactionProvider transactionProvider; + + public DiscardChanges(final TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient, + String netconfSessionIdForReporting) { + super(configRegistryClient, netconfSessionIdForReporting); + this.transactionProvider = transactionProvider; + } + + private static void fromXml(XmlElement xml) { + xml.checkName(DISCARD); + xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + } + + @Override + protected String getOperationName() { + return DISCARD; + } + + @Override + protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException { + try { + fromXml(xml); + } catch (final IllegalArgumentException e) { + logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage()); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute, + ErrorSeverity.error, errorInfo); + } + + try { + this.transactionProvider.abortTransaction(); + } catch (final IllegalStateException e) { + logger.warn("Abort failed: ", e); + final Map errorInfo = new HashMap<>(); + errorInfo + .put(ErrorTag.operation_failed.name(), + "Operation failed. Use 'get-config' or 'edit-config' before triggering 'discard-changes' operation"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed, + ErrorSeverity.error, errorInfo); + } + logger.info("Changes discarded successfully from datastore {}", Datastore.candidate); + + return document.createElement(XmlNetconfConstants.OK); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Validate.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Validate.java new file mode 100644 index 0000000000..b8cae43643 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/Validate.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations; + +import com.google.common.base.Preconditions; +import org.opendaylight.controller.config.api.ValidationException; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.HashMap; +import java.util.Map; + +public class Validate extends AbstractConfigNetconfOperation { + + public static final String VALIDATE = "validate"; + + private static final Logger logger = LoggerFactory.getLogger(Validate.class); + + private final TransactionProvider transactionProvider; + + public Validate(final TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient, + String netconfSessionIdForReporting) { + super(configRegistryClient, netconfSessionIdForReporting); + this.transactionProvider = transactionProvider; + } + + private void checkXml(XmlElement xml) { + xml.checkName(VALIDATE); + xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + + XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY, + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + XmlElement sourceChildNode = sourceElement.getOnlyChildElement(); + + sourceChildNode.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + String datastoreValue = sourceChildNode.getName(); + Datastore sourceDatastore = Datastore.valueOf(datastoreValue); + + Preconditions.checkState(sourceDatastore == Datastore.candidate, "Only " + Datastore.candidate + + " is supported as source for " + VALIDATE + " but was " + datastoreValue); + } + + @Override + protected String getOperationName() { + return VALIDATE; + } + + @Override + protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException { + try { + checkXml(xml); + } catch (IllegalStateException e) { + logger.warn("Rpc error: {}", ErrorTag.missing_attribute, e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.missing_attribute.name(), "Missing value of datastore attribute"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.missing_attribute, + ErrorSeverity.error, errorInfo); + } catch (final IllegalArgumentException e) { + logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage()); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute, + ErrorSeverity.error, errorInfo); + } + + try { + transactionProvider.validateTransaction(); + } catch (ValidationException e) { + logger.warn("Validation failed", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.operation_failed.name(), "Validation failed"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed, + ErrorSeverity.error, errorInfo); + } catch (IllegalStateException e) { + logger.warn("Validation failed", e); + final Map errorInfo = new HashMap<>(); + errorInfo + .put(ErrorTag.operation_failed.name(), + "Datastore is not present. Use 'get-config' or 'edit-config' before triggering 'operations' operation"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed, + ErrorSeverity.error, errorInfo); + + } + + logger.info("Datastore {} validated successfully", Datastore.candidate); + + return document.createElement(XmlNetconfConstants.OK); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/AbstractEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/AbstractEditConfigStrategy.java new file mode 100644 index 0000000000..d8ea7d7af7 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/AbstractEditConfigStrategy.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import java.util.Map; + +import javax.management.InstanceNotFoundException; +import javax.management.ObjectName; + +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class AbstractEditConfigStrategy implements EditConfigStrategy { + + private static final Logger logger = LoggerFactory.getLogger(AbstractEditConfigStrategy.class); + + @Override + public void executeConfiguration(String module, String instance, Map configuration, + ConfigTransactionClient ta) { + + try { + ObjectName on = ta.lookupConfigBean(module, instance); + logger.debug("ServiceInstance for {} {} located successfully under {}", module, instance, on); + executeStrategy(configuration, ta, on); + } catch (InstanceNotFoundException e) { + handleMissingInstance(configuration, ta, module, instance); + } + + } + + abstract void handleMissingInstance(Map configuration, ConfigTransactionClient ta, + String module, String instance); + + abstract void executeStrategy(Map configuration, ConfigTransactionClient ta, + ObjectName objectName); + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/DeleteEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/DeleteEditConfigStrategy.java new file mode 100644 index 0000000000..ffe107f8ce --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/DeleteEditConfigStrategy.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import java.util.Map; + +import javax.management.InstanceNotFoundException; +import javax.management.ObjectName; + +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DeleteEditConfigStrategy extends AbstractEditConfigStrategy { + + private static final Logger logger = LoggerFactory.getLogger(DeleteEditConfigStrategy.class); + + @Override + void handleMissingInstance(Map configuration, ConfigTransactionClient ta, + String module, String instance) { + throw new IllegalStateException("Unable to delete " + module + ":" + instance + " , ServiceInstance not found"); + } + + @Override + void executeStrategy(Map configuration, ConfigTransactionClient ta, ObjectName on) { + try { + ta.destroyModule(on); + logger.debug("ServiceInstance {} deleted successfully", on); + } catch (InstanceNotFoundException e) { + throw new IllegalStateException("Unable to delete " + on, e); + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java new file mode 100644 index 0000000000..de21f310e2 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfig.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import org.opendaylight.controller.config.api.JmxAttributeValidationException; +import org.opendaylight.controller.config.api.ValidationException; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.*; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +public class EditConfig extends AbstractConfigNetconfOperation { + + private static final Logger logger = LoggerFactory.getLogger(EditConfig.class); + + private final YangStoreSnapshot yangStoreSnapshot; + + private final TransactionProvider transactionProvider; + private EditConfigXmlParser editConfigXmlParser; + + public EditConfig(YangStoreSnapshot yangStoreSnapshot, TransactionProvider transactionProvider, + ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) { + super(configRegistryClient, netconfSessionIdForReporting); + this.yangStoreSnapshot = yangStoreSnapshot; + this.transactionProvider = transactionProvider; + this.editConfigXmlParser = new EditConfigXmlParser(); + } + + @VisibleForTesting + Element getResponseInternal(final Document document, + final EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException { + if (editConfigExecution.shouldTest()) { + executeTests(configRegistryClient, editConfigExecution); + } + + if (editConfigExecution.shouldSet()) { + executeSet(configRegistryClient, editConfigExecution); + } + + logger.info("Operation {} successful", EditConfigXmlParser.EDIT_CONFIG); + + return document.createElement(XmlNetconfConstants.OK); + } + + private void executeSet(ConfigRegistryClient configRegistryClient, + EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException { + try { + set(configRegistryClient, editConfigExecution); + } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) { + logger.warn("Set phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage()); + throw new NetconfDocumentedException("Test phase: " + e.getMessage(), e, ErrorType.application, + ErrorTag.operation_failed, ErrorSeverity.error, errorInfo); + } + logger.debug("Set phase for {} operation successful", EditConfigXmlParser.EDIT_CONFIG); + } + + private void executeTests(ConfigRegistryClient configRegistryClient, + EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException { + try { + test(configRegistryClient, editConfigExecution.resolvedXmlElements); + } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) { + logger.warn("Test phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage()); + throw new NetconfDocumentedException("Test phase: " + e.getMessage(), e, ErrorType.application, + ErrorTag.operation_failed, ErrorSeverity.error, errorInfo); + } + logger.debug("Test phase for {} operation successful", EditConfigXmlParser.EDIT_CONFIG); + } + + private void test(ConfigRegistryClient configRegistryClient, + Map> resolvedModules) { + ObjectName taON = transactionProvider.getTestTransaction(); + try { + + // default strategy = replace wipes config + if (EditStrategyType.defaultStrategy() == EditStrategyType.replace) { + transactionProvider.wipeTestTransaction(taON); + } + setOnTransaction(configRegistryClient, resolvedModules, taON); + transactionProvider.validateTestTransaction(taON); + } finally { + transactionProvider.abortTestTransaction(taON); + } + } + + private void set(ConfigRegistryClient configRegistryClient, + EditConfigXmlParser.EditConfigExecution editConfigExecution) { + ObjectName taON = transactionProvider.getOrCreateTransaction(); + + // default strategy = replace wipes config + if (EditStrategyType.defaultStrategy() == EditStrategyType.replace) { + transactionProvider.wipeTransaction(); + } + setOnTransaction(configRegistryClient, editConfigExecution.resolvedXmlElements, taON); + } + + private void setOnTransaction(ConfigRegistryClient configRegistryClient, + Map> resolvedXmlElements, ObjectName taON) { + ConfigTransactionClient ta = configRegistryClient.getConfigTransactionClient(taON); + + for (Multimap modulesToResolved : resolvedXmlElements.values()) { + for (Entry moduleToResolved : modulesToResolved.entries()) { + String moduleName = moduleToResolved.getKey(); + + ModuleElementResolved moduleElementResolved = moduleToResolved.getValue(); + String instanceName = moduleElementResolved.getInstanceName(); + + InstanceConfigElementResolved ice = moduleElementResolved.getInstanceConfigElementResolved(); + EditConfigStrategy strategy = ice.getEditStrategy(); + strategy.executeConfiguration(moduleName, instanceName, ice.getConfiguration(), ta); + } + } + } + + public static Config getConfigMapping(ConfigRegistryClient configRegistryClient, + Map> mBeanEntries) { + Map> factories = transform(configRegistryClient, mBeanEntries); + return new Config(factories); + } + + // TODO refactor + private static Map> transform(final ConfigRegistryClient configRegistryClient, + Map> mBeanEntries) { + return Maps.transformEntries(mBeanEntries, + new Maps.EntryTransformer, Map>() { + + @Override + public Map transformEntry(String arg0, Map arg1) { + return Maps.transformEntries(arg1, + new Maps.EntryTransformer() { + + @Override + public ModuleConfig transformEntry(String key, ModuleMXBeanEntry value) { + return new ModuleConfig(key, new InstanceConfig(configRegistryClient, value + .getAttributes())); + } + }); + } + }); + } + + @Override + protected String getOperationName() { + return EditConfigXmlParser.EDIT_CONFIG; + } + + @Override + protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException { + + EditConfigXmlParser.EditConfigExecution editConfigExecution; + Config cfg = getConfigMapping(configRegistryClient, yangStoreSnapshot.getModuleMXBeanEntryMap()); + try { + editConfigExecution = editConfigXmlParser.fromXml(xml, cfg); + } catch (IllegalStateException e) { + logger.warn("Error parsing xml", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.missing_attribute.name(), "Missing value for 'target' attribute"); + throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.missing_attribute, + ErrorSeverity.error, errorInfo); + } catch (final IllegalArgumentException e) { + logger.warn("Error parsing xml", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage()); + throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.bad_attribute, + ErrorSeverity.error, errorInfo); + } catch (final UnsupportedOperationException e) { + logger.warn("Unsupported", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for 'edit-config'"); + throw new NetconfDocumentedException(e.getMessage(), ErrorType.application, + ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo); + } + + return getResponseInternal(document, editConfigExecution); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigStrategy.java new file mode 100644 index 0000000000..6b7b622d56 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigStrategy.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; + +import java.util.Map; + +public interface EditConfigStrategy { + + void executeConfiguration(String module, String instance, Map configuration, + ConfigTransactionClient ta); + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigXmlParser.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigXmlParser.java new file mode 100644 index 0000000000..d835dfd30f --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigXmlParser.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Multimap; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Arrays; +import java.util.Map; + +public class EditConfigXmlParser { + + private static final Logger logger = LoggerFactory.getLogger(EditConfigXmlParser.class); + + public static final String EDIT_CONFIG = "edit-config"; + public static final String DEFAULT_OPERATION_KEY = "default-operation"; + static final String ERROR_OPTION_KEY = "error-option"; + static final String DEFAULT_ERROR_OPTION = "stop-on-error"; + static final String TARGET_KEY = "target"; + static final String TEST_OPTION_KEY = "test-option"; + + public EditConfigXmlParser() { + } + + EditConfigXmlParser.EditConfigExecution fromXml(final XmlElement xml, final Config cfgMapping) + throws NetconfDocumentedException { + + EditStrategyType.resetDefaultStrategy(); + + xml.checkName(EditConfigXmlParser.EDIT_CONFIG); + xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + + XmlElement targetElement = xml.getOnlyChildElementWithSameNamespace(EditConfigXmlParser.TARGET_KEY); + XmlElement targetChildNode = targetElement.getOnlyChildElementWithSameNamespace(); + String datastoreValue = targetChildNode.getName(); + Datastore targetDatastore = Datastore.valueOf(datastoreValue); + logger.debug("Setting {} to '{}'", EditConfigXmlParser.TARGET_KEY, targetDatastore); + + // check target + Preconditions.checkArgument(targetDatastore == Datastore.candidate, + "Only %s datastore supported for edit config but was: %s", Datastore.candidate, targetDatastore); + + // Test option + TestOption testOption; + Optional testOptionElementOpt = xml + .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.TEST_OPTION_KEY); + if (testOptionElementOpt.isPresent()) { + String testOptionValue = testOptionElementOpt.get().getTextContent(); + testOption = EditConfigXmlParser.TestOption.getFromXmlName(testOptionValue); + } else { + testOption = EditConfigXmlParser.TestOption.getDefault(); + } + logger.debug("Setting {} to '{}'", EditConfigXmlParser.TEST_OPTION_KEY, testOption); + + // Error option + Optional errorOptionElement = xml + .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.ERROR_OPTION_KEY); + if (errorOptionElement.isPresent()) { + String errorOptionParsed = errorOptionElement.get().getTextContent(); + if (false == errorOptionParsed.equals(EditConfigXmlParser.DEFAULT_ERROR_OPTION)) + throw new UnsupportedOperationException("Only " + EditConfigXmlParser.DEFAULT_ERROR_OPTION + + " supported for " + EditConfigXmlParser.ERROR_OPTION_KEY + ", was " + errorOptionParsed); + } + + // Default op + Optional defaultContent = xml + .getOnlyChildElementWithSameNamespaceOptionally(EditConfigXmlParser.DEFAULT_OPERATION_KEY); + if (defaultContent.isPresent()) + EditStrategyType.setDefaultStrategy(EditStrategyType.valueOf(defaultContent.get().getTextContent())); + + XmlElement configElement = xml.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CONFIG_KEY); + + return new EditConfigXmlParser.EditConfigExecution(xml, cfgMapping, configElement, testOption); + } + + private void removeMountpointsFromConfig(XmlElement configElement, XmlElement mountpointsElement) { + configElement.getDomElement().removeChild(mountpointsElement.getDomElement()); + } + + @VisibleForTesting + static enum TestOption { + testOnly, set, testThenSet; + + static TestOption getFromXmlName(String testOptionXmlName) { + switch (testOptionXmlName) { + case "test-only": + return testOnly; + case "test-then-set": + return testThenSet; + case "set": + return set; + default: + throw new IllegalArgumentException("Unsupported test option " + testOptionXmlName + " supported: " + + Arrays.toString(TestOption.values())); + } + } + + public static TestOption getDefault() { + return testThenSet; + } + + } + + @VisibleForTesting + static class EditConfigExecution { + XmlElement editConfigXml; + Map> resolvedXmlElements; + TestOption testOption; + + EditConfigExecution(XmlElement xml, Config configResolver, XmlElement configElement, TestOption testOption) { + this.editConfigXml = xml; + this.resolvedXmlElements = configResolver.fromXml(configElement); + this.testOption = testOption; + } + + boolean shouldTest() { + return testOption == TestOption.testOnly || testOption == TestOption.testThenSet; + } + + boolean shouldSet() { + return testOption == TestOption.set || testOption == TestOption.testThenSet; + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditStrategyType.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditStrategyType.java new file mode 100644 index 0000000000..a7a0518cc5 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditStrategyType.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import java.util.EnumSet; +import java.util.Set; + +import com.google.common.base.Preconditions; + +public enum EditStrategyType { + // can be default + merge, replace, none, + // additional per element + delete, remove; + + private static final Set defaultStrats = EnumSet.of(merge, replace, none); + + private static EditStrategyType defaultStrat = merge; + + public static EditStrategyType defaultStrategy() { + return defaultStrat; + } + + public static void setDefaultStrategy(EditStrategyType strat) { + Preconditions.checkArgument(strat.canBeDefault(), "Default edit strategy can be only of value " + defaultStrats + + ", but was " + strat); + defaultStrat = strat; + } + + public static void resetDefaultStrategy() { + setDefaultStrategy(EditStrategyType.merge); + } + + public boolean isEnforcing() { + switch (this) { + case merge: + case none: + case remove: + case delete: + return false; + case replace: + return true; + + default: + throw new IllegalStateException("Default edit strategy can be only of value " + defaultStrats + " but was " + + this); + } + } + + private static final EnumSet defaults; + + static { + defaults = EnumSet.of(merge, replace, none); + } + + private boolean canBeDefault() { + return defaults.contains(this); + } + + public EditConfigStrategy getFittingStrategy() { + switch (this) { + case merge: + return new MergeEditConfigStrategy(); + case replace: + return new ReplaceEditConfigStrategy(); + case delete: + return new DeleteEditConfigStrategy(); + case remove: + return new RemoveEditConfigStrategy(); + case none: + return new NoneEditConfigStrategy(); + default: + throw new UnsupportedOperationException("Unimplemented edit config strategy" + this); + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/MergeEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/MergeEditConfigStrategy.java new file mode 100644 index 0000000000..2a4a784a8a --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/MergeEditConfigStrategy.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import java.util.Map; +import java.util.Map.Entry; + +import javax.management.Attribute; +import javax.management.InstanceAlreadyExistsException; +import javax.management.ObjectName; + +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MergeEditConfigStrategy extends AbstractEditConfigStrategy { + + private static final Logger logger = LoggerFactory.getLogger(MergeEditConfigStrategy.class); + + @Override + void handleMissingInstance(Map configuration, ConfigTransactionClient ta, + String module, String instance) { + ObjectName on; + try { + on = ta.createModule(module, instance); + logger.info("New instance for {} {} created under name {}", module, instance, on); + executeStrategy(configuration, ta, on); + } catch (InstanceAlreadyExistsException e1) { + throw new IllegalStateException("Unable to create instance for " + module + " : " + instance); + } + } + + @Override + void executeStrategy(Map configuration, ConfigTransactionClient ta, ObjectName on) { + for (Entry configAttributeEntry : configuration.entrySet()) { + try { + AttributeConfigElement ace = configAttributeEntry.getValue(); + + if (!ace.getResolvedValue().isPresent()) { + logger.debug("Skipping attribute {} for {}", configAttributeEntry.getKey(), on); + continue; + } + + Object value = ace.getResolvedValue().get(); + ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value)); + logger.debug("Attribute {} set to {} for {}", configAttributeEntry.getKey(), value, on); + } catch (Exception e) { + throw new IllegalStateException("Unable to set attributes for " + on + ", Error with attribute " + + configAttributeEntry.getKey() + ":" + configAttributeEntry.getValue(), e); + } + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/NoneEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/NoneEditConfigStrategy.java new file mode 100644 index 0000000000..db11ce381e --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/NoneEditConfigStrategy.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import java.util.Map; + +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NoneEditConfigStrategy implements EditConfigStrategy { + + private static final Logger logger = LoggerFactory.getLogger(NoneEditConfigStrategy.class); + + @Override + public void executeConfiguration(String module, String instance, Map configuration, + ConfigTransactionClient ta) { + logger.debug("Skipping configuration element for {}:{}", module, instance); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/RemoveEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/RemoveEditConfigStrategy.java new file mode 100644 index 0000000000..76ca09433a --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/RemoveEditConfigStrategy.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +public class RemoveEditConfigStrategy extends DeleteEditConfigStrategy { + + private static final Logger logger = LoggerFactory.getLogger(RemoveEditConfigStrategy.class); + + @Override + void handleMissingInstance(Map configuration, ConfigTransactionClient ta, + String module, String instance) { + logger.warn("Unable to delete {}:{}, ServiceInstance not found", module, instance); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategy.java new file mode 100644 index 0000000000..0091d6cc84 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategy.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import java.util.Map; +import java.util.Map.Entry; + +import javax.management.Attribute; +import javax.management.InstanceAlreadyExistsException; +import javax.management.ObjectName; + +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ReplaceEditConfigStrategy extends AbstractEditConfigStrategy { + + private static final Logger logger = LoggerFactory.getLogger(ReplaceEditConfigStrategy.class); + + @Override + void handleMissingInstance(Map configuration, ConfigTransactionClient ta, + String module, String instance) { + try { + ObjectName on = ta.createModule(module, instance); + logger.debug("New instance for {} {} created under name {}", module, instance, on); + executeStrategy(configuration, ta, on); + } catch (InstanceAlreadyExistsException e) { + logger.warn("Error creating instance {}:{}, replace failed", module, instance, e); + throw new IllegalStateException("Unable to create new instance for " + module + " : " + instance, e); + } + } + + @Override + void executeStrategy(Map configuration, ConfigTransactionClient ta, ObjectName on) { + for (Entry configAttributeEntry : configuration.entrySet()) { + try { + AttributeConfigElement ace = configAttributeEntry.getValue(); + + if (!ace.getResolvedValue().isPresent()) { + Object value = ace.getResolvedDefaultValue(); + ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value)); + logger.debug("Attribute {} set to default value {} for {}", configAttributeEntry.getKey(), value, + on); + } else { + Object value = ace.getResolvedValue().get(); + ta.setAttribute(on, ace.getJmxName(), new Attribute(ace.getJmxName(), value)); + logger.debug("Attribute {} set to value {} for {}", configAttributeEntry.getKey(), value, on); + } + + } catch (Exception e) { + throw new IllegalStateException("Unable to set attributes for " + on + ", Error with attribute " + + configAttributeEntry.getKey() + ":" + configAttributeEntry.getValue(), e); + } + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java new file mode 100644 index 0000000000..b93843d28e --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/get/Get.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.get; + +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; +import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.InstanceRuntime; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.ModuleRuntime; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.runtime.Runtime; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class Get extends AbstractConfigNetconfOperation { + + public static final String GET = "get"; + + private final YangStoreSnapshot yangStoreSnapshot; + private static final Logger logger = LoggerFactory.getLogger(Get.class); + + public Get(YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient, + String netconfSessionIdForReporting) { + super(configRegistryClient, netconfSessionIdForReporting); + this.yangStoreSnapshot = yangStoreSnapshot; + } + + private Map> createModuleRuntimes(ConfigRegistryClient configRegistryClient, + Map> mBeanEntries) { + Map> retVal = Maps.newHashMap(); + + for (String namespace : mBeanEntries.keySet()) { + + Map innerMap = Maps.newHashMap(); + Map entriesFromNamespace = mBeanEntries.get(namespace); + for (String module : entriesFromNamespace.keySet()) { + + ModuleMXBeanEntry mbe = entriesFromNamespace.get(module); + + Map cache = Maps.newHashMap(); + RuntimeBeanEntry root = null; + for (RuntimeBeanEntry rbe : mbe.getRuntimeBeans()) { + cache.put(rbe, new InstanceConfig(configRegistryClient, rbe.getYangPropertiesToTypesMap())); + if (rbe.isRoot()) + root = rbe; + } + + if (root == null) + continue; + + InstanceRuntime rootInstanceRuntime = createInstanceRuntime(root, cache); + ModuleRuntime moduleRuntime = new ModuleRuntime(module, rootInstanceRuntime); + innerMap.put(module, moduleRuntime); + } + + retVal.put(namespace, innerMap); + } + return retVal; + } + + private InstanceRuntime createInstanceRuntime(RuntimeBeanEntry root, Map cache) { + Map children = Maps.newHashMap(); + for (RuntimeBeanEntry child : root.getChildren()) { + children.put(child.getJavaNamePrefix(), createInstanceRuntime(child, cache)); + } + + return new InstanceRuntime(cache.get(root), children, createJmxToYangMap(root.getChildren())); + } + + private Map createJmxToYangMap(List children) { + Map jmxToYangNamesForChildRbe = Maps.newHashMap(); + for (RuntimeBeanEntry rbe : children) { + jmxToYangNamesForChildRbe.put(rbe.getJavaNamePrefix(), rbe.getYangName()); + } + return jmxToYangNamesForChildRbe; + } + + private static void checkXml(XmlElement xml) { + xml.checkName(GET); + xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + + // Filter option - unsupported + if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0) + throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for " + GET); + } + + @Override + protected String getOperationName() { + return GET; + } + + @Override + protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException { + try { + checkXml(xml); + } catch (final IllegalArgumentException e) { + logger.warn("Error parsing xml", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage()); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute, + ErrorSeverity.error, errorInfo); + } catch (final UnsupportedOperationException e) { + logger.warn("Unsupported", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for 'get'"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, + ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo); + } + final Set runtimeBeans = configRegistryClient.lookupRuntimeBeans(); + final Map> moduleMappings = createModuleRuntimes(configRegistryClient, + yangStoreSnapshot.getModuleMXBeanEntryMap()); + final Runtime runtime = new Runtime(moduleMappings); + + final Element element = runtime.toXml(runtimeBeans, document); + + logger.info("{} operation successful", GET); + + return element; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/CandidateDatastoreQueryStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/CandidateDatastoreQueryStrategy.java new file mode 100644 index 0000000000..8e0a9d7cde --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/CandidateDatastoreQueryStrategy.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig; + +import java.util.Set; + +import javax.management.ObjectName; + +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; + +public class CandidateDatastoreQueryStrategy implements DatastoreQueryStrategy { + + private final TransactionProvider transactionProvider; + + public CandidateDatastoreQueryStrategy(TransactionProvider transactionProvider) { + this.transactionProvider = transactionProvider; + } + + @Override + public Set queryInstances(ConfigRegistryClient configRegistryClient) { + ObjectName on = transactionProvider.getOrCreateTransaction(); + ConfigTransactionClient proxy = configRegistryClient.getConfigTransactionClient(on); + return proxy.lookupConfigBeans(); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/DatastoreQueryStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/DatastoreQueryStrategy.java new file mode 100644 index 0000000000..4ae56703f8 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/DatastoreQueryStrategy.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig; + +import java.util.Set; + +import javax.management.ObjectName; + +import org.opendaylight.controller.config.util.ConfigRegistryClient; + +public interface DatastoreQueryStrategy { + + /** + * @param configRegistryClient + * @return + */ + Set queryInstances(ConfigRegistryClient configRegistryClient); + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java new file mode 100644 index 0000000000..9b8c1503c7 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/GetConfig.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig; + +import com.google.common.base.Optional; +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Datastore; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class GetConfig extends AbstractConfigNetconfOperation { + + public static final String GET_CONFIG = "get-config"; + + private final YangStoreSnapshot yangStoreSnapshot; + private final Optional maybeNamespace; + + private final TransactionProvider transactionProvider; + + private static final Logger logger = LoggerFactory.getLogger(GetConfig.class); + + public GetConfig(YangStoreSnapshot yangStoreSnapshot, Optional maybeNamespace, + TransactionProvider transactionProvider, ConfigRegistryClient configRegistryClient, + String netconfSessionIdForReporting) { + super(configRegistryClient, netconfSessionIdForReporting); + this.yangStoreSnapshot = yangStoreSnapshot; + this.maybeNamespace = maybeNamespace; + this.transactionProvider = transactionProvider; + } + + public static Datastore fromXml(XmlElement xml) { + xml.checkName(GET_CONFIG); + xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + + XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY, + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + XmlElement sourceNode = sourceElement.getOnlyChildElement(); + String sourceParsed = sourceNode.getName(); + logger.debug("Setting source datastore to '{}'", sourceParsed); + Datastore sourceDatastore = Datastore.valueOf(sourceParsed); + + // Filter option - unsupported + if (xml.getChildElements(XmlNetconfConstants.FILTER).size() != 0) + throw new UnsupportedOperationException("Unsupported option " + XmlNetconfConstants.FILTER + " for " + + GET_CONFIG); + + return sourceDatastore; + + } + + private Element getResponseInternal(final Document document, final ConfigRegistryClient configRegistryClient, + final Datastore source) throws NetconfDocumentedException { + Element dataElement = document.createElement(XmlNetconfConstants.DATA_KEY); + final Set instances = Datastore.getInstanceQueryStrategy(source, this.transactionProvider) + .queryInstances(configRegistryClient); + final Config configMapping = new Config(transform(configRegistryClient, + yangStoreSnapshot.getModuleMXBeanEntryMap())); + dataElement = configMapping.toXml(instances, this.maybeNamespace, document, dataElement); + + logger.info("{} operation successful", GET_CONFIG); + + return dataElement; + } + + // TODO refactor ... duplicate code + private Map> transform(final ConfigRegistryClient configRegistryClient, + Map> mBeanEntries) { + return Maps.transformEntries(mBeanEntries, + new Maps.EntryTransformer, Map>() { + + @Override + public Map transformEntry(String arg0, Map arg1) { + return Maps.transformEntries(arg1, + new Maps.EntryTransformer() { + + @Override + public ModuleConfig transformEntry(String key, ModuleMXBeanEntry value) { + return new ModuleConfig(key, new InstanceConfig(configRegistryClient, value + .getAttributes()), value.getProvidedServices().values()); + } + }); + } + }); + } + + @Override + protected String getOperationName() { + return GET_CONFIG; + } + + @Override + public Element handle(Document document, XmlElement xml) throws NetconfDocumentedException { + Datastore source; + try { + source = fromXml(xml); + } catch (final IllegalArgumentException e) { + logger.warn("Rpc error: {}", ErrorTag.bad_attribute, e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage()); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.bad_attribute, + ErrorSeverity.error, errorInfo); + } catch (final IllegalStateException e) { + logger.warn("Rpc error: {}", ErrorTag.missing_attribute, e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.missing_attribute.name(), "Missing datasource attribute value"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.rpc, ErrorTag.missing_attribute, + ErrorSeverity.error, errorInfo); + } catch (final UnsupportedOperationException e) { + logger.warn("Unsupported", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for get"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, + ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo); + } + return getResponseInternal(document, configRegistryClient, source); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java new file mode 100644 index 0000000000..c3f8257b1f --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/getconfig/RunningDatastoreQueryStrategy.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig; + +import java.util.Set; + +import javax.management.ObjectName; + +import org.opendaylight.controller.config.util.ConfigRegistryClient; + +public class RunningDatastoreQueryStrategy implements DatastoreQueryStrategy { + + @Override + public Set queryInstances(ConfigRegistryClient configRegistryClient) { + return configRegistryClient.lookupConfigBeans(); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java new file mode 100644 index 0000000000..7bdfa277a0 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpc.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; +import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; +import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.SimpleTypeResolver; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.SimpleAttributeMappingStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.InstanceRuntimeRpc; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.Rpcs; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit; +import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.management.ObjectName; +import javax.management.openmbean.SimpleType; +import java.util.Map; + +public class RuntimeRpc extends AbstractConfigNetconfOperation { + + private static final Logger logger = LoggerFactory.getLogger(Commit.class); + public static final String CONTEXT_INSTANCE = "context-instance"; + + private final YangStoreSnapshot yangStoreSnapshot; + + public RuntimeRpc(final YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient, + String netconfSessionIdForReporting) { + super(configRegistryClient, netconfSessionIdForReporting); + this.yangStoreSnapshot = yangStoreSnapshot; + } + + private String getStringRepresentation(final Object result) { + final SimpleType simpleType = SimpleTypeResolver.getSimpleType(result.getClass().getName()); + final Optional mappedAttributeOpt = new SimpleAttributeMappingStrategy(simpleType).mapAttribute(result); + return mappedAttributeOpt.isPresent() ? mappedAttributeOpt.get() : ""; + } + + private Object executeOperation(final ConfigRegistryClient configRegistryClient, final ObjectName on, + final String name, final Map attributes) { + final Object[] params = new Object[attributes.size()]; + final String[] signature = new String[attributes.size()]; + + int i = 0; + for (final String attrName : attributes.keySet()) { + final AttributeConfigElement attribute = attributes.get(attrName); + final Optional resolvedValueOpt = attribute.getResolvedValue(); + + params[i] = resolvedValueOpt.isPresent() ? resolvedValueOpt.get() : attribute.getResolvedDefaultValue(); + signature[i] = resolvedValueOpt.isPresent() ? resolvedValueOpt.get().getClass().getName() : attribute + .getResolvedDefaultValue().getClass().getName(); + i++; + } + + return configRegistryClient.invokeMethod(on, name, params, signature); + } + + public NetconfOperationExecution fromXml(final XmlElement xml) throws NetconfDocumentedException { + final String namespace = xml.getNamespace(); + final XmlElement contextInstanceElement = xml.getOnlyChildElement(CONTEXT_INSTANCE); + final String operationName = xml.getName(); + + final RuntimeRpcElementResolved id = RuntimeRpcElementResolved.fromXpath( + contextInstanceElement.getTextContent(), operationName, namespace); + + final Rpcs rpcs = mapRpcs(yangStoreSnapshot.getModuleMXBeanEntryMap()); + + final ModuleRpcs rpcMapping = rpcs.getRpcMapping(id); + final InstanceRuntimeRpc instanceRuntimeRpc = rpcMapping.getRpc(id.getRuntimeBeanName(), operationName); + + // TODO move to Rpcs after xpath attribute is redesigned + + final ObjectName on = id.getObjectName(rpcMapping); + Map attributes = instanceRuntimeRpc.fromXml(xml); + attributes = sortAttributes(attributes, xml); + + return new NetconfOperationExecution(on, instanceRuntimeRpc.getName(), attributes, + instanceRuntimeRpc.getReturnType(), namespace); + } + + @Override + public HandlingPriority canHandle(Document message) { + XmlElement requestElement = getRequestElementWithCheck(message); + + XmlElement operationElement = requestElement.getOnlyChildElement(); + final String netconfOperationName = operationElement.getName(); + final String netconfOperationNamespace = operationElement.getNamespace(); + + final Optional contextInstanceElement = operationElement + .getOnlyChildElementOptionally(CONTEXT_INSTANCE); + + if (contextInstanceElement.isPresent() == false) + return HandlingPriority.CANNOT_HANDLE; + + final RuntimeRpcElementResolved id = RuntimeRpcElementResolved.fromXpath(contextInstanceElement.get() + .getTextContent(), netconfOperationName, netconfOperationNamespace); + + // TODO reuse rpcs instance in fromXml method + final Rpcs rpcs = mapRpcs(yangStoreSnapshot.getModuleMXBeanEntryMap()); + + try { + + final ModuleRpcs rpcMapping = rpcs.getRpcMapping(id); + final InstanceRuntimeRpc instanceRuntimeRpc = rpcMapping.getRpc(id.getRuntimeBeanName(), + netconfOperationName); + Preconditions.checkState(instanceRuntimeRpc != null, "No rpc found for %s:%s", netconfOperationNamespace, + netconfOperationName); + + } catch (IllegalStateException e) { + logger.debug("Cannot handle runtime operation {}:{}", netconfOperationNamespace, netconfOperationName, e); + return HandlingPriority.CANNOT_HANDLE; + } + + return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY; + } + + @Override + protected HandlingPriority canHandle(String netconfOperationName, String namespace) { + throw new UnsupportedOperationException( + "This should not be used since it is not possible to provide check with these attributes"); + } + + @Override + protected String getOperationName() { + throw new UnsupportedOperationException("Runtime rpc does not have a stable name"); + } + + @Override + protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException { + + // TODO exception handling + // TODO check for namespaces and unknown elements + + final NetconfOperationExecution execution = fromXml(xml); + + logger.debug("Invoking operation {} on {} with arguments {}", execution.operationName, execution.on, + execution.attributes); + final Object result = executeOperation(configRegistryClient, execution.on, execution.operationName, + execution.attributes); + + logger.info("Operation {} called successfully on {} with arguments {} with result {}", execution.operationName, + execution.on, execution.attributes, result); + + if (execution.returnType.equals("void")) { + return document.createElement("ok"); + } else { + final Element output = XmlUtil.createTextElement(document, "result", getStringRepresentation(result)); + XmlUtil.addNamespaceAttr(output, execution.namespace); + return output; + } + } + + private static class NetconfOperationExecution { + + private final ObjectName on; + private final String operationName; + private final Map attributes; + private final String returnType; + private final String namespace; + + public NetconfOperationExecution(final ObjectName on, final String name, + final Map attributes, final String returnType, final String namespace) { + this.on = on; + this.operationName = name; + this.attributes = attributes; + this.returnType = returnType; + this.namespace = namespace; + } + + } + + private static Map sortAttributes( + final Map attributes, final XmlElement xml) { + final Map sorted = Maps.newLinkedHashMap(); + + for (XmlElement xmlElement : xml.getChildElements()) { + final String name = xmlElement.getName(); + if (CONTEXT_INSTANCE.equals(name) == false) { // skip context + // instance child node + // because it + // specifies + // ObjectName + final AttributeConfigElement value = attributes.get(name); + if (value == null) { + throw new IllegalArgumentException("Cannot find yang mapping for node " + xmlElement); + } + sorted.put(name, value); + } + } + + return sorted; + } + + private static Rpcs mapRpcs(final Map> mBeanEntries) { + + final Map> map = Maps.newHashMap(); + + for (final String namespace : mBeanEntries.keySet()) { + + Map namespaceToModules = map.get(namespace); + if (namespaceToModules == null) { + namespaceToModules = Maps.newHashMap(); + map.put(namespace, namespaceToModules); + } + + for (final String moduleName : mBeanEntries.get(namespace).keySet()) { + + ModuleRpcs rpcMapping = namespaceToModules.get(moduleName); + if (rpcMapping == null) { + rpcMapping = new ModuleRpcs(); + namespaceToModules.put(moduleName, rpcMapping); + } + + final ModuleMXBeanEntry entry = mBeanEntries.get(namespace).get(moduleName); + + for (final RuntimeBeanEntry runtimeEntry : entry.getRuntimeBeans()) { + rpcMapping.addNameMapping(runtimeEntry); + for (final Rpc rpc : runtimeEntry.getRpcs()) { + rpcMapping.addRpc(runtimeEntry, rpc); + } + } + } + } + + return new Rpcs(map); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpcElementResolved.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpcElementResolved.java new file mode 100644 index 0000000000..838e5d8731 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/runtimerpc/RuntimeRpcElementResolved.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc; + +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; + +import javax.management.ObjectName; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Represents parsed xpath to runtime bean instance + */ +public final class RuntimeRpcElementResolved { + private final String moduleName; + private final String instanceName; + private final String namespace; + private final String runtimeBeanName; + private final Map additionalAttributes; + + private RuntimeRpcElementResolved(String namespace, String moduleName, String instanceName, String runtimeBeanName, + Map additionalAttributes) { + this.moduleName = moduleName; + this.instanceName = instanceName; + this.additionalAttributes = additionalAttributes; + this.namespace = namespace; + this.runtimeBeanName = runtimeBeanName; + } + + public String getModuleName() { + return moduleName; + } + + public String getInstanceName() { + return instanceName; + } + + public String getNamespace() { + return namespace; + } + + public String getRuntimeBeanName() { + return runtimeBeanName; + } + + public ObjectName getObjectName(ModuleRpcs rpcMapping) { + Map additionalAttributesJavaNames = Maps + .newHashMapWithExpectedSize(additionalAttributes.size()); + for (String attributeYangName : additionalAttributes.keySet()) { + String attributeJavaName = rpcMapping.getRbeJavaName(attributeYangName); + Preconditions.checkState(attributeJavaName != null, + "Cannot find java name for runtime bean wtih yang name %s", attributeYangName); + additionalAttributesJavaNames.put(attributeJavaName, additionalAttributes.get(attributeYangName)); + } + return ObjectNameUtil.createRuntimeBeanName(moduleName, instanceName, additionalAttributesJavaNames); + } + + private static final String xpathPatternBlueprint = "/" + XmlNetconfConstants.DATA_KEY + "/" + + XmlNetconfConstants.MODULES_KEY + "/" + XmlNetconfConstants.MODULE_KEY + "\\[" + + XmlNetconfConstants.NAME_KEY + "='(.+)'\\]/" + XmlNetconfConstants.INSTANCE_KEY + "\\[" + + XmlNetconfConstants.NAME_KEY + "='([^']+)'\\](.*)"; + private static final Pattern xpathPattern = Pattern.compile(xpathPatternBlueprint); + private static final String additionalPatternBlueprint = "(.+)\\[(.+)='(.+)'\\]"; + private static final Pattern additionalPattern = Pattern.compile(additionalPatternBlueprint); + + public static RuntimeRpcElementResolved fromXpath(String xpath, String elementName, String namespace) { + Matcher matcher = xpathPattern.matcher(xpath); + Preconditions.checkState(matcher.matches(), + "Node %s with value '%s' not in required form on rpc element %s, required format is %s", + RuntimeRpc.CONTEXT_INSTANCE, xpath, elementName, xpathPatternBlueprint); + + String moduleName = matcher.group(1); + String instanceName = matcher.group(2); + String additionalString = matcher.group(3); + HashMap additionalAttributes = Maps. newHashMap(); + String runtimeBeanYangName = moduleName; + for (String additionalKeyValue : additionalString.split("/")) { + if (Strings.isNullOrEmpty(additionalKeyValue)) + continue; + matcher = additionalPattern.matcher(additionalKeyValue); + Preconditions + .checkState( + matcher.matches(), + "Attribute %s not in required form on rpc element %s, required format for additional attributes is %s", + additionalKeyValue, elementName, additionalPatternBlueprint); + String name = matcher.group(1); + runtimeBeanYangName = name; + additionalAttributes.put(name, matcher.group(3)); + } + + return new RuntimeRpcElementResolved(namespace, moduleName, instanceName, runtimeBeanYangName, + additionalAttributes); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java new file mode 100644 index 0000000000..83029c44e6 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.osgi; + +import org.opendaylight.controller.config.yang.store.api.YangStoreService; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Hashtable; + +import static com.google.common.base.Preconditions.checkState; + +public class Activator implements BundleActivator, YangStoreServiceTracker.YangStoreTrackerListener { + + private static final Logger logger = LoggerFactory.getLogger(Activator.class); + + private BundleContext context; + ServiceRegistration osgiRegistration; + + @Override + public void start(BundleContext context) throws Exception { + this.context = context; + YangStoreServiceTracker tracker = new YangStoreServiceTracker(context, this); + tracker.open(); + } + + @Override + public void stop(BundleContext context) throws Exception { + } + + @Override + public synchronized void onYangStoreAdded(YangStoreService yangStoreService) { + checkState(osgiRegistration == null, "More than one onYangStoreAdded received"); + NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService); + logger.debug("Registering into OSGi"); + osgiRegistration = context.registerService(new String[]{NetconfOperationServiceFactory.class.getName()}, factory, + new Hashtable()); + } + + @Override + public synchronized void onYangStoreRemoved() { + osgiRegistration.unregister(); + osgiRegistration = null; + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java new file mode 100644 index 0000000000..ec1915d6fc --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationProvider.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.osgi; + +import com.google.common.base.Optional; +import com.google.common.collect.Sets; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Validate; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpc; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; + +import java.util.Set; + +final class NetconfOperationProvider { + private final YangStoreSnapshot yangStoreSnapshot; + private final Set operations; + private final ConfigRegistryClient configRegistryClient; + private final TransactionProvider transactionProvider; + + NetconfOperationProvider(YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient, + TransactionProvider transactionProvider, String netconfSessionIdForReporting) { + + this.yangStoreSnapshot = yangStoreSnapshot; + this.configRegistryClient = configRegistryClient; + this.transactionProvider = transactionProvider; + operations = setUpOperations(yangStoreSnapshot, configRegistryClient, transactionProvider, + netconfSessionIdForReporting); + } + + Set getOperations() { + return operations; + } + + private static Set setUpOperations(YangStoreSnapshot yangStoreSnapshot, + ConfigRegistryClient configRegistryClient, TransactionProvider transactionProvider, + String netconfSessionIdForReporting) { + Set ops = Sets.newHashSet(); + + GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional. absent(), transactionProvider, + configRegistryClient, netconfSessionIdForReporting); + + ops.add(getConfigOp); + ops.add(new EditConfig(yangStoreSnapshot, transactionProvider, configRegistryClient, + netconfSessionIdForReporting)); + ops.add(new Commit(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); + ops.add(new Get(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting)); + ops.add(new DiscardChanges(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); + ops.add(new Validate(transactionProvider, configRegistryClient, netconfSessionIdForReporting)); + ops.add(new RuntimeRpc(yangStoreSnapshot, configRegistryClient, netconfSessionIdForReporting)); + + return ops; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java new file mode 100644 index 0000000000..35695f7510 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.osgi; + +import org.opendaylight.controller.config.util.ConfigRegistryJMXClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreException; +import org.opendaylight.controller.config.yang.store.api.YangStoreService; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.MBeanServer; +import java.lang.management.ManagementFactory; + +public class NetconfOperationServiceFactoryImpl implements NetconfOperationServiceFactory { + + public static final int ATTEMPT_TIMEOUT_MS = 1000; + + private final YangStoreService yangStoreService; + private final ConfigRegistryJMXClient jmxClient; + + private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceFactoryImpl.class); + + public NetconfOperationServiceFactoryImpl(YangStoreService yangStoreService) { + this(yangStoreService, ManagementFactory.getPlatformMBeanServer()); + } + + public NetconfOperationServiceFactoryImpl(YangStoreService yangStoreService, MBeanServer mBeanServer) { + this.yangStoreService = yangStoreService; + + // Config registry might not be present yet, but will be eventually + while(true) { + + final ConfigRegistryJMXClient configRegistryJMXClient; + try { + configRegistryJMXClient = new ConfigRegistryJMXClient(mBeanServer); + } catch (IllegalStateException e) { + logger.debug("Jmx client could not be created, reattempting"); + try { + Thread.sleep(ATTEMPT_TIMEOUT_MS); + } catch (InterruptedException e1) { + throw new RuntimeException(e1); + } + continue; + } + + jmxClient = configRegistryJMXClient; + break; + } + } + + @Override + public NetconfOperationServiceImpl createService(long netconfSessionId, String netconfSessionIdForReporting) { + try { + return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting); + } catch (YangStoreException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java new file mode 100644 index 0000000000..8497edbd26 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceImpl.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.osgi; + +import com.google.common.base.Optional; +import com.google.common.collect.Sets; +import org.opendaylight.controller.config.util.ConfigRegistryJMXClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreException; +import org.opendaylight.controller.config.yang.store.api.YangStoreService; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.confignetconfconnector.util.Util; +import org.opendaylight.controller.netconf.mapping.api.Capability; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; +import org.opendaylight.yangtools.yang.model.api.Module; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * Manages life cycle of {@link YangStoreSnapshot}. + */ +public class NetconfOperationServiceImpl implements NetconfOperationService { + + private final YangStoreSnapshot yangStoreSnapshot; + private final NetconfOperationProvider operationProvider; + private final Set capabilities; + private final TransactionProvider transactionProvider; + + public NetconfOperationServiceImpl(YangStoreService yangStoreService, ConfigRegistryJMXClient jmxClient, + String netconfSessionIdForReporting) throws YangStoreException { + + yangStoreSnapshot = yangStoreService.getYangStoreSnapshot(); + transactionProvider = new TransactionProvider(jmxClient, netconfSessionIdForReporting); + operationProvider = new NetconfOperationProvider(yangStoreSnapshot, jmxClient, transactionProvider, + netconfSessionIdForReporting); + capabilities = setupCapabilities(yangStoreSnapshot); + } + + @Override + public void close() { + yangStoreSnapshot.close(); + transactionProvider.close(); + } + + @Override + public Set getCapabilities() { + return capabilities; + } + + @Override + public Set getNetconfOperations() { + return operationProvider.getOperations(); + } + + @Override + public Set getFilters() { + return Collections.emptySet(); + } + + private static Set setupCapabilities(YangStoreSnapshot yangStoreSnapshot) { + Set capabilities = Sets.newHashSet(); + + capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0")); + capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:rollback-on-error:1.0")); + capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:operations:1.0")); + capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:operations:1.1")); + capabilities + .add(new BasicCapability( + "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04")); + + final Collection> modulesAndContents = yangStoreSnapshot.getModuleMap().values(); + for (Map.Entry moduleAndContent : modulesAndContents) { + capabilities.add(new YangStoreCapability(moduleAndContent)); + } + + return capabilities; + } + + private static class BasicCapability implements Capability { + + private final String capability; + + private BasicCapability(String capability) { + this.capability = capability; + } + + @Override + public String getCapabilityUri() { + return capability; + } + + @Override + public Optional getModuleName() { + return Optional.absent(); + } + + @Override + public Optional getRevision() { + return Optional.absent(); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.absent(); + } + } + + private static class YangStoreCapability extends BasicCapability { + + private final String content; + private final String revision; + private final String moduleName; + + public YangStoreCapability(Map.Entry moduleAndContent) { + super(getAsString(moduleAndContent.getKey())); + this.content = moduleAndContent.getValue(); + Module module = moduleAndContent.getKey(); + this.moduleName = module.getName(); + this.revision = Util.writeDate(module.getRevision()); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.of(content); + } + + private static String getAsString(Module module) { + final StringBuffer capabilityContent = new StringBuffer(); + capabilityContent.append(module.getNamespace()); + capabilityContent.append("?module="); + capabilityContent.append(module.getName()); + capabilityContent.append("&revision="); + capabilityContent.append(Util.writeDate(module.getRevision())); + return capabilityContent.toString(); + } + + @Override + public Optional getModuleName() { + return Optional.of(moduleName); + } + + @Override + public Optional getRevision() { + return Optional.of(revision); + } + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceTracker.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceTracker.java new file mode 100644 index 0000000000..3b1e89d7df --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/YangStoreServiceTracker.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.osgi; + +import org.opendaylight.controller.config.yang.store.api.YangStoreService; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +class YangStoreServiceTracker extends ServiceTracker { + private final YangStoreTrackerListener listener; + + YangStoreServiceTracker(BundleContext context, final YangStoreTrackerListener listener) { + super(context, YangStoreService.class, null); + this.listener = listener; + } + + @Override + public synchronized YangStoreService addingService(final ServiceReference reference) { + final YangStoreService yangStoreService = super.addingService(reference); + listener.onYangStoreAdded(yangStoreService); + return yangStoreService; + } + + @Override + public synchronized void removedService(final ServiceReference reference, + final YangStoreService service) { + listener.onYangStoreRemoved(); + } + + static interface YangStoreTrackerListener { + void onYangStoreAdded(YangStoreService yangStoreService); + void onYangStoreRemoved(); + } +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java new file mode 100644 index 0000000000..b3483a737a --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/transactions/TransactionProvider.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.transactions; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import org.opendaylight.controller.config.api.ValidationException; +import org.opendaylight.controller.config.api.jmx.CommitStatus; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.InstanceNotFoundException; +import javax.management.ObjectName; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class TransactionProvider implements AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(TransactionProvider.class); + + private final ConfigRegistryClient configRegistryClient; + + private final String netconfSessionIdForReporting; + private ObjectName transaction; + private final List allOpenedTransactions = new ArrayList<>(); + + public TransactionProvider(ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) { + this.configRegistryClient = configRegistryClient; + this.netconfSessionIdForReporting = netconfSessionIdForReporting; + } + + @Override + public synchronized void close() { + for (ObjectName tx : allOpenedTransactions) { + if (isStillOpenTransaction(tx)) { + try { + configRegistryClient.getConfigTransactionClient(tx).abortConfig(); + } catch (Exception e) { + logger.debug("Ignoring {} while closing transaction {}", e.toString(), tx, e); + } + } + } + allOpenedTransactions.clear(); + } + + public Optional getTransaction() { + + if (transaction == null) + return Optional.absent(); + + // Transaction was already closed somehow + if (isStillOpenTransaction(transaction) == false) { + logger.warn("Fixing illegal state: transaction {} was closed in {}", transaction, + netconfSessionIdForReporting); + transaction = null; + return Optional.absent(); + } + return Optional.of(transaction); + } + + private boolean isStillOpenTransaction(ObjectName transaction) { + boolean isStillOpenTransaction = configRegistryClient.getOpenConfigs().contains(transaction); + return isStillOpenTransaction; + } + + public synchronized ObjectName getOrCreateTransaction() { + Optional ta = getTransaction(); + + if (ta.isPresent()) + return ta.get(); + transaction = configRegistryClient.beginConfig(); + allOpenedTransactions.add(transaction); + return transaction; + } + + /** + * Used for editConfig test option + */ + public synchronized ObjectName getTestTransaction() { + ObjectName testTx = configRegistryClient.beginConfig(); + allOpenedTransactions.add(testTx); + return testTx; + } + + /** + * Commit and notification send must be atomic + */ + public synchronized CommitStatus commitTransaction() throws NetconfDocumentedException { + final Optional taON = getTransaction(); + Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting); + CommitStatus status = configRegistryClient.commitConfig(taON.get()); + allOpenedTransactions.remove(transaction); + transaction = null; + return status; + } + + public synchronized void abortTransaction() { + Optional taON = getTransaction(); + Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting); + + ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get()); + transactionClient.abortConfig(); + allOpenedTransactions.remove(transaction); + transaction = null; + } + + public synchronized void abortTestTransaction(ObjectName testTx) { + ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(testTx); + allOpenedTransactions.remove(testTx); + transactionClient.abortConfig(); + } + + public void validateTransaction() throws ValidationException { + Optional taON = getTransaction(); + Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting); + + ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get()); + transactionClient.validateConfig(); + } + + public void validateTestTransaction(ObjectName taON) { + ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON); + transactionClient.validateConfig(); + } + + public void wipeTestTransaction(ObjectName taON) { + wipeInternal(taON, true, null); + } + + /** + * Wiping means removing all module instances keeping the transaction open. + */ + synchronized void wipeInternal(ObjectName taON, boolean isTest, String moduleName) { + ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON); + + Set lookupConfigBeans = moduleName == null ? transactionClient.lookupConfigBeans() + : transactionClient.lookupConfigBeans(moduleName); + for (ObjectName instance : lookupConfigBeans) { + try { + transactionClient.destroyModule(instance); + } catch (InstanceNotFoundException e) { + if (isTest) + logger.debug("Unable to clean configuration in transactiom {}", taON, e); + else + logger.warn("Unable to clean configuration in transactiom {}", taON, e); + + throw new IllegalStateException("Unable to clean configuration in transactiom " + taON, e); + } + } + logger.debug("Transaction {} wiped clean", taON); + } + + public void wipeTransaction() { + Optional taON = getTransaction(); + Preconditions.checkState(taON.isPresent(), "No transaction found for session " + netconfSessionIdForReporting); + wipeInternal(taON.get(), false, null); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/util/Util.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/util/Util.java new file mode 100644 index 0000000000..26719592bb --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/util/Util.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.util; + +import com.google.common.base.Preconditions; +import org.opendaylight.controller.config.yang.store.api.YangStoreException; +import org.opendaylight.controller.config.yang.store.api.YangStoreService; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public final class Util { + + /** + * Used for date <-> xml serialization + */ + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + public static String writeDate(final Date date) { + return dateFormat.format(date); + } + + public static Date readDate(final String s) throws ParseException { + return dateFormat.parse(s); + } + + public static void checkType(final Object value, final Class clazz) { + Preconditions.checkArgument(clazz.isAssignableFrom(value.getClass()), "Unexpected type " + value.getClass() + + " should be " + clazz); + } + + // TODO: add message and proper error types + public static YangStoreSnapshot getYangStore(final YangStoreService yangStoreService) + throws NetconfDocumentedException { + try { + return yangStoreService.getYangStoreSnapshot(); + } catch (final YangStoreException e) { + throw new NetconfDocumentedException("TODO", e, ErrorType.application, ErrorTag.bad_attribute, + ErrorSeverity.error); + } + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java new file mode 100644 index 0000000000..c72cb7498b --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.matchers.JUnitMatchers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.api.ModuleIdentifier; +import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator; +import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; +import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl; +import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.config.yang.store.impl.MbeParser; +import org.opendaylight.controller.config.yang.test.impl.*; +import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.DiscardChanges; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.get.Get; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.getconfig.GetConfig; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpc; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession; +import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.management.InstanceAlreadyExistsException; +import javax.management.ObjectName; +import javax.xml.parsers.ParserConfigurationException; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.util.*; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class NetconfMappingTest extends AbstractConfigTest { + private static final Logger logger = LoggerFactory.getLogger(NetconfMappingTest.class); + + private static final String INSTANCE_NAME = "test1"; + private static final String NETCONF_SESSION_ID = "foo"; + private NetconfTestImplModuleFactory factory; + private DepTestImplModuleFactory factory2; + + @Mock + YangStoreSnapshot yangStoreSnapshot; + @Mock + NetconfOperationRouter netconfOperationRouter; + + private TransactionProvider transactionProvider; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doReturn(getMbes()).when(this.yangStoreSnapshot).getModuleMXBeanEntryMap(); + this.factory = new NetconfTestImplModuleFactory(); + this.factory2 = new DepTestImplModuleFactory(); + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(this.factory, this.factory2)); + + transactionProvider = new TransactionProvider(this.configRegistryClient, NETCONF_SESSION_ID); + } + + private ObjectName createModule(final String instanceName) throws InstanceAlreadyExistsException { + final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction(); + + final ObjectName on = transaction.createModule(this.factory.getImplementationName(), instanceName); + final NetconfTestImplModuleMXBean mxBean = transaction.newMXBeanProxy(on, NetconfTestImplModuleMXBean.class); + setModule(mxBean, transaction); + + transaction.commit(); + return on; + } + + @Test + public void testConfigNetconf() throws Exception { + + createModule(INSTANCE_NAME); + + edit("netconfMessages/editConfig.xml"); + checkBinaryLeafEdited(getConfigCandidate()); + + // default-operation:none, should not affect binary leaf + edit("netconfMessages/editConfig_none.xml"); + checkBinaryLeafEdited(getConfigCandidate()); + + // check after edit + commit(); + Element response = getConfigRunning(); + // System.out.println(Xml.toString(response)); + + checkBinaryLeafEdited(response); + checkTypeConfigAttribute(response); + + edit("netconfMessages/editConfig_remove.xml"); + + commit(); + response = getConfigCandidate(); + final String responseFromCandidate = XmlUtil.toString(response).replaceAll("\\s+", ""); + // System.out.println(responseFromCandidate); + response = getConfigRunning(); + final String responseFromRunning = XmlUtil.toString(response).replaceAll("\\s+", ""); + // System.out.println(responseFromRunning); + assertEquals(responseFromCandidate, responseFromRunning); + + final String expectedResult = XmlFileLoader.fileToString("netconfMessages/editConfig_expectedResult.xml") + .replaceAll("\\s+", ""); + + assertEquals(expectedResult, responseFromRunning); + assertEquals(expectedResult, responseFromCandidate); + + edit("netconfMessages/editConfig_none.xml"); + doNothing().when(netconfOperationRouter).close(); + closeSession(); + verify(netconfOperationRouter).close(); + verifyNoMoreInteractions(netconfOperationRouter); + } + + private void closeSession() throws NetconfDocumentedException, ParserConfigurationException, SAXException, + IOException { + DefaultCloseSession closeOp = new DefaultCloseSession(NETCONF_SESSION_ID); + executeOp(closeOp, "netconfMessages/closeSession.xml"); + } + + private void edit(String resource) throws ParserConfigurationException, SAXException, IOException, + NetconfDocumentedException { + EditConfig editOp = new EditConfig(yangStoreSnapshot, transactionProvider, configRegistryClient, + NETCONF_SESSION_ID); + executeOp(editOp, resource); + } + + private void commit() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { + Commit commitOp = new Commit(transactionProvider, configRegistryClient, NETCONF_SESSION_ID); + executeOp(commitOp, "netconfMessages/commit.xml"); + } + + private Element getConfigCandidate() throws ParserConfigurationException, SAXException, IOException, + NetconfDocumentedException { + GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional. absent(), transactionProvider, + configRegistryClient, NETCONF_SESSION_ID); + return executeOp(getConfigOp, "netconfMessages/getConfig_candidate.xml"); + } + + private Element getConfigRunning() throws ParserConfigurationException, SAXException, IOException, + NetconfDocumentedException { + GetConfig getConfigOp = new GetConfig(yangStoreSnapshot, Optional. absent(), transactionProvider, + configRegistryClient, NETCONF_SESSION_ID); + return executeOp(getConfigOp, "netconfMessages/getConfig.xml"); + } + + @Test(expected = NetconfDocumentedException.class) + public void testConfigNetconfReplaceDefaultEx() throws Exception { + + createModule(INSTANCE_NAME); + + edit("netconfMessages/editConfig.xml"); + edit("netconfMessages/editConfig_replace_default_ex.xml"); + } + + @Test + public void testConfigNetconfReplaceDefault() throws Exception { + + createModule(INSTANCE_NAME); + + edit("netconfMessages/editConfig.xml"); + commit(); + Element response = getConfigRunning(); + final int allInstances = response.getElementsByTagName("module").getLength(); + + edit("netconfMessages/editConfig_replace_default.xml"); + + commit(); + response = getConfigRunning(); + + final int afterReplace = response.getElementsByTagName("module").getLength(); + assertEquals(4, allInstances); + assertEquals(2, afterReplace); + } + + @Test(expected = NetconfDocumentedException.class) + public void testSameAttrDifferentNamespaces() throws Exception { + try { + edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml"); + } catch (NetconfDocumentedException e) { + String message = e.getMessage(); + assertThat(message, + JUnitMatchers + .containsString("Element simple-long-2 present multiple times with different namespaces")); + assertThat(message, + JUnitMatchers.containsString("urn:opendaylight:params:xml:ns:yang:controller:test:impl")); + assertThat(message, + JUnitMatchers + .containsString(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG)); + throw e; + } + } + + @Test(expected = NetconfDocumentedException.class) + public void testDifferentNamespaceInTO() throws Exception { + try { + edit("netconfMessages/namespaces/editConfig_differentNamespaceTO.xml"); + } catch (NetconfDocumentedException e) { + String message = e.getMessage(); + assertThat(message, JUnitMatchers.containsString("Unrecognised elements")); + assertThat(message, JUnitMatchers.containsString("simple-int2")); + assertThat(message, JUnitMatchers.containsString("dto_d")); + throw e; + } + } + + @Test(expected = NetconfDocumentedException.class) + public void testSameAttrDifferentNamespacesList() throws Exception { + try { + edit("netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml"); + } catch (NetconfDocumentedException e) { + String message = e.getMessage(); + assertThat(message, + JUnitMatchers.containsString("Element binaryLeaf present multiple times with different namespaces")); + assertThat(message, + JUnitMatchers.containsString("urn:opendaylight:params:xml:ns:yang:controller:test:impl")); + assertThat(message, + JUnitMatchers + .containsString(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG)); + throw e; + } + } + + @Test + public void testTypeNameConfigAttributeMatching() throws Exception { + edit("netconfMessages/editConfig.xml"); + commit(); + edit("netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml"); + commit(); + + Element response = getConfigRunning(); + checkTypeConfigAttribute(response); + } + + // TODO add functionality + @Test(expected = NetconfDocumentedException.class) + public void testConfigNetconfReplaceModuleEx() throws Exception { + + createModule(INSTANCE_NAME); + + edit("netconfMessages/editConfig.xml"); + edit("netconfMessages/editConfig_replace_module_ex.xml"); + } + + @Test + public void testUnrecognisedConfigElements() throws Exception { + + String format = "netconfMessages/unrecognised/editConfig_unrecognised%d.xml"; + final int TESTS_COUNT = 8; + + for (int i = 0; i < TESTS_COUNT; i++) { + String file = String.format(format, i + 1); + try { + edit(file); + } catch (NetconfDocumentedException e) { + Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("Unrecognised elements")); + Assert.assertThat(e.getMessage(), JUnitMatchers.containsString("unknownAttribute")); + continue; + } + fail("Unrecognised test should throw exception " + file); + } + } + + @Test + @Ignore + // FIXME + public void testConfigNetconfReplaceModule() throws Exception { + + createModule(INSTANCE_NAME); + + edit("netconfMessages/editConfig.xml"); + commit(); + Element response = getConfigRunning(); + final int allInstances = response.getElementsByTagName("instance").getLength(); + + edit("netconfMessages/editConfig_replace_module.xml"); + + commit(); + response = getConfigRunning(); + final int afterReplace = response.getElementsByTagName("instance").getLength(); + + Assert.assertEquals(4 + 4 /* Instances from services */, allInstances); + Assert.assertEquals(3 + 3, afterReplace); + } + + @Test(expected = NetconfDocumentedException.class) + public void testEx() throws Exception { + + commit(); + } + + @Test(expected = NetconfDocumentedException.class) + public void testEx2() throws Exception { + discard(); + } + + private void discard() throws ParserConfigurationException, SAXException, IOException, NetconfDocumentedException { + DiscardChanges discardOp = new DiscardChanges(transactionProvider, configRegistryClient, NETCONF_SESSION_ID); + executeOp(discardOp, "netconfMessages/discardChanges.xml"); + } + + private void checkBinaryLeafEdited(final Element response) { + final NodeList children = response.getElementsByTagName("binaryLeaf"); + assertEquals(3, children.getLength()); + final StringBuffer buf = new StringBuffer(); + for (int i = 0; i < 3; i++) { + final Element e = (Element) children.item(i); + buf.append(XmlElement.fromDomElement(e).getTextContent()); + } + assertEquals("810", buf.toString()); + + } + + private void checkTypeConfigAttribute(Element response) { + + XmlElement modulesElement = XmlElement.fromDomElement(response).getOnlyChildElement("data") + .getOnlyChildElement("modules"); + + XmlElement configAttributeType = null; + for (XmlElement moduleElement : modulesElement.getChildElements("module")) { + for (XmlElement type : moduleElement.getChildElements("type")) { + if (type.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY).equals("") == false) { + configAttributeType = type; + } + } + } + + assertEquals("configAttributeType", configAttributeType.getTextContent()); + } + + private Map> getMbes() throws Exception { + final List yangDependencies = getYangs(); + + final Map> mBeanEntries = Maps.newHashMap(); + mBeanEntries.putAll(new MbeParser().parseYangFiles(yangDependencies).getModuleMXBeanEntryMap()); + + return mBeanEntries; + } + + @Test + public void testConfigNetconfRuntime() throws Exception { + + ModuleIdentifier id = new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance"); + RootRuntimeBeanRegistrator rootReg = new RootRuntimeBeanRegistratorImpl(internalJmxRegistrator, id); + NetconfTestImplRuntimeRegistrator registrator = new NetconfTestImplRuntimeRegistrator(rootReg); + + NetconfTestImplRuntimeRegistration a = registerRoot(registrator); + InnerRunningDataRuntimeRegistration reg = registerInner(a); + registerInner2(reg); + + id = new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance2"); + rootReg = new RootRuntimeBeanRegistratorImpl(internalJmxRegistrator, id); + registrator = new NetconfTestImplRuntimeRegistrator(rootReg); + + a = registerRoot(registrator); + registerAdditional(a); + registerAdditional(a); + registerAdditional(a); + registerAdditional(a); + reg = registerInner(a); + registerInner2(reg); + reg = registerInner(a); + registerInner2(reg); + registerInner2(reg); + reg = registerInner(a); + registerInner2(reg); + registerInner2(reg); + registerInner2(reg); + reg = registerInner(a); + registerInner2(reg); + registerInner2(reg); + registerInner2(reg); + registerInner2(reg); + + Element response = get(); + + assertEquals(2, getElementsSize(response, "instance")); + assertEquals(2, getElementsSize(response, "asdf")); + assertEquals(5, getElementsSize(response, "inner-running-data")); + assertEquals(5, getElementsSize(response, "deep2")); + assertEquals(11, getElementsSize(response, "inner-inner-running-data")); + assertEquals(11, getElementsSize(response, "deep3")); + assertEquals(4, getElementsSize(response, "inner-running-data-additional")); + assertEquals(4, getElementsSize(response, "deep4")); + // TODO assert keys + + RuntimeRpc netconf = new RuntimeRpc(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID); + + response = executeOp(netconf, "netconfMessages/rpc.xml"); + assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("testarg1".toUpperCase())); + + response = executeOp(netconf, "netconfMessages/rpcInner.xml"); + assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("ok")); + + response = executeOp(netconf, "netconfMessages/rpcInnerInner.xml"); + assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("true")); + } + + private Element get() throws NetconfDocumentedException, ParserConfigurationException, SAXException, IOException { + Get getOp = new Get(yangStoreSnapshot, configRegistryClient, NETCONF_SESSION_ID); + return executeOp(getOp, "netconfMessages/get.xml"); + } + + private int getElementsSize(Element response, String elementName) { + return response.getElementsByTagName(elementName).getLength(); + } + + private Object registerAdditional(final NetconfTestImplRuntimeRegistration a) { + class InnerRunningDataAdditionalRuntimeMXBeanTest implements InnerRunningDataAdditionalRuntimeMXBean { + + private final int simpleInt; + private final String simpleString; + + public InnerRunningDataAdditionalRuntimeMXBeanTest(final int simpleInt, final String simpleString) { + this.simpleInt = simpleInt; + this.simpleString = simpleString; + } + + @Override + public Integer getSimpleInt3() { + return this.simpleInt; + } + + @Override + public Deep4 getDeep4() { + final Deep4 d = new Deep4(); + d.setBoool(false); + return d; + } + + @Override + public String getSimpleString() { + return this.simpleString; + } + + @Override + public void noArgInner() { + } + + } + + final int simpleInt = counter++; + return a.register(new InnerRunningDataAdditionalRuntimeMXBeanTest(simpleInt, "randomString_" + simpleInt)); + } + + private void registerInner2(final InnerRunningDataRuntimeRegistration reg) { + class InnerInnerRunningDataRuntimeMXBeanTest implements InnerInnerRunningDataRuntimeMXBean { + + private final int simpleInt; + + public InnerInnerRunningDataRuntimeMXBeanTest(final int simpleInt) { + this.simpleInt = simpleInt; + } + + @Override + public List getNotStateBean() { + final NotStateBean notStateBean = new NotStateBean(); + final NotStateBeanInternal notStateBeanInternal = new NotStateBeanInternal(); + notStateBean.setNotStateBeanInternal(Lists.newArrayList(notStateBeanInternal)); + return Lists.newArrayList(notStateBean); + } + + @Override + public Integer getSimpleInt3() { + return this.simpleInt; + } + + @Override + public Deep3 getDeep3() { + return new Deep3(); + } + + @Override + public Boolean noArgInnerInner(Integer integer, Boolean aBoolean) { + return aBoolean; + } + + } + + reg.register(new InnerInnerRunningDataRuntimeMXBeanTest(counter++)); + + } + + private static int counter = 1000; + + private InnerRunningDataRuntimeRegistration registerInner(final NetconfTestImplRuntimeRegistration a) { + + class InnerRunningDataRuntimeMXBeanTest implements InnerRunningDataRuntimeMXBean { + + private final int simpleInt; + + public InnerRunningDataRuntimeMXBeanTest(final int simpleInt) { + this.simpleInt = simpleInt; + } + + @Override + public Integer getSimpleInt3() { + return this.simpleInt; + } + + @Override + public Deep2 getDeep2() { + return new Deep2(); + } + + } + return a.register(new InnerRunningDataRuntimeMXBeanTest(counter++)); + } + + private NetconfTestImplRuntimeRegistration registerRoot(final NetconfTestImplRuntimeRegistrator registrator) { + final NetconfTestImplRuntimeRegistration a = registrator.register(new NetconfTestImplRuntimeMXBean() { + + @Override + public Long getCreatedSessions() { + return 11L; + } + + @Override + public Asdf getAsdf() { + final Asdf asdf = new Asdf(); + asdf.setSimpleInt(55); + asdf.setSimpleString("asdf"); + return asdf; + } + + @Override + public String noArg(final String arg1) { + return arg1.toUpperCase(); + } + + }); + return a; + } + + private Element executeOp(final NetconfOperation op, final String filename) throws ParserConfigurationException, + SAXException, IOException, NetconfDocumentedException { + + final Document request = XmlFileLoader.xmlFileToDocument(filename); + + logger.debug("Executing netconf operation\n{}", XmlUtil.toString(request)); + HandlingPriority priority = op.canHandle(request); + + Preconditions.checkState(priority != HandlingPriority.CANNOT_HANDLE); + + final Document response = op.handle(request, netconfOperationRouter); + logger.debug("Got response\n{}", XmlUtil.toString(response)); + return response.getDocumentElement(); + } + + private List getYangs() throws FileNotFoundException { + List paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang", + "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang", + "/META-INF/yang/ietf-inet-types.yang"); + final Collection yangDependencies = new ArrayList<>(); + for (String path : paths) { + final InputStream is = Preconditions + .checkNotNull(getClass().getResourceAsStream(path), path + " not found"); + yangDependencies.add(is); + } + return Lists.newArrayList(yangDependencies); + } + + private void setModule(final NetconfTestImplModuleMXBean mxBean, final ConfigTransactionJMXClient transaction) + throws InstanceAlreadyExistsException { + mxBean.setSimpleInt((long) 44); + mxBean.setBinaryLeaf(new byte[] { 8, 7, 9 }); + final DtoD dtob = getDtoD(); + mxBean.setDtoD(dtob); + // + final DtoC dtoa = getDtoC(); + mxBean.setDtoC(dtoa); + mxBean.setSimpleBoolean(false); + // + final Peers p1 = new Peers(); + p1.setCoreSize(44L); + p1.setPort("port1"); + p1.setSimpleInt3(456); + final Peers p2 = new Peers(); + p2.setCoreSize(44L); + p2.setPort("port23"); + p2.setSimpleInt3(456); + mxBean.setPeers(Lists. newArrayList(p1, p2)); + // // + mxBean.setSimpleLong(454545L); + mxBean.setSimpleLong2(44L); + mxBean.setSimpleBigInteger(BigInteger.valueOf(999L)); + mxBean.setSimpleByte(new Byte((byte) 4)); + mxBean.setSimpleShort(new Short((short) 4)); + mxBean.setSimpleTest(545); + + mxBean.setComplexList(Lists. newArrayList()); + mxBean.setSimpleList(Lists. newArrayList()); + + final ObjectName testingDepOn = transaction.createModule(this.factory2.getImplementationName(), "dep"); + mxBean.setTestingDep(testingDepOn); + } + + private static DtoD getDtoD() { + final DtoD dtob = new DtoD(); + dtob.setSimpleInt1((long) 444); + dtob.setSimpleInt2((long) 4444); + dtob.setSimpleInt3(454); + final ComplexDtoBInner dtobInner = new ComplexDtoBInner(); + final Deep deep = new Deep(); + deep.setSimpleInt3(4); + dtobInner.setDeep(deep); + dtobInner.setSimpleInt3(44); + dtobInner.setSimpleList(Lists.newArrayList(4)); + dtob.setComplexDtoBInner(Lists.newArrayList(dtobInner)); + dtob.setSimpleList(Lists.newArrayList(4)); + return dtob; + } + + private static DtoC getDtoC() { + final DtoC dtoa = new DtoC(); + // dtoa.setSimpleArg((long) 55); + final DtoAInner dtoAInner = new DtoAInner(); + final DtoAInnerInner dtoAInnerInner = new DtoAInnerInner(); + dtoAInnerInner.setSimpleArg(456L); + dtoAInner.setDtoAInnerInner(dtoAInnerInner); + dtoAInner.setSimpleArg(44L); + dtoa.setDtoAInner(dtoAInner); + return dtoa; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/ServiceTrackerTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/ServiceTrackerTest.java new file mode 100644 index 0000000000..e07f9ce3e1 --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/ServiceTrackerTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector; + +import org.junit.Test; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services.ServiceInstance; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ServiceTrackerTest { + + @Test + public void test() { + Services.ServiceInstance serviceInstance = new ServiceInstance("module", "serviceInstance"); + + String string = serviceInstance.toString(); + + Services.ServiceInstance serviceInstance2 = Services.ServiceInstance.fromString(string); + + assertEquals(serviceInstance, serviceInstance2); + } + + @Test + public void testOneInstanceMultipleServices() { + Services services = new Services(); + services.addServiceEntry("s1", "module", "instance"); + assertEquals(1, services.getMappedServices().size()); + + services.addServiceEntry("s2", "module", "instance"); + assertEquals(2, services.getMappedServices().size()); + } + + @Test + public void testMultipleInstancesOneName() throws Exception { + Services services = new Services(); + services.addServiceEntry("s1", "module", "instance"); + assertEquals(1, services.getMappedServices().size()); + + services.addServiceEntry("s1", "module2", "instance"); + assertEquals(1, services.getMappedServices().size()); + assertEquals(2, services.getMappedServices().get("s1").size()); + assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance")); + assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_1")); + } + + @Test + public void testMultipleInstancesOneName2() throws Exception { + Services services = new Services(); + services.addServiceEntry("s1", "module", "instance_1"); + + services.addServiceEntry("s2", "module2", "instance"); + services.addServiceEntry("s2", "module3", "instance"); + services.addServiceEntry("s1", "module3", "instance"); + + assertEquals(2, services.getMappedServices().get("s1").size()); + assertEquals(2, services.getMappedServices().get("s2").size()); + assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_2")); + assertTrue(services.getMappedServices().get("s1").containsKey("ref_instance_1")); + assertTrue(services.getMappedServices().get("s2").containsKey("ref_instance")); + assertTrue(services.getMappedServices().get("s2").containsKey("ref_instance_2")); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/ValidateTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/ValidateTest.java new file mode 100644 index 0000000000..8d14e9680c --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/ValidateTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations; + +import org.junit.Test; +import org.opendaylight.controller.config.api.ValidationException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Element; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +public class ValidateTest { + + public static final String NETCONF_SESSION_ID_FOR_REPORTING = "foo"; + + @Test(expected = NetconfDocumentedException.class) + public void test() throws Exception { + final XmlElement xml = XmlElement.fromString(""); + final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING); + validate.handle(null, xml); + } + + @Test(expected = NetconfDocumentedException.class) + public void testNoSource() throws Exception { + final XmlElement xml = XmlElement.fromString(""); + final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING); + validate.handle(null, xml); + } + + @Test(expected = NetconfDocumentedException.class) + public void testNoNamespace() throws Exception { + final XmlElement xml = XmlElement.fromString(""); + final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING); + validate.handle(null, xml); + } + + @Test(expected = NetconfDocumentedException.class) + public void testRunningSource() throws Exception { + + final XmlElement xml = XmlElement.fromString(""); + final Validate validate = new Validate(null, null, NETCONF_SESSION_ID_FOR_REPORTING); + validate.handle(null, xml); + } + + @Test(expected = NetconfDocumentedException.class) + public void testNoTransaction() throws Exception { + final XmlElement xml = XmlElement.fromString(""); + final TransactionProvider transactionProvider = mock(TransactionProvider.class); + doThrow(IllegalStateException.class).when(transactionProvider).validateTransaction(); + final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING); + validate.handle(null, xml); + } + + @Test(expected = NetconfDocumentedException.class) + public void testValidationException() throws Exception { + final XmlElement xml = XmlElement.fromString(">"); + final TransactionProvider transactionProvider = mock(TransactionProvider.class); + doThrow(ValidationException.class).when(transactionProvider).validateTransaction(); + final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING); + validate.handle(null, xml); + } + + @Test + public void testValidation() throws Exception { + final XmlElement xml = XmlElement.fromString(""); + final TransactionProvider transactionProvider = mock(TransactionProvider.class); + final Element okElement = XmlUtil.readXmlToElement(""); + doNothing().when(transactionProvider).validateTransaction(); + final Validate validate = new Validate(transactionProvider, null, NETCONF_SESSION_ID_FOR_REPORTING); + Element ok = validate.handle(XmlUtil.newDocument(), xml); + assertEquals(XmlUtil.toString(okElement), XmlUtil.toString(ok)); + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigTest.java new file mode 100644 index 0000000000..f43caf641f --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/EditConfigTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import com.google.common.base.Optional; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.util.ConfigRegistryClient; +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfigElementResolved; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigStrategy; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser.EditConfigExecution; +import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.ValidateTest; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; + +import javax.management.ObjectName; +import java.util.Map; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; + +public class EditConfigTest { + + @Mock + private YangStoreSnapshot yangStoreSnapshot; + @Mock + private TransactionProvider provider; + @Mock + private ConfigRegistryClient configRegistry; + @Mock + private ConfigTransactionClient configTransactionClient; + @Mock + private ObjectName mockOn; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + doReturn("mockON").when(mockOn).toString(); + doReturn(mockOn).when(provider).getTestTransaction(); + doNothing().when(provider).validateTestTransaction(any(ObjectName.class)); + + doReturn(mockOn).when(provider).getTestTransaction(); + doNothing().when(provider).abortTestTransaction(any(ObjectName.class)); + doReturn(mockOn).when(provider).getOrCreateTransaction(); + + doReturn(configTransactionClient).when(configRegistry).getConfigTransactionClient(any(ObjectName.class)); + doReturn("mockConfigTransactionClient").when(configTransactionClient).toString(); + + doReturn(mockOn).when(configTransactionClient).lookupConfigBean(anyString(), anyString()); + } + + @Test + public void test() throws NetconfDocumentedException { + EditConfig edit = new EditConfig(yangStoreSnapshot, provider, configRegistry, + ValidateTest.NETCONF_SESSION_ID_FOR_REPORTING); + EditConfigStrategy editStrat = mock(EditConfigStrategy.class); + doNothing().when(editStrat).executeConfiguration(anyString(), anyString(), anyMap(), + any(ConfigTransactionClient.class)); + Map> resolvedXmlElements = getMapping(editStrat); + + Config cfg = mock(Config.class); + XmlElement xmlElement = mock(XmlElement.class); + doReturn(resolvedXmlElements).when(cfg).fromXml(xmlElement); + + EditConfigExecution editConfigExecution = new EditConfigExecution(null, cfg, xmlElement, + EditConfigXmlParser.TestOption.testThenSet); + + edit.getResponseInternal(XmlUtil.newDocument(), editConfigExecution); + + verify(provider).getTestTransaction(); + verify(provider).validateTestTransaction(mockOn); + verify(provider).abortTestTransaction(mockOn); + + verify(provider).getOrCreateTransaction(); + + // For every instance execute strat + verify(editStrat, times(2/* Test */+ 2/* Set */)).executeConfiguration(anyString(), anyString(), anyMap(), + any(ConfigTransactionClient.class)); + } + + private Map> getMapping(EditConfigStrategy editStrat) { + Map> result = Maps.newHashMap(); + + Multimap innerMultimap = HashMultimap.create(); + Map attributes = getSimpleAttributes(); + + InstanceConfigElementResolved ice1 = mock(InstanceConfigElementResolved.class); + doReturn(attributes).when(ice1).getConfiguration(); + doReturn(editStrat).when(ice1).getEditStrategy(); + innerMultimap.put("m1", new ModuleElementResolved("i1", ice1)); + + InstanceConfigElementResolved ice2 = mock(InstanceConfigElementResolved.class); + doReturn(attributes).when(ice2).getConfiguration(); + doReturn(editStrat).when(ice2).getEditStrategy(); + innerMultimap.put("m1", new ModuleElementResolved("i2", ice2)); + + result.put("n1", innerMultimap); + + return result; + } + + static Map getSimpleAttributes() { + Map attributes = Maps.newHashMap(); + AttributeConfigElement ace1 = mock(AttributeConfigElement.class); + doReturn("abcd").when(ace1).getResolvedDefaultValue(); + doReturn(Optional. of("abc")).when(ace1).getResolvedValue(); + doReturn("mockedAce1").when(ace1).toString(); + doReturn("jmxNameAce1").when(ace1).getJmxName(); + attributes.put("a1", ace1); + return attributes; + } + +} diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java new file mode 100644 index 0000000000..13421a215e --- /dev/null +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/operations/editconfig/ReplaceEditConfigStrategyTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; + +import java.util.Map; + +import javax.management.Attribute; +import javax.management.ObjectName; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.util.ConfigTransactionClient; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement; + +import com.google.common.collect.Sets; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigTest; +import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.ReplaceEditConfigStrategy; + +public class ReplaceEditConfigStrategyTest { + + @Mock + private ConfigTransactionClient ta; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + doNothing().when(ta).destroyConfigBean(anyString(), anyString()); + doReturn(mockON()).when(ta).lookupConfigBean(anyString(), anyString()); + doNothing().when(ta).setAttribute(any(ObjectName.class), anyString(), any(Attribute.class)); + } + + @Test + public void test() throws Exception { + ReplaceEditConfigStrategy strat = new ReplaceEditConfigStrategy(); + + Map map = EditConfigTest.getSimpleAttributes(); + + doReturn(Sets.newHashSet(mockON(), mockON())).when(ta).lookupConfigBeans(); + + strat.executeConfiguration("m1", "i1", map, ta); + + verify(ta).lookupConfigBean(anyString(), anyString()); + verify(ta).setAttribute(any(ObjectName.class), anyString(), any(Attribute.class)); + } + + ObjectName mockON() { + ObjectName mock = mock(ObjectName.class); + doReturn("mockON").when(mock).toString(); + return mock; + } + +} diff --git a/opendaylight/netconf/config-persister-impl/pom.xml b/opendaylight/netconf/config-persister-impl/pom.xml new file mode 100644 index 0000000000..c86202b6fb --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/pom.xml @@ -0,0 +1,98 @@ + + 4.0.0 + + + netconf-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + + config-persister-impl + ${project.artifactId} + bundle + + + + + org.opendaylight.controller + config-persister-api + 0.2.1-SNAPSHOT + + + ${project.groupId} + netconf-api + ${project.version} + + + ${project.groupId} + netconf-client + ${project.version} + + + ${project.groupId} + netconf-util + ${project.version} + + + org.slf4j + slf4j-api + + + com.google.guava + guava + + + org.osgi + org.osgi.core + + + org.opendaylight.controller + config-persister-file-adapter + 0.2.1-SNAPSHOT + + + + + org.opendaylight.bgpcep + mockito-configuration + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator + + org.opendaylight.controller.config.persister.storage.adapter + + + com.google.common.base, + com.google.common.collect, + javax.management, + javax.xml.parsers, + org.opendaylight.controller.config.persist.api, + org.opendaylight.controller.config.stat, + org.opendaylight.controller.config.persist.api.storage, + org.opendaylight.controller.netconf.api, + org.opendaylight.controller.netconf.api.jmx, + org.opendaylight.controller.netconf.client, + org.opendaylight.controller.netconf.util.osgi, + org.opendaylight.controller.netconf.util.xml, + org.osgi.framework, + org.slf4j, + org.w3c.dom, + org.xml.sax, + + + + + + + + + diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java new file mode 100644 index 0000000000..c01a225a33 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.persist.impl; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import org.opendaylight.controller.config.persist.api.Persister; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification; +import org.opendaylight.controller.netconf.api.jmx.DefaultCommitOperationMXBean; +import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification; +import org.opendaylight.controller.netconf.client.NetconfClient; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.annotation.concurrent.ThreadSafe; +import javax.management.*; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.Set; + +/** + * Responsible for listening for notifications from netconf containing latest + * committed configuration that should be persisted, and also for loading last + * configuration. + */ +@ThreadSafe +public class ConfigPersisterNotificationHandler implements NotificationListener, Closeable { + + private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class); + + private final InetSocketAddress address; + + private NetconfClient netconfClient; + + private final Persister persister; + private final MBeanServerConnection mbeanServer; + private Long currentSessionId; + + private final ObjectName on = DefaultCommitOperationMXBean.objectName; + + public static final long DEFAULT_TIMEOUT = 40000L; + private final long timeout; + + public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address, + MBeanServerConnection mbeanServer) { + this(persister, address, mbeanServer, DEFAULT_TIMEOUT); + } + + public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address, + MBeanServerConnection mbeanServer, long timeout) { + this.persister = persister; + this.address = address; + this.mbeanServer = mbeanServer; + this.timeout = timeout; + } + + public void init() throws InterruptedException { + Optional maybeConfig = loadLastConfig(); + + if (maybeConfig.isPresent()) { + logger.debug("Last config found {}", persister); + + registerToNetconf(maybeConfig.get().getCapabilities()); + + final String configSnapshot = maybeConfig.get().getConfigSnapshot(); + try { + pushLastConfig(XmlUtil.readXmlToElement(configSnapshot)); + } catch (SAXException | IOException e) { + throw new IllegalStateException("Unable to load last config", e); + } + + } else { + // this ensures that netconf is initialized, this is first + // connection + // this means we can register as listener for commit + registerToNetconf(Collections.emptySet()); + + logger.info("No last config provided by backend storage {}", persister); + } + registerAsJMXListener(); + } + + private synchronized long registerToNetconf(Set expectedCaps) throws InterruptedException { + + Set currentCapabilities = Sets.newHashSet(); + + // TODO think about moving capability subset check to netconf client + // could be utilized by integration tests + + long pollingStart = System.currentTimeMillis(); + int delay = 5000; + + int attempt = 0; + + while (true) { + attempt++; + + try { + netconfClient = new NetconfClient(this.toString(), address, delay); + // TODO is this correct ex to catch ? + } catch (IllegalStateException e) { + logger.debug("Netconf {} was not initialized or is not stable, attempt {}", address, attempt, e); + Thread.sleep(delay); + continue; + } + currentCapabilities = netconfClient.getCapabilities(); + + if (isSubset(currentCapabilities, expectedCaps)) { + logger.debug("Hello from netconf stable with {} capabilities", currentCapabilities); + currentSessionId = netconfClient.getSessionId(); + logger.info("Session id received from netconf server: {}", currentSessionId); + return currentSessionId; + } + + if (System.currentTimeMillis() > pollingStart + timeout) { + break; + } + + logger.debug("Polling hello from netconf, attempt {}, capabilities {}", attempt, currentCapabilities); + + try { + netconfClient.close(); + } catch (IOException e) { + throw new RuntimeException("Error closing temporary client " + netconfClient); + } + + Thread.sleep(delay); + } + + throw new RuntimeException("Netconf server did not provide required capabilities " + expectedCaps + + " in time, provided capabilities " + currentCapabilities); + + } + + private boolean isSubset(Set currentCapabilities, Set expectedCaps) { + for (String exCap : expectedCaps) { + if (currentCapabilities.contains(exCap) == false) + return false; + } + return true; + } + + private void registerAsJMXListener() { + try { + mbeanServer.addNotificationListener(on, this, null, null); + } catch (InstanceNotFoundException | IOException e) { + throw new RuntimeException("Cannot register as JMX listener to netconf", e); + } + } + + @Override + public void handleNotification(Notification notification, Object handback) { + if (notification instanceof NetconfJMXNotification == false) + return; + + // Socket should not be closed at this point + // Activator unregisters this as JMX listener before close is called + + logger.debug("Received notification {}", notification); + if (notification instanceof CommitJMXNotification) { + try { + handleAfterCommitNotification((CommitJMXNotification) notification); + } catch (Exception e) { + // TODO: notificationBroadcast support logs only DEBUG + logger.warn("Exception occured during notification handling: ", e); + throw e; + } + } else + throw new IllegalStateException("Unknown config registry notification type " + notification); + } + + private void handleAfterCommitNotification(final CommitJMXNotification notification) { + try { + final XmlElement configElement = XmlElement.fromDomElement(notification.getConfigSnapshot()); + persister.persistConfig(new Persister.ConfigSnapshotHolder() { + @Override + public String getConfigSnapshot() { + return XmlUtil.toString(configElement.getDomElement()); + } + + @Override + public Set getCapabilities() { + return notification.getCapabilities(); + } + }); + logger.debug("Configuration persisted successfully"); + } catch (IOException e) { + throw new RuntimeException("Unable to persist configuration snapshot", e); + } + } + + private Optional loadLastConfig() { + Optional maybeConfigElement; + try { + maybeConfigElement = persister.loadLastConfig(); + } catch (IOException e) { + throw new RuntimeException("Unable to load configuration", e); + } + return maybeConfigElement; + } + + private synchronized void pushLastConfig(Element persistedConfig) { + StringBuilder response = new StringBuilder("editConfig response = {"); + + Element configElement = persistedConfig; + NetconfMessage message = createEditConfigMessage(configElement, "/netconfOp/editConfig.xml"); + NetconfMessage responseMessage = netconfClient.sendMessage(message); + + XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument()); + Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY)); + element = element.getOnlyChildElement(); + + checkIsOk(element, responseMessage); + response.append(XmlUtil.toString(responseMessage.getDocument())); + response.append("}"); + responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml")); + + element = XmlElement.fromDomDocument(responseMessage.getDocument()); + Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY)); + element = element.getOnlyChildElement(); + + checkIsOk(element, responseMessage); + response.append("commit response = {"); + response.append(XmlUtil.toString(responseMessage.getDocument())); + response.append("}"); + logger.debug("Last configuration loaded successfully"); + } + + private void checkIsOk(XmlElement element, NetconfMessage responseMessage) { + if (element.getName().equals(XmlNetconfConstants.OK)) { + return; + } else { + if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) { + logger.warn("Can not load last configuration, operation failed"); + throw new IllegalStateException("Can not load last configuration, operation failed: " + + XmlUtil.toString(responseMessage.getDocument())); + } + logger.warn("Can not load last configuration. Operation failed."); + throw new IllegalStateException("Can not load last configuration. Operation failed: " + + XmlUtil.toString(responseMessage.getDocument())); + } + } + + private NetconfMessage createEditConfigMessage(Element dataElement, String editConfigResourcename) { + try (InputStream stream = getClass().getResourceAsStream(editConfigResourcename)) { + Preconditions.checkNotNull(stream, "Unable to load resource " + editConfigResourcename); + + Document doc = XmlUtil.readXmlToDocument(stream); + + doc.getDocumentElement(); + XmlElement editConfigElement = XmlElement.fromDomDocument(doc).getOnlyChildElement(); + XmlElement configWrapper = editConfigElement.getOnlyChildElement(XmlNetconfConstants.CONFIG_KEY); + editConfigElement.getDomElement().removeChild(configWrapper.getDomElement()); + for (XmlElement el : XmlElement.fromDomElement(dataElement).getChildElements()) { + configWrapper.appendChild((Element) doc.importNode(el.getDomElement(), true)); + } + editConfigElement.appendChild(configWrapper.getDomElement()); + return new NetconfMessage(doc); + } catch (IOException | SAXException e) { + throw new RuntimeException("Unable to parse message from resources " + editConfigResourcename, e); + } + } + + private NetconfMessage getNetconfMessageFromResource(String resource) { + try (InputStream stream = getClass().getResourceAsStream(resource)) { + Preconditions.checkNotNull(stream, "Unable to load resource " + resource); + return new NetconfMessage(XmlUtil.readXmlToDocument(stream)); + } catch (SAXException | IOException e) { + throw new RuntimeException("Unable to parse message from resources " + resource, e); + } + } + + @Override + public synchronized void close() { + // TODO persister is received from constructor, should not be closed + // here + try { + persister.close(); + } catch (Exception e) { + logger.warn("Unable to close config persister {}", persister, e); + } + + if (netconfClient != null) { + try { + netconfClient.close(); + } catch (Exception e) { + logger.warn("Unable to close connection to netconf {}", netconfClient, e); + } + } + + // unregister from JMX + try { + if (mbeanServer.isRegistered(on)) { + mbeanServer.removeNotificationListener(on, this); + } + } catch (Exception e) { + logger.warn("Unable to unregister {} as listener for {}", this, on, e); + } + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java new file mode 100644 index 0000000000..08b0d1a511 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/NoOpStorageAdapter.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.persist.impl; + +import com.google.common.base.Optional; +import org.opendaylight.controller.config.persist.api.storage.StorageAdapter; +import org.opendaylight.controller.config.stat.ConfigProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class NoOpStorageAdapter implements StorageAdapter { + private static final Logger logger = LoggerFactory.getLogger(NoOpStorageAdapter.class); + + @Override + public void setProperties(ConfigProvider configProvider) { + logger.debug("setProperties called with {}", configProvider); + } + + @Override + public void persistConfig(ConfigSnapshotHolder holder) throws IOException { + logger.debug("persistConfig called with {}", holder); + } + + @Override + public Optional loadLastConfig() throws IOException { + logger.debug("loadLastConfig called"); + return Optional.absent(); + } + + @Override + public void close() throws IOException { + logger.debug("close called"); + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java new file mode 100644 index 0000000000..03892f0da7 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterImpl.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.persist.impl; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import org.opendaylight.controller.config.persist.api.Persister; +import org.opendaylight.controller.config.persist.api.storage.StorageAdapter; +import org.opendaylight.controller.config.stat.ConfigProvider; + +import java.io.IOException; + +/** + * {@link Persister} implementation that delegates persisting functionality to + * underlying {@link Persister} called Storage Adapter. + * + * Storage adapters are low level persisters that do the heavy lifting for this + * class. Instances of storage adapters can be injected directly via constructor + * or instantiated from a full name of its class provided in a properties file. + * + * Name of storage adapter class should be located under + * {@link #STORAGE_ADAPTER_CLASS_PROP} key. + */ +public final class PersisterImpl implements Persister { + + public static final String STORAGE_ADAPTER_CLASS_PROP = "netconf.config.persister.storageAdapterClass"; + private final StorageAdapter storage; + + public PersisterImpl(StorageAdapter storage) { + this.storage = storage; + } + + public static Optional createFromProperties(ConfigProvider configProvider) { + String storageAdapterClass = configProvider.getProperty(STORAGE_ADAPTER_CLASS_PROP); + StorageAdapter storage; + if (storageAdapterClass == null || storageAdapterClass.equals("")) { + return Optional.absent(); + } + + try { + storage = StorageAdapter.class.cast(resolveClass(storageAdapterClass, StorageAdapter.class).newInstance()); + storage.setProperties(configProvider); + + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + throw new IllegalArgumentException("Unable to instantiate storage adapter from " + storageAdapterClass, e); + } + return Optional.of(new PersisterImpl(storage)); + } + + private static Class resolveClass(String storageAdapterClass, Class baseType) throws ClassNotFoundException { + Class clazz = Class.forName(storageAdapterClass); + + if (!isImplemented(baseType, clazz)) + throw new IllegalArgumentException("Storage adapter " + clazz + " has to implement " + baseType); + return clazz; + } + + private static boolean isImplemented(Class expectedIface, Class byClazz) { + for (Class iface : byClazz.getInterfaces()) { + if (iface.equals(expectedIface)) + return true; + } + return false; + } + + @Override + public void persistConfig(ConfigSnapshotHolder holder) throws IOException { + storage.persistConfig(holder); + } + + @Override + public Optional loadLastConfig() throws IOException { + return storage.loadLastConfig(); + } + + @VisibleForTesting + StorageAdapter getStorage() { + return storage; + } + + @Override + public void close() throws IOException { + storage.close(); + } + + @Override + public String toString() { + return "PersisterImpl [storage=" + storage + "]"; + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java new file mode 100644 index 0000000000..b17309123c --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.persist.impl; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; + +public final class Util { + + public static ScheduledExecutorService getExecutorServiceWithThreadName(final String threadNamePrefix, + int threadCount) { + return Executors.newScheduledThreadPool(threadCount, new ThreadFactory() { + + private int i = 1; + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setName(threadNamePrefix + ":" + i++); + return thread; + } + }); + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java new file mode 100644 index 0000000000..cf1b0af454 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.persist.impl.osgi; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler; +import org.opendaylight.controller.netconf.persist.impl.NoOpStorageAdapter; +import org.opendaylight.controller.netconf.persist.impl.PersisterImpl; +import org.opendaylight.controller.config.stat.ConfigProvider; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.TLSConfiguration; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.management.MBeanServer; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; + +public class ConfigPersisterActivator implements BundleActivator { + + private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class); + + private final static MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer(); + + private ConfigPersisterNotificationHandler configPersisterNotificationHandler; + + private Thread initializationThread; + + @Override + public void start(BundleContext context) throws Exception { + logger.debug("ConfigPersister activator started"); + + ConfigProvider configProvider = new ConfigProvider.ConfigProviderImpl(context); + Optional maybePersister = PersisterImpl.createFromProperties(configProvider); + if (maybePersister.isPresent() == false) { + throw new IllegalStateException("No persister is defined in " + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP + + " property. For noop persister use " + NoOpStorageAdapter.class.getCanonicalName() + + " . Persister is not operational"); + } + + Optional maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(configProvider); + Optional maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(configProvider); + + InetSocketAddress address; + if (maybeTLSConfiguration.isPresent()) { + throw new UnsupportedOperationException("TLS is currently not supported for persister"); + } else if (maybeTCPAddress.isPresent()) { + address = maybeTCPAddress.get(); + } else { + throw new IllegalStateException("Netconf is not configured, persister is not operational"); + } + + PersisterImpl persister = maybePersister.get(); + configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(persister, address, + platformMBeanServer); + Runnable initializationRunnable = new Runnable() { + @Override + public void run() { + try { + configPersisterNotificationHandler.init(); + } catch (InterruptedException e) { + logger.info("Interrupted while waiting for netconf connection"); + } + } + }; + initializationThread = new Thread(initializationRunnable, "ConfigPersister-registrator"); + initializationThread.start(); + } + + @Override + public void stop(BundleContext context) throws Exception { + initializationThread.interrupt(); + configPersisterNotificationHandler.close(); + } +} diff --git a/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml new file mode 100644 index 0000000000..4e34591284 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/client_hello.xml @@ -0,0 +1,5 @@ + + + urn:ietf:params:netconf:base:1.0 + + diff --git a/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/commit.xml b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/commit.xml new file mode 100644 index 0000000000..ae1f6e87fa --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/commit.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/editConfig.xml b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/editConfig.xml new file mode 100644 index 0000000000..8ec1c8c069 --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/main/resources/netconfOp/editConfig.xml @@ -0,0 +1,12 @@ + + + + + + replace + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java new file mode 100644 index 0000000000..36e88251dd --- /dev/null +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterImplTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.persist.impl; + +import com.google.common.base.Optional; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.persist.api.Persister; +import org.opendaylight.controller.config.persist.api.storage.StorageAdapter; +import org.opendaylight.controller.config.persist.storage.file.FileStorageAdapter; +import org.opendaylight.controller.config.stat.ConfigProvider; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.*; +import static org.junit.matchers.JUnitMatchers.containsString; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; + +public class PersisterImplTest { + @Mock + ConfigProvider mockedConfigProvider; + + @Before + public void setUpMocks() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testFromProperties() throws Exception { + doReturn(MockAdapter.class.getName()).when(mockedConfigProvider).getProperty( + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP); + + PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedConfigProvider).get(); + persisterImpl.persistConfig(null); + persisterImpl.loadLastConfig(); + persisterImpl.persistConfig(null); + persisterImpl.loadLastConfig(); + + assertEquals(2, MockAdapter.persist); + assertEquals(2, MockAdapter.load); + assertEquals(1, MockAdapter.props); + } + + @Test + public void testFromProperties2() throws Exception { + mockedConfigProvider = mock(ConfigProvider.class); + doReturn(FileStorageAdapter.class.getName()).when(mockedConfigProvider).getProperty( + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP); + doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when( + mockedConfigProvider).getProperty(FileStorageAdapter.FILE_STORAGE_PROP); + doReturn("mockedConfigProvider").when(mockedConfigProvider).toString(); + doReturn(null).when(mockedConfigProvider).getProperty("numberOfBackups"); + + PersisterImpl persisterImpl = PersisterImpl.createFromProperties(mockedConfigProvider).get(); + assertTrue(persisterImpl.getStorage() instanceof FileStorageAdapter); + } + + @Test + public void testFromProperties3() throws Exception { + mockedConfigProvider = mock(ConfigProvider.class); + doReturn(FileStorageAdapter.class.getName()).when(mockedConfigProvider).getProperty( + PersisterImpl.STORAGE_ADAPTER_CLASS_PROP); + doReturn("target" + File.separator + "generated-test-sources" + File.separator + "testFile").when( + mockedConfigProvider).getProperty(FileStorageAdapter.FILE_STORAGE_PROP); + doReturn("mockedConfigProvider").when(mockedConfigProvider).toString(); + doReturn("0").when(mockedConfigProvider).getProperty("numberOfBackups"); + try { + PersisterImpl.createFromProperties(mockedConfigProvider).get(); + fail(); + } catch (RuntimeException e) { + assertThat( + e.getMessage(), + containsString("numberOfBackups property should be either set to positive value, or ommited. Can not be set to 0.")); + } + } + + @Test + public void test() throws Exception { + StorageAdapter storage = mock(StorageAdapter.class); + doReturn(null).when(storage).loadLastConfig(); + doNothing().when(storage).persistConfig(any(Persister.ConfigSnapshotHolder.class)); + PersisterImpl persister = new PersisterImpl(storage); + persister.loadLastConfig(); + persister.persistConfig(null); + + verify(storage).loadLastConfig(); + verify(storage).persistConfig(any(Persister.ConfigSnapshotHolder.class)); + } + + public static class MockAdapter implements StorageAdapter { + + static int persist = 0; + + @Override + public void persistConfig(ConfigSnapshotHolder holder) throws IOException { + persist++; + } + + static int load = 0; + + @Override + public Optional loadLastConfig() throws IOException { + load++; + return null;// ? + } + + static int props = 0; + + @Override + public void setProperties(ConfigProvider configProvider) { + props++; + } + + @Override + public void close() throws IOException { + // TODO Auto-generated method stub + + } + + } + +} diff --git a/opendaylight/netconf/netconf-api/pom.xml b/opendaylight/netconf/netconf-api/pom.xml new file mode 100644 index 0000000000..57e77cc945 --- /dev/null +++ b/opendaylight/netconf/netconf-api/pom.xml @@ -0,0 +1,54 @@ + + + + + netconf-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + + 4.0.0 + netconf-api + ${project.artifactId} + bundle + + + + org.opendaylight.controller + config-api + 0.2.1-SNAPSHOT + + + org.opendaylight.bgpcep + framework + + + + + + + org.apache.felix + maven-bundle-plugin + + + + + + javax.management, + org.opendaylight.controller.config.api.jmx, + org.opendaylight.protocol.framework, + org.w3c.dom + + + org.opendaylight.controller.netconf.api, + org.opendaylight.controller.netconf.api.jmx, + + + + + + + + + diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDeserializerException.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDeserializerException.java new file mode 100644 index 0000000000..f42db6938d --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDeserializerException.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +import org.opendaylight.protocol.framework.DeserializerException; + +/** + * This exception is thrown by + * {@link NetconfSessionListener#onMessage(NetconfMessage)} to indicate fatal + * communication problem after which the session should be closed. + */ +public class NetconfDeserializerException extends DeserializerException { + private static final long serialVersionUID = 1L; + + public NetconfDeserializerException(final String message) { + super(message); + } + + public NetconfDeserializerException(final String message, final Exception e) { + super(message, e); + } +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java new file mode 100644 index 0000000000..1107572df1 --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfDocumentedException.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +import java.util.Collections; +import java.util.Map; + +import org.opendaylight.protocol.framework.DocumentedException; + +/** + * Checked exception to communicate an error that needs to be sent to the + * netconf client. + */ +public class NetconfDocumentedException extends DocumentedException { + + private static final long serialVersionUID = 1L; + + public enum ErrorType { + transport, rpc, protocol, application; + + public String getTagValue() { + return name(); + } + } + + public enum ErrorTag { + missing_attribute("missing-attribute"), unknown_element("unknown-element"), operation_not_supported( + "operation-not-supported"), bad_attribute("bad-attribute"), data_missing("data-missing"), operation_failed( + "operation-failed"), invalid_value("invalid-value"), malformed_message("malformed-message"); + + private final String tagValue; + + ErrorTag(final String tagValue) { + this.tagValue = tagValue; + } + + public String getTagValue() { + return this.tagValue; + } + } + + public enum ErrorSeverity { + error, warning; + + public String getTagValue() { + return name(); + } + } + + private final ErrorType errorType; + private final ErrorTag errorTag; + private final ErrorSeverity errorSeverity; + private final Map errorInfo; + + public NetconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag, + final ErrorSeverity errorSeverity) { + this(message, errorType, errorTag, errorSeverity, Collections. emptyMap()); + } + + public NetconfDocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag, + final ErrorSeverity errorSeverity, final Map errorInfo) { + super(message); + this.errorType = errorType; + this.errorTag = errorTag; + this.errorSeverity = errorSeverity; + this.errorInfo = errorInfo; + } + + public NetconfDocumentedException(final String message, final Exception cause, final ErrorType errorType, + final ErrorTag errorTag, final ErrorSeverity errorSeverity) { + this(message, cause, errorType, errorTag, errorSeverity, Collections. emptyMap()); + } + + public NetconfDocumentedException(final String message, final Exception cause, final ErrorType errorType, + final ErrorTag errorTag, final ErrorSeverity errorSeverity, final Map errorInfo) { + super(message, cause); + this.errorType = errorType; + this.errorTag = errorTag; + this.errorSeverity = errorSeverity; + this.errorInfo = errorInfo; + } + + public ErrorType getErrorType() { + return this.errorType; + } + + public ErrorTag getErrorTag() { + return this.errorTag; + } + + public ErrorSeverity getErrorSeverity() { + return this.errorSeverity; + } + + public Map getErrorInfo() { + return this.errorInfo; + } + + @Override + public String toString() { + return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType + + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo=" + + this.errorInfo + '}'; + } +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java new file mode 100644 index 0000000000..33d41b0470 --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfMessage.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +import org.opendaylight.protocol.framework.ProtocolMessage; +import org.w3c.dom.Document; + +/** + * NetconfMessage represents a wrapper around org.w3c.dom.Document. Needed for + * implementing ProtocolMessage interface. + */ +public final class NetconfMessage implements ProtocolMessage { + + private static final long serialVersionUID = 462175939836367285L; + + private final Document doc; + + public NetconfMessage(final Document doc) { + this.doc = doc; + } + + public Document getDocument() { + return this.doc; + } +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfOperationRouter.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfOperationRouter.java new file mode 100644 index 0000000000..49ca0c0106 --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfOperationRouter.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +import org.w3c.dom.Document; + +public interface NetconfOperationRouter extends AutoCloseable { + + Document onNetconfMessage(Document message) throws NetconfDocumentedException; + + @Override + void close(); + +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfServerSessionPreferences.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfServerSessionPreferences.java new file mode 100644 index 0000000000..d56213cf16 --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfServerSessionPreferences.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +/** + * The only input for the start of a NETCONF session is hello-message. + */ +public final class NetconfServerSessionPreferences extends NetconfSessionPreferences { + + private final long sessionId; + + public NetconfServerSessionPreferences(final NetconfMessage helloMessage, long sessionId) { + super(helloMessage); + this.sessionId = sessionId; + } + + public long getSessionId() { + return sessionId; + } +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java new file mode 100644 index 0000000000..a61d6938f6 --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSession.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +import org.opendaylight.protocol.framework.AbstractProtocolSession; + +public abstract class NetconfSession extends AbstractProtocolSession { + + public abstract void sendMessage(NetconfMessage netconfMessage); +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionListener.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionListener.java new file mode 100644 index 0000000000..54cb471604 --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionListener.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +import org.opendaylight.protocol.framework.SessionListener; + +public interface NetconfSessionListener extends + SessionListener { + +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionPreferences.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionPreferences.java new file mode 100644 index 0000000000..1ec46c8d6f --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfSessionPreferences.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +public class NetconfSessionPreferences { + protected final NetconfMessage helloMessage; + + public NetconfSessionPreferences(final NetconfMessage helloMessage) { + this.helloMessage = helloMessage; + } + + /** + * @return the helloMessage + */ + public NetconfMessage getHelloMessage() { + return this.helloMessage; + } +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfTerminationReason.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfTerminationReason.java new file mode 100644 index 0000000000..9de3071060 --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/NetconfTerminationReason.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api; + +import org.opendaylight.protocol.framework.TerminationReason; + +public class NetconfTerminationReason implements TerminationReason { + + private final String reason; + + public NetconfTerminationReason(String reason) { + this.reason = reason; + } + + @Override + public String getErrorMessage() { + return reason; + } +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/CommitJMXNotification.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/CommitJMXNotification.java new file mode 100644 index 0000000000..0c4174000f --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/CommitJMXNotification.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api.jmx; + +import org.w3c.dom.Element; + +import javax.management.NotificationBroadcasterSupport; +import java.util.Set; + +public class CommitJMXNotification extends NetconfJMXNotification { + + private final Element configSnapshot; + + private static final String afterCommitMessageTemplate = "Commit successful: %s"; + private final Set capabilities; + + CommitJMXNotification(NotificationBroadcasterSupport source, String message, Element cfgSnapshot, + Set capabilities) { + super(TransactionProviderJMXNotificationType.commit, source, String.format(afterCommitMessageTemplate, message)); + this.configSnapshot = cfgSnapshot; + this.capabilities = capabilities; + } + + public Element getConfigSnapshot() { + return configSnapshot; + } + + public Set getCapabilities() { + return capabilities; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("CommitJMXNotification{"); + sb.append("configSnapshot=").append(configSnapshot); + sb.append(", capabilities=").append(getCapabilities()); + sb.append('}'); + return sb.toString(); + } + + /** + * + */ + private static final long serialVersionUID = -8587623362011695514L; + +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/DefaultCommitOperationMXBean.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/DefaultCommitOperationMXBean.java new file mode 100644 index 0000000000..a170d2905f --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/DefaultCommitOperationMXBean.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api.jmx; + +import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; + +import javax.management.ObjectName; + +public interface DefaultCommitOperationMXBean { + + static String typeName = "NetconfNotificationProvider"; + public static ObjectName objectName = ObjectNameUtil.createONWithDomainAndType(typeName); + +} diff --git a/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/NetconfJMXNotification.java b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/NetconfJMXNotification.java new file mode 100644 index 0000000000..5aa4bffce3 --- /dev/null +++ b/opendaylight/netconf/netconf-api/src/main/java/org/opendaylight/controller/netconf/api/jmx/NetconfJMXNotification.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.api.jmx; + +import java.util.Set; + +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; + +import org.w3c.dom.Element; + +public abstract class NetconfJMXNotification extends Notification { + + /** + * + */ + private static final long serialVersionUID = 6754474563863772845L; + + private static long sequenceNumber = 1; + + private final TransactionProviderJMXNotificationType type; + + protected NetconfJMXNotification(TransactionProviderJMXNotificationType type, + NotificationBroadcasterSupport source, String message) { + super(type.toString(), source, sequenceNumber++, System.nanoTime(), message); + this.type = type; + } + + @Override + public String toString() { + return "TransactionProviderJMXNotification [type=" + type + "]"; + } + + /** + * Sends this notification using source that created it + */ + public void send() { + ((NotificationBroadcasterSupport) getSource()).sendNotification(this); + } + + /** + * Creates notification about successful commit execution. + * + * Intended for config-persister. + * + * @param transactionName + * @param cfgSnapshot + */ + public static CommitJMXNotification afterCommit(NotificationBroadcasterSupport source, String message, + Element cfgSnapshot, Set capabilities) { + return new CommitJMXNotification(source, message, cfgSnapshot, capabilities); + } + + static enum TransactionProviderJMXNotificationType { + commit; + } + +} diff --git a/opendaylight/netconf/netconf-client/pom.xml b/opendaylight/netconf/netconf-client/pom.xml new file mode 100644 index 0000000000..ac949f5445 --- /dev/null +++ b/opendaylight/netconf/netconf-client/pom.xml @@ -0,0 +1,77 @@ + + 4.0.0 + + + netconf-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + + netconf-client + ${project.artifactId} + bundle + + + + + ${project.groupId} + netconf-util + ${project.version} + + + ${project.groupId} + netconf-api + ${project.version} + + + org.opendaylight.bgpcep + framework + + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + + + + + org.apache.felix + maven-bundle-plugin + + + + org.opendaylight.controller.netconf.client, + + + com.google.common.base, + com.google.common.collect, + io.netty.channel, + io.netty.channel.socket, + io.netty.util, + io.netty.util.concurrent, + javax.annotation, + javax.net.ssl, + javax.xml.namespace, + javax.xml.parsers, + javax.xml.xpath, + org.opendaylight.controller.netconf.api, + org.opendaylight.controller.netconf.util, + org.opendaylight.controller.netconf.util.xml, + org.opendaylight.protocol.framework, + org.slf4j, + org.w3c.dom, + org.xml.sax + + + + + + + + diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java new file mode 100644 index 0000000000..b5a06ca0ab --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.client; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GlobalEventExecutor; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.protocol.framework.NeverReconnectStrategy; +import org.opendaylight.protocol.framework.ReconnectStrategy; +import org.opendaylight.protocol.framework.TimedReconnectStrategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLContext; +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + +public class NetconfClient implements Closeable { + + private static final Logger logger = LoggerFactory.getLogger(NetconfClient.class); + + public static final int DEFAULT_CONNECT_TIMEOUT = 5000; + private final NetconfClientDispatcher dispatch; + private final String label; + private final NetconfClientSession clientSession; + private final NetconfClientSessionListener sessionListener; + private final long sessionId; + private final InetSocketAddress address; + + // TODO test reconnecting constructor + public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectionAttempts, + int attemptMsTimeout) { + this(clientLabelForLogging, address, getReconnectStrategy(connectionAttempts, attemptMsTimeout), Optional + . absent()); + } + + private NetconfClient(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat, + Optional maybeSSLContext) { + this.label = clientLabelForLogging; + dispatch = new NetconfClientDispatcher(maybeSSLContext); + + sessionListener = new NetconfClientSessionListener(); + Future clientFuture = dispatch.createClient(address, sessionListener, strat); + this.address = address; + clientSession = get(clientFuture); + this.sessionId = clientSession.getSessionId(); + } + + private NetconfClientSession get(Future clientFuture) { + try { + return clientFuture.get(); + } catch (InterruptedException | CancellationException e) { + throw new RuntimeException("Netconf client interrupted", e); + } catch (ExecutionException e) { + throw new IllegalStateException("Unable to create netconf client", e); + } + } + + public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectTimeoutMs, + Optional maybeSSLContext) { + this(clientLabelForLogging, address, + new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, connectTimeoutMs), maybeSSLContext); + } + + public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectTimeoutMs) { + this(clientLabelForLogging, address, + new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, connectTimeoutMs), Optional + . absent()); + } + + public NetconfClient(String clientLabelForLogging, InetSocketAddress address) { + this(clientLabelForLogging, address, new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, + DEFAULT_CONNECT_TIMEOUT), Optional. absent()); + } + + public NetconfClient(String clientLabelForLogging, InetSocketAddress address, Optional maybeSSLContext) { + this(clientLabelForLogging, address, new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, + DEFAULT_CONNECT_TIMEOUT), maybeSSLContext); + } + + public NetconfMessage sendMessage(NetconfMessage message) { + return sendMessage(message, 5, 1000); + } + + public NetconfMessage sendMessage(NetconfMessage message, int attempts, int attemptMsDelay) { + Preconditions.checkState(clientSession.isUp(), "Session was not up yet"); + clientSession.sendMessage(message); + try { + return sessionListener.getLastMessage(attempts, attemptMsDelay); + } catch (InterruptedException e) { + throw new RuntimeException(this + " Cannot read message from " + address, e); + } catch (IllegalStateException e) { + throw new IllegalStateException(this + " Cannot read message from " + address, e); + } + } + + @Override + public void close() throws IOException { + clientSession.close(); + dispatch.close(); + } + + private static ReconnectStrategy getReconnectStrategy(int connectionAttempts, int attemptMsTimeout) { + return new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, attemptMsTimeout, 1000, 1.0, null, + Long.valueOf(connectionAttempts), null); + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("NetconfClient{"); + sb.append("label=").append(label); + sb.append(", sessionId=").append(sessionId); + sb.append('}'); + return sb.toString(); + } + + public long getSessionId() { + return sessionId; + } + + public Set getCapabilities() { + Preconditions.checkState(clientSession != null, "Client was not initialized successfully"); + return Sets.newHashSet(clientSession.getServerCapabilities()); + } +} diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java new file mode 100644 index 0000000000..4df8235441 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientDispatcher.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.client; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.netty.channel.socket.SocketChannel; +import io.netty.util.HashedWheelTimer; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.Promise; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfSession; +import org.opendaylight.controller.netconf.api.NetconfTerminationReason; +import org.opendaylight.controller.netconf.util.AbstractChannelInitializer; +import org.opendaylight.protocol.framework.AbstractDispatcher; +import org.opendaylight.protocol.framework.ReconnectStrategy; +import org.opendaylight.protocol.framework.SessionListener; +import org.opendaylight.protocol.framework.SessionListenerFactory; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import java.net.InetSocketAddress; + +public class NetconfClientDispatcher extends AbstractDispatcher { + + private final Optional maybeContext; + private final NetconfClientSessionNegotiatorFactory negotatorFactory; + + public NetconfClientDispatcher(final Optional maybeContext) { + this.maybeContext = Preconditions.checkNotNull(maybeContext); + this.negotatorFactory = new NetconfClientSessionNegotiatorFactory(new HashedWheelTimer()); + } + + public Future createClient(InetSocketAddress address, + final NetconfClientSessionListener sessionListener, ReconnectStrategy strat) { + + return super.createClient(address, strat, new PipelineInitializer() { + + @Override + public void initializeChannel(final SocketChannel ch, final Promise promise) { + initialize(ch, promise); + } + + private void initialize(SocketChannel ch, Promise promise) { + new ClientChannelInitializer(maybeContext, negotatorFactory, sessionListener).initialize(ch, promise); + } + }); + } + + private static class ClientChannelInitializer extends AbstractChannelInitializer { + + private final NetconfClientSessionNegotiatorFactory negotiatorFactory; + private final NetconfClientSessionListener sessionListener; + + private ClientChannelInitializer(Optional maybeContext, + NetconfClientSessionNegotiatorFactory negotiatorFactory, NetconfClientSessionListener sessionListener) { + super(maybeContext); + this.negotiatorFactory = negotiatorFactory; + this.sessionListener = sessionListener; + } + + @Override + protected void initializeAfterDecoder(SocketChannel ch, Promise promise) { + ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(new SessionListenerFactory() { + @Override + public SessionListener getSessionListener() { + return sessionListener; + } + }, ch, promise)); + } + + @Override + protected void initSslEngine(SSLEngine sslEngine) { + sslEngine.setUseClientMode(true); + } + } + +} diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java new file mode 100644 index 0000000000..f0180cf78d --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSession.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.client; + +import io.netty.channel.Channel; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfSession; +import org.opendaylight.controller.netconf.api.NetconfTerminationReason; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.SessionListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collection; + +public class NetconfClientSession extends NetconfSession { + + private final SessionListener sessionListener; + private final long sessionId; + private final Channel channel; + + private static final Logger logger = LoggerFactory.getLogger(NetconfClientSession.class); + private final Collection capabilities; + private boolean up; + + public NetconfClientSession(SessionListener sessionListener, Channel channel, long sessionId, + Collection capabilities) { + this.sessionListener = sessionListener; + this.channel = channel; + this.sessionId = sessionId; + this.capabilities = capabilities; + logger.debug("Client Session {} created", toString()); + } + + @Override + public void close() { + channel.close(); + sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Client Session closed")); + } + + @Override + protected void handleMessage(NetconfMessage netconfMessage) { + logger.debug("Client Session {} received message {}", toString(), + XmlUtil.toString(netconfMessage.getDocument())); + sessionListener.onMessage(this, netconfMessage); + } + + @Override + public void sendMessage(NetconfMessage netconfMessage) { + channel.writeAndFlush(netconfMessage); + } + + @Override + protected void endOfInput() { + logger.debug("Client Session {} end of input detected while session was in state {}", toString(), isUp() ? "up" + : "initialized"); + if (isUp()) { + this.sessionListener.onSessionDown(this, new IOException("End of input detected. Close the session.")); + } + } + + @Override + protected void sessionUp() { + logger.debug("Client Session {} up", toString()); + sessionListener.onSessionUp(this); + this.up = true; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("ClientNetconfSession{"); + sb.append("sessionId=").append(sessionId); + sb.append('}'); + return sb.toString(); + } + + public boolean isUp() { + return up; + } + + public long getSessionId() { + return sessionId; + } + + public Collection getServerCapabilities() { + return capabilities; + } +} diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionListener.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionListener.java new file mode 100644 index 0000000000..d3c1b22c84 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionListener.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.client; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfTerminationReason; +import org.opendaylight.protocol.framework.SessionListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +public class NetconfClientSessionListener implements + SessionListener { + + private static final Logger logger = LoggerFactory.getLogger(NetconfClientSessionListener.class); + private AtomicBoolean up = new AtomicBoolean(false); + + @Override + public void onSessionUp(NetconfClientSession clientSession) { + up.set(true); + } + + @Override + public void onSessionDown(NetconfClientSession clientSession, Exception e) { + logger.debug("Client Session {} down, reason: {}", clientSession, e.getMessage()); + up.set(false); + } + + @Override + public void onSessionTerminated(NetconfClientSession clientSession, + NetconfTerminationReason netconfTerminationReason) { + logger.debug("Client Session {} terminated, reason: {}", clientSession, + netconfTerminationReason.getErrorMessage()); + up.set(false); + } + + @Override + public synchronized void onMessage(NetconfClientSession session, NetconfMessage message) { + synchronized (messages) { + this.messages.add(message); + } + } + + private int lastReadMessage = -1; + private List messages = Lists.newArrayList(); + + public NetconfMessage getLastMessage(int attempts, int attemptMsDelay) throws InterruptedException { + Preconditions.checkState(up.get(), "Session was not up yet"); + + for (int i = 0; i < attempts; i++) { + synchronized (messages) { + if (messages.size() - 1 > lastReadMessage) { + lastReadMessage++; + return messages.get(lastReadMessage); + } + } + + if (up.get() == false) + throw new IllegalStateException("Session ended while trying to read message"); + Thread.sleep(attemptMsDelay); + } + + throw new IllegalStateException("No netconf message to read"); + } +} diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java new file mode 100644 index 0000000000..17a55c52bc --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiator.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.client; + +import com.google.common.base.Function; +import com.google.common.collect.Collections2; +import io.netty.channel.Channel; +import io.netty.util.Timer; +import io.netty.util.concurrent.Promise; +import org.opendaylight.controller.netconf.api.NetconfSessionPreferences; +import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator; +import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.SessionListener; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import javax.annotation.Nullable; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import java.util.Collection; +import java.util.List; + +public class NetconfClientSessionNegotiator extends + AbstractNetconfSessionNegotiator { + + protected NetconfClientSessionNegotiator(NetconfSessionPreferences sessionPreferences, + Promise promise, Channel channel, Timer timer, SessionListener sessionListener) { + super(sessionPreferences, promise, channel, timer, sessionListener); + } + + private static Collection getCapabilities(Document doc) { + XmlElement responseElement = XmlElement.fromDomDocument(doc); + XmlElement capabilitiesElement = responseElement + .getOnlyChildElementWithSameNamespace(XmlNetconfConstants.CAPABILITIES); + List caps = capabilitiesElement.getChildElements(XmlNetconfConstants.CAPABILITY); + return Collections2.transform(caps, new Function() { + + @Nullable + @Override + public String apply(@Nullable XmlElement input) { + return input.getTextContent(); + } + }); + } + + private static final XPathExpression sessionIdXPath = XMLNetconfUtil + .compileXPath("/netconf:hello/netconf:session-id"); + + private long extractSessionId(Document doc) { + final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, doc, XPathConstants.NODE); + String textContent = sessionIdNode.getTextContent(); + if (textContent == null || textContent.equals("")) { + throw new IllegalStateException("Session id not received from server"); + } + + return Long.valueOf(textContent); + } + + @Override + protected NetconfClientSession getSession(SessionListener sessionListener, Channel channel, Document doc) { + return new NetconfClientSession(sessionListener, channel, extractSessionId(doc), getCapabilities(doc)); + } +} diff --git a/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java new file mode 100644 index 0000000000..db0b953bdd --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClientSessionNegotiatorFactory.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.client; + +import com.google.common.base.Preconditions; +import io.netty.channel.Channel; +import io.netty.util.Timer; +import io.netty.util.concurrent.Promise; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfSessionPreferences; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.SessionListenerFactory; +import org.opendaylight.protocol.framework.SessionNegotiator; +import org.opendaylight.protocol.framework.SessionNegotiatorFactory; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.io.InputStream; + +public class NetconfClientSessionNegotiatorFactory implements SessionNegotiatorFactory { + + private final Timer timer; + + public NetconfClientSessionNegotiatorFactory(Timer timer) { + this.timer = timer; + } + + private static NetconfMessage loadHelloMessageTemplate() { + final String helloMessagePath = "/client_hello.xml"; + try (InputStream is = NetconfClientSessionNegotiatorFactory.class.getResourceAsStream(helloMessagePath)) { + Preconditions.checkState(is != null, "Input stream from %s was null", helloMessagePath); + return new NetconfMessage(XmlUtil.readXmlToDocument(is)); + } catch (SAXException | IOException e) { + throw new RuntimeException("Unable to load hello message", e); + } + } + + @Override + public SessionNegotiator getSessionNegotiator(SessionListenerFactory sessionListenerFactory, Channel channel, + Promise promise) { + // Hello message needs to be recreated every time + NetconfSessionPreferences proposal = new NetconfSessionPreferences(loadHelloMessageTemplate()); + return new NetconfClientSessionNegotiator(proposal, promise, channel, timer, + sessionListenerFactory.getSessionListener()); + } + +} diff --git a/opendaylight/netconf/netconf-client/src/main/resources/client_hello.xml b/opendaylight/netconf/netconf-client/src/main/resources/client_hello.xml new file mode 100644 index 0000000000..1d85b688e5 --- /dev/null +++ b/opendaylight/netconf/netconf-client/src/main/resources/client_hello.xml @@ -0,0 +1,5 @@ + + + urn:ietf:params:netconf:base:1.0 + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/pom.xml b/opendaylight/netconf/netconf-impl/pom.xml new file mode 100644 index 0000000000..b52e85bb21 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/pom.xml @@ -0,0 +1,148 @@ + + + + + netconf-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + + 4.0.0 + netconf-impl + ${project.artifactId} + bundle + + + + + + ${project.groupId} + netconf-api + ${project.version} + + + ${project.groupId} + netconf-util + ${project.version} + + + org.opendaylight.controller + config-util + 0.2.1-SNAPSHOT + + + ${project.groupId} + netconf-mapping-api + ${project.version} + + + + org.opendaylight.bgpcep + util + + + + org.opendaylight.bgpcep + framework + + + + org.osgi + org.osgi.core + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + + + org.opendaylight.bgpcep + mockito-configuration + test + + + commons-io + commons-io + 2.4 + test + + + + ${project.groupId} + + yang-store-api + test + + + xmlunit + xmlunit + 1.4 + test + + + ${project.groupId} + netconf-util + ${project.version} + test + test-jar + + + ${project.groupId} + netconf-client + ${project.version} + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.controller.netconf.impl.osgi.NetconfImplActivator + + com.google.common.base, + com.google.common.collect, + io.netty.channel, + io.netty.channel.socket, + io.netty.util, + io.netty.util.concurrent, + javax.management, + javax.net.ssl, + javax.xml.namespace, + javax.xml.xpath, + org.opendaylight.controller.netconf.api, + org.opendaylight.controller.netconf.api.jmx, + org.opendaylight.controller.netconf.mapping.api, + org.opendaylight.controller.netconf.util, + org.opendaylight.controller.netconf.util.mapping, + org.opendaylight.controller.netconf.util.osgi, + org.opendaylight.controller.netconf.util.xml, + org.opendaylight.protocol.framework, + org.osgi.framework, + org.osgi.util.tracker, + org.slf4j, + org.w3c.dom, + org.xml.sax, + org.opendaylight.controller.netconf.util.messages, + org.opendaylight.controller.config.stat + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java new file mode 100644 index 0000000000..1d2e039b29 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CapabilityProviderImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot; +import org.opendaylight.controller.netconf.mapping.api.Capability; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +public class CapabilityProviderImpl implements CapabilityProvider { + private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot; + private final Set capabilityURIs; + + public CapabilityProviderImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) { + this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot; + Map urisToCapabilitiesInternalMap = getCapabilitiesInternal(netconfOperationServiceSnapshot); + capabilityURIs = Collections.unmodifiableSet(urisToCapabilitiesInternalMap.keySet()); + } + + private static Map getCapabilitiesInternal( + NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) { + Map capabilityMap = Maps.newHashMap(); + + for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) { + final Set caps = netconfOperationService.getCapabilities(); + + for (Capability cap : caps) { + // TODO check for duplicates ? + capabilityMap.put(cap.getCapabilityUri(), cap); + } + } + + return capabilityMap; + } + + @Override + public synchronized String getSchemaForCapability(String moduleName, Optional revision) { + + Map> mappedModulesToRevisionToSchema = Maps.newHashMap(); + + for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) { + final Set caps = netconfOperationService.getCapabilities(); + + for (Capability cap : caps) { + if (cap.getModuleName().isPresent() == false) + continue; + if (cap.getRevision().isPresent() == false) + continue; + if (cap.getCapabilitySchema().isPresent() == false) + continue; + + final String currentModuleName = cap.getModuleName().get(); + Map revisionMap = mappedModulesToRevisionToSchema.get(currentModuleName); + if (revisionMap == null) { + revisionMap = Maps.newHashMap(); + mappedModulesToRevisionToSchema.put(currentModuleName, revisionMap); + } + + String currentRevision = cap.getRevision().get(); + revisionMap.put(currentRevision, cap.getCapabilitySchema().get()); + } + } + + Map revisionMapRequest = mappedModulesToRevisionToSchema.get(moduleName); + Preconditions.checkState(revisionMapRequest != null, "Capability for module %s not present, " + "" + + "available modules : %s", moduleName, capabilityURIs); + + if (revision.isPresent()) { + String schema = revisionMapRequest.get(revision.get()); + + Preconditions.checkState(schema != null, + "Capability for module %s:%s not present, available revisions for module: %s", moduleName, + revision.get(), revisionMapRequest.keySet()); + + return schema; + } else { + Preconditions.checkState(revisionMapRequest.size() == 1, + "Expected 1 capability for module %s, available revisions : %s", moduleName, + revisionMapRequest.keySet()); + return revisionMapRequest.values().iterator().next(); + } + } + + @Override + public synchronized Set getCapabilities() { + return capabilityURIs; + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java new file mode 100644 index 0000000000..305fb57424 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification; +import org.opendaylight.controller.netconf.api.jmx.DefaultCommitOperationMXBean; +import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; + +import javax.management.*; +import java.util.Set; + +public class DefaultCommitNotificationProducer extends NotificationBroadcasterSupport implements + DefaultCommitOperationMXBean, AutoCloseable { + + private static final Logger logger = LoggerFactory.getLogger(DefaultCommitNotificationProducer.class); + + private final MBeanServer mbeanServer; + + private final ObjectName on = DefaultCommitOperationMXBean.objectName; + + public DefaultCommitNotificationProducer(MBeanServer mBeanServer) { + this.mbeanServer = mBeanServer; + registerMBean(this, mbeanServer, on); + } + + private static void registerMBean(final Object instance, final MBeanServer mbs, final ObjectName on) { + try { + mbs.registerMBean(instance, on); + } catch (InstanceAlreadyExistsException | MBeanRegistrationException | NotCompliantMBeanException e) { + throw new RuntimeException("Unable to register " + instance + " as " + on, e); + } + } + + public void sendCommitNotification(String message, Element cfgSnapshot, Set capabilities) { + CommitJMXNotification notif = NetconfJMXNotification.afterCommit(this, message, cfgSnapshot, capabilities); + logger.debug("Notification about commit {} sent", notif); + sendNotification(notif); + } + + @Override + public void close() { + try { + mbeanServer.unregisterMBean(on); + } catch (InstanceNotFoundException | MBeanRegistrationException e) { + logger.warn("Ignoring exception while unregistering {} as {}", this, on, e); + } + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java new file mode 100644 index 0000000000..324da56ca5 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerDispatcher.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import com.google.common.base.Optional; +import io.netty.channel.ChannelFuture; +import io.netty.channel.socket.SocketChannel; +import io.netty.util.concurrent.Promise; +import org.opendaylight.controller.netconf.api.NetconfSession; +import org.opendaylight.controller.netconf.impl.util.DeserializerExceptionHandler; +import org.opendaylight.controller.netconf.util.AbstractChannelInitializer; +import org.opendaylight.protocol.framework.AbstractDispatcher; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import java.net.InetSocketAddress; + +public class NetconfServerDispatcher extends AbstractDispatcher { + + private final ServerChannelInitializer initializer; + + public NetconfServerDispatcher(final Optional maybeContext, + NetconfServerSessionNegotiatorFactory serverNegotiatorFactory, + NetconfServerSessionListenerFactory listenerFactory) { + this.initializer = new ServerChannelInitializer(maybeContext, serverNegotiatorFactory, listenerFactory); + } + + // FIXME change headers for all new source code files + + // TODO test create server with same address twice + public ChannelFuture createServer(InetSocketAddress address) { + + return super.createServer(address, new PipelineInitializer() { + @Override + public void initializeChannel(final SocketChannel ch, final Promise promise) { + initializer.initialize(ch, promise); + } + }); + } + + private static class ServerChannelInitializer extends AbstractChannelInitializer { + + private final NetconfServerSessionNegotiatorFactory negotiatorFactory; + private final NetconfServerSessionListenerFactory listenerFactory; + + private ServerChannelInitializer(Optional maybeContext, + NetconfServerSessionNegotiatorFactory negotiatorFactory, + NetconfServerSessionListenerFactory listenerFactory) { + super(maybeContext); + this.negotiatorFactory = negotiatorFactory; + this.listenerFactory = listenerFactory; + } + + @Override + protected void initializeAfterDecoder(SocketChannel ch, Promise promise) { + ch.pipeline().addLast("deserializerExHandler", new DeserializerExceptionHandler()); + ch.pipeline().addLast("negotiator", negotiatorFactory.getSessionNegotiator(listenerFactory, ch, promise)); + } + + @Override + protected void initSslEngine(SSLEngine sslEngine) { + sslEngine.setUseClientMode(false); + } + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java new file mode 100644 index 0000000000..1ae3fabbb9 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSession.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import io.netty.channel.Channel; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfSession; +import org.opendaylight.controller.netconf.api.NetconfTerminationReason; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.SessionListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class NetconfServerSession extends NetconfSession { + + private final SessionListener sessionListener; + private final Channel channel; + + private static final Logger logger = LoggerFactory.getLogger(NetconfServerSession.class); + private final long sessionId; + private boolean up = false; + + public NetconfServerSession(SessionListener sessionListener, Channel channel, long sessionId) { + this.sessionListener = sessionListener; + this.channel = channel; + this.sessionId = sessionId; + logger.debug("Session {} created", toString()); + } + + @Override + public void close() { + channel.close(); + sessionListener.onSessionTerminated(this, new NetconfTerminationReason("Session closed")); + } + + @Override + protected void handleMessage(NetconfMessage netconfMessage) { + logger.debug("Session {} received message {}", toString(), XmlUtil.toString(netconfMessage.getDocument())); + sessionListener.onMessage(this, netconfMessage); + } + + public void sendMessage(NetconfMessage netconfMessage) { + channel.writeAndFlush(netconfMessage); + } + + @Override + protected void endOfInput() { + logger.debug("Session {} end of input detected while session was in state {}", toString(), isUp() ? "up" + : "initialized"); + if (isUp()) { + this.sessionListener.onSessionDown(this, new IOException("End of input detected. Close the session.")); + } + } + + @Override + protected void sessionUp() { + logger.debug("Session {} up", toString()); + sessionListener.onSessionUp(this); + this.up = true; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("ServerNetconfSession{"); + sb.append("sessionId=").append(sessionId); + sb.append('}'); + return sb.toString(); + } + + public boolean isUp() { + return up; + } + + public long getSessionId() { + return sessionId; + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java new file mode 100644 index 0000000000..4f71ab9bb5 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfTerminationReason; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl; +import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.SessionListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import static com.google.common.base.Preconditions.checkState; + +public class NetconfServerSessionListener implements + SessionListener { + + static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionListener.class); + public static final String MESSAGE_ID = "message-id"; + + private NetconfOperationRouterImpl operationRouter; + + public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter) { + this.operationRouter = operationRouter; + } + + @Override + public void onSessionUp(NetconfServerSession netconfNetconfServerSession) { + + } + + @Override + public void onSessionDown(NetconfServerSession netconfNetconfServerSession, Exception e) { + logger.debug("Session {} down, reason: {}", netconfNetconfServerSession, e.getMessage()); + + operationRouter.close(); + } + + @Override + public void onSessionTerminated(NetconfServerSession netconfNetconfServerSession, + NetconfTerminationReason netconfTerminationReason) { + logger.debug("Session {} terminated, reason: {}", netconfNetconfServerSession, + netconfTerminationReason.getErrorMessage()); + + operationRouter.close(); + } + + @Override + public void onMessage(NetconfServerSession session, NetconfMessage netconfMessage) { + try { + + Preconditions.checkState(operationRouter != null, "Cannot handle message, session up was not yet received"); + // FIXME: there is no validation since the document may contain yang + // schemas + final NetconfMessage message = processDocument(netconfMessage); + logger.debug("Respondign with message {}", XmlUtil.toString(message.getDocument())); + session.sendMessage(message); + + if (isCloseSession(netconfMessage)) { + closeNetconfSession(session); + } + + } catch (final RuntimeException e) { + logger.error("Unexpected exception", e); + // TODO: should send generic error or close session? + throw new RuntimeException("Unable to process incoming message " + netconfMessage, e); + } catch (NetconfDocumentedException e) { + SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage); + } + } + + private void closeNetconfSession(NetconfServerSession session) { + // destroy NetconfOperationService + session.close(); + logger.info("Session {} closed successfully", session.getSessionId()); + } + + private NetconfMessage processDocument(final NetconfMessage netconfMessage) throws NetconfDocumentedException { + + final Document incommingDocument = netconfMessage.getDocument(); + final Node rootNode = incommingDocument.getDocumentElement(); + + if (rootNode.getNodeName().equals(XmlNetconfConstants.RPC_KEY)) { + final String messageId = rootNode.getAttributes().getNamedItem(MESSAGE_ID).getTextContent(); + checkState(messageId != null); + final Document responseDocument = XmlUtil.newDocument(); + Document rpcReply = operationRouter.onNetconfMessage(incommingDocument); + responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true)); + return new NetconfMessage(responseDocument); + } else { + // unknown command, send RFC 4741 p.70 unknown-element + /* + * Tag: unknown-element Error-type: rpc, protocol, application + * Severity: error Error-info: : name of the + * unexpected element Description: An unexpected element is present. + */ + // TODO add message to error info + throw new NetconfDocumentedException("Unknown tag " + rootNode.getNodeName(), + NetconfDocumentedException.ErrorType.protocol, NetconfDocumentedException.ErrorTag.unknown_element, + NetconfDocumentedException.ErrorSeverity.error, ImmutableMap.of("bad-element", + rootNode.getNodeName())); + } + } + + private static boolean isCloseSession(final NetconfMessage incommingDocument) { + final Document document = incommingDocument.getDocument(); + XmlElement rpcElement = XmlElement.fromDomDocument(document); + if (rpcElement.getOnlyChildElementOptionally("close-session").isPresent()) + return true; + + return false; + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java new file mode 100644 index 0000000000..eea6dc19da --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot; +import org.opendaylight.protocol.framework.SessionListenerFactory; + +public class NetconfServerSessionListenerFactory implements SessionListenerFactory { + + private final NetconfOperationServiceFactoryListener factoriesListener; + + private final DefaultCommitNotificationProducer commitNotifier; + + private final SessionIdProvider idProvider; + + public NetconfServerSessionListenerFactory(NetconfOperationServiceFactoryListener factoriesListener, + DefaultCommitNotificationProducer commitNotifier, SessionIdProvider idProvider) { + this.factoriesListener = factoriesListener; + this.commitNotifier = commitNotifier; + this.idProvider = idProvider; + } + + @Override + public NetconfServerSessionListener getSessionListener() { + NetconfOperationServiceSnapshot netconfOperationServiceSnapshot = factoriesListener.getSnapshot(idProvider + .getCurrentSessionId()); + + CapabilityProvider capabilityProvider = new CapabilityProviderImpl(netconfOperationServiceSnapshot); + + NetconfOperationRouterImpl operationRouter = new NetconfOperationRouterImpl(netconfOperationServiceSnapshot, + capabilityProvider, commitNotifier); + + return new NetconfServerSessionListener(operationRouter); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java new file mode 100644 index 0000000000..e14ae3e4dc --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiator.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences; +import org.opendaylight.controller.netconf.util.AbstractNetconfSessionNegotiator; +import org.opendaylight.protocol.framework.SessionListener; +import org.w3c.dom.Document; + +import io.netty.channel.Channel; +import io.netty.util.Timer; +import io.netty.util.concurrent.Promise; + +public class NetconfServerSessionNegotiator extends + AbstractNetconfSessionNegotiator { + + protected NetconfServerSessionNegotiator(NetconfServerSessionPreferences sessionPreferences, + Promise promise, Channel channel, Timer timer, SessionListener sessionListener) { + super(sessionPreferences, promise, channel, timer, sessionListener); + } + + @Override + protected NetconfServerSession getSession(SessionListener sessionListener, Channel channel, Document doc) { + return new NetconfServerSession(sessionListener, channel, sessionPreferences.getSessionId()); + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java new file mode 100644 index 0000000000..e74723032d --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import io.netty.channel.Channel; +import io.netty.util.Timer; +import io.netty.util.concurrent.Promise; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences; +import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener; +import org.opendaylight.controller.netconf.impl.util.NetconfUtil; +import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.SessionListenerFactory; +import org.opendaylight.protocol.framework.SessionNegotiator; +import org.opendaylight.protocol.framework.SessionNegotiatorFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; + +public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorFactory { + + private final Timer timer; + + private static final Document helloMessageTemplate = loadHelloMessageTemplate(); + private final SessionIdProvider idProvider; + private final NetconfOperationServiceFactoryListener factoriesListener; + + public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationServiceFactoryListener factoriesListener, + SessionIdProvider idProvider) { + this.timer = timer; + this.factoriesListener = factoriesListener; + this.idProvider = idProvider; + } + + private static Document loadHelloMessageTemplate() { + return NetconfUtil.createMessage( + NetconfServerSessionNegotiatorFactory.class.getResourceAsStream("/server_hello.xml")).getDocument(); + } + + @Override + public SessionNegotiator getSessionNegotiator(SessionListenerFactory sessionListenerFactory, Channel channel, + Promise promise) { + long sessionId = idProvider.getNextSessionId(); + + NetconfServerSessionPreferences proposal = new NetconfServerSessionPreferences(createHelloMessage(sessionId), + sessionId); + return new NetconfServerSessionNegotiator(proposal, promise, channel, timer, + sessionListenerFactory.getSessionListener()); + } + + private static final XPathExpression sessionIdXPath = XMLNetconfUtil + .compileXPath("/netconf:hello/netconf:session-id"); + private static final XPathExpression capabilitiesXPath = XMLNetconfUtil + .compileXPath("/netconf:hello/netconf:capabilities"); + + private NetconfMessage createHelloMessage(long sessionId) { + Document helloMessageTemplate = getHelloTemplateClone(); + + // change session ID + final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, helloMessageTemplate, + XPathConstants.NODE); + sessionIdNode.setTextContent(String.valueOf(sessionId)); + + // add capabilities from yang store + final Element capabilitiesElement = (Element) XmlUtil.evaluateXPath(capabilitiesXPath, helloMessageTemplate, + XPathConstants.NODE); + + CapabilityProvider capabilityProvider = new CapabilityProviderImpl(factoriesListener.getSnapshot(sessionId)); + + for (String capability : capabilityProvider.getCapabilities()) { + final Element capabilityElement = helloMessageTemplate.createElement(XmlNetconfConstants.CAPABILITY); + capabilityElement.setTextContent(capability); + capabilitiesElement.appendChild(capabilityElement); + } + return new NetconfMessage(helloMessageTemplate); + } + + private synchronized Document getHelloTemplateClone() { + return (Document) this.helloMessageTemplate.cloneNode(true); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SessionIdProvider.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SessionIdProvider.java new file mode 100644 index 0000000000..60fbfd8196 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/SessionIdProvider.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import java.util.concurrent.atomic.AtomicLong; + +public final class SessionIdProvider { + + private final AtomicLong sessionCounter = new AtomicLong(0); + + public long getNextSessionId() { + return sessionCounter.incrementAndGet(); + } + + public long getCurrentSessionId() { + return sessionCounter.get(); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java new file mode 100644 index 0000000000..b785b2d2ab --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/CapabilityProvider.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl.mapping; + +import com.google.common.base.Optional; + +import java.util.Set; + +public interface CapabilityProvider { + + String getSchemaForCapability(String moduleName, Optional revision); + + Set getCapabilities(); + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java new file mode 100644 index 0000000000..1438515f04 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCloseSession.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl.mapping.operations; + +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; +import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class DefaultCloseSession extends AbstractNetconfOperation { + public static final String CLOSE_SESSION = "close-session"; + + public DefaultCloseSession(String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + } + + @Override + protected HandlingPriority canHandle(String operationName, String netconfOperationNamespace) { + if (operationName.equals(CLOSE_SESSION) == false) + return HandlingPriority.CANNOT_HANDLE; + if (netconfOperationNamespace.equals(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0) == false) + return HandlingPriority.CANNOT_HANDLE; + + return HandlingPriority.HANDLE_WITH_MAX_PRIORITY; + } + + /** + * Close netconf operation router associated to this session, which in turn + * closes NetconfOperationServiceSnapshot with all NetconfOperationService + * instances + */ + @Override + protected Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter) + throws NetconfDocumentedException { + opRouter.close(); + return document.createElement(XmlNetconfConstants.OK); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java new file mode 100644 index 0000000000..8637c0dd74 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl.mapping.operations; + +import com.google.common.collect.Maps; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; +import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilterChain; +import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation.OperationNameAndNamespace; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.io.InputStream; +import java.util.Map; + +public class DefaultCommit implements NetconfOperationFilter { + + private static final Logger logger = LoggerFactory.getLogger(DefaultCommit.class); + + private static final String NOTIFY_ATTR = "notify"; + + private final DefaultCommitNotificationProducer notificationProducer; + private final CapabilityProvider cap; + private final String netconfSessionIdForReporting; + + public DefaultCommit(DefaultCommitNotificationProducer notifier, CapabilityProvider cap, + String netconfSessionIdForReporting) { + this.notificationProducer = notifier; + this.cap = cap; + this.netconfSessionIdForReporting = netconfSessionIdForReporting; + this.getConfigMessage = loadGetConfigMessage(); + } + + private final Document getConfigMessage; + public static final String GET_CONFIG_CANDIDATE_XML_LOCATION = "/getConfig_candidate.xml"; + + private static Document loadGetConfigMessage() { + try (InputStream asStream = DefaultCommit.class.getResourceAsStream(GET_CONFIG_CANDIDATE_XML_LOCATION)) { + return XmlUtil.readXmlToDocument(asStream); + } catch (Exception e) { + throw new IllegalStateException("Unable to load getConfig message for notifications from " + + GET_CONFIG_CANDIDATE_XML_LOCATION); + } + } + + @Override + public Document doFilter(Document message, NetconfOperationRouter operationRouter, + NetconfOperationFilterChain filterChain) throws NetconfDocumentedException { + OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(message); + if (canHandle(operationNameAndNamespace)) { + if (isCommitWithoutNotification(message)) { + message = removePersisterAttributes(message); + logger.debug("Skipping commit notification"); + // fall back to filter chain + } else { + Document innerResult = filterChain.execute(message, operationRouter); + Element cfgSnapshot = getConfigSnapshot(operationRouter); + logger.debug("Config snapshot retrieved successfully {}", cfgSnapshot); + notificationProducer.sendCommitNotification("ok", cfgSnapshot, cap.getCapabilities()); + return innerResult; + } + } + return filterChain.execute(message, operationRouter); + } + + @Override + public int getSoringOrder() { + return 0; + } + + @Override + public int compareTo(NetconfOperationFilter o) { + return Integer.compare(getSoringOrder(), o.getSoringOrder()); + } + + private boolean canHandle(OperationNameAndNamespace operationNameAndNamespace) { + if (operationNameAndNamespace.getOperationName().equals("commit") == false) + return false; + return operationNameAndNamespace.getNamespace().equals( + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + } + + private Document removePersisterAttributes(Document message) { + final Element documentElement = message.getDocumentElement(); + documentElement.removeAttribute(NOTIFY_ATTR); + return message; + } + + private boolean isCommitWithoutNotification(Document message) { + XmlElement xmlElement = XmlElement.fromDomElementWithExpected(message.getDocumentElement(), + XmlNetconfConstants.RPC_KEY, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + + String attr = xmlElement.getAttribute(NOTIFY_ATTR); + + if (attr == null || attr.equals("")) + return false; + else if (attr.equals(Boolean.toString(false))) { + logger.debug("Commit operation received with notify=false attribute {}", message); + return true; + } else { + return false; + } + } + + private Element getConfigSnapshot(NetconfOperationRouter opRouter) throws NetconfDocumentedException { + final Document responseDocument = opRouter.onNetconfMessage(getConfigMessage); + + XmlElement dataElement; + try { + XmlElement xmlElement = XmlElement.fromDomElementWithExpected(responseDocument.getDocumentElement(), + XmlNetconfConstants.RPC_REPLY_KEY, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + dataElement = xmlElement.getOnlyChildElement(XmlNetconfConstants.DATA_KEY); + } catch (IllegalArgumentException e) { + final String msg = "Unexpected response from get-config operation"; + logger.warn(msg, e); + Map info = Maps.newHashMap(); + info.put(NetconfDocumentedException.ErrorTag.operation_failed.toString(), e.getMessage()); + throw new NetconfDocumentedException(msg, e, NetconfDocumentedException.ErrorType.application, + NetconfDocumentedException.ErrorTag.operation_failed, + NetconfDocumentedException.ErrorSeverity.error, info); + } + + return dataElement.getDomElement(); + } + + @Override + public String toString() { + return "DefaultCommit{" + netconfSessionIdForReporting + '}'; + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java new file mode 100644 index 0000000000..3e65ed8842 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultGetSchema.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl.mapping.operations; + +import com.google.common.base.Optional; +import com.google.common.collect.Maps; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; +import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; +import org.opendaylight.controller.netconf.util.mapping.AbstractNetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.HashMap; +import java.util.Map; + +public final class DefaultGetSchema extends AbstractNetconfOperation { + + private final CapabilityProvider cap; + + private static final Logger logger = LoggerFactory.getLogger(DefaultGetSchema.class); + + public DefaultGetSchema(CapabilityProvider cap, String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + this.cap = cap; + } + + public static final String GET_SCHEMA = "get-schema"; + public static final String IDENTIFIER = "identifier"; + public static final String VERSION = "version"; + + @Override + protected HandlingPriority canHandle(String netconfOperationName, String namespace) { + if (netconfOperationName.equals("get-schema") == false) + return HandlingPriority.CANNOT_HANDLE; + if (namespace.equals(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING) == false) + return HandlingPriority.CANNOT_HANDLE; + + return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY; + } + + @Override + protected Element handle(Document document, XmlElement xml, NetconfOperationRouter router) + throws NetconfDocumentedException { + GetSchemaEntry entry; + + try { + entry = new GetSchemaEntry(xml); + } catch (final IllegalArgumentException e) { + logger.warn("Error parsing xml", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(NetconfDocumentedException.ErrorTag.bad_attribute.name(), e.getMessage()); + throw new NetconfDocumentedException(e.getMessage(), e, NetconfDocumentedException.ErrorType.rpc, + NetconfDocumentedException.ErrorTag.bad_attribute, NetconfDocumentedException.ErrorSeverity.error, + errorInfo); + } catch (final IllegalStateException e) { + logger.warn("Error parsing xml", e); + final Map errorInfo = new HashMap<>(); + errorInfo.put(NetconfDocumentedException.ErrorTag.bad_attribute.name(), e.getMessage()); + throw new NetconfDocumentedException(e.getMessage(), e, NetconfDocumentedException.ErrorType.rpc, + NetconfDocumentedException.ErrorTag.bad_attribute, NetconfDocumentedException.ErrorSeverity.error, + errorInfo); + } + + String schema; + try { + schema = cap.getSchemaForCapability(entry.identifier, entry.version); + } catch (IllegalStateException e) { + Map errorInfo = Maps.newHashMap(); + errorInfo.put(entry.identifier, e.getMessage()); + logger.warn("Rpc error: {}", NetconfDocumentedException.ErrorTag.operation_failed, e); + throw new NetconfDocumentedException(e.getMessage(), NetconfDocumentedException.ErrorType.application, + NetconfDocumentedException.ErrorTag.operation_failed, + NetconfDocumentedException.ErrorSeverity.error, errorInfo); + } + + Element getSchemaResult; + getSchemaResult = XmlUtil.createTextElement(document, XmlNetconfConstants.DATA_KEY, schema); + XmlUtil.addNamespaceAttr(getSchemaResult, + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING); + + logger.info("{} operation successful", GET_SCHEMA); + + return getSchemaResult; + } + + private static final class GetSchemaEntry { + private final String identifier; + private final Optional version; + + GetSchemaEntry(XmlElement getSchemaElement) { + getSchemaElement.checkName(GET_SCHEMA); + getSchemaElement.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING); + + XmlElement identifierElement = getSchemaElement.getOnlyChildElementWithSameNamespace(IDENTIFIER); + identifier = identifierElement.getTextContent(); + Optional versionElement = getSchemaElement + .getOnlyChildElementWithSameNamespaceOptionally(VERSION); + if (versionElement.isPresent()) { + version = Optional.of(versionElement.get().getTextContent()); + } else { + version = Optional.absent(); + } + + } + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java new file mode 100644 index 0000000000..96da1a6018 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfImplActivator.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.impl.osgi; + +import com.google.common.base.Optional; +import io.netty.util.HashedWheelTimer; +import org.opendaylight.controller.config.stat.ConfigProvider; +import org.opendaylight.controller.netconf.impl.*; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; +import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.TLSConfiguration; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLContext; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; + +public class NetconfImplActivator implements BundleActivator { + + private static final Logger logger = LoggerFactory.getLogger(NetconfImplActivator.class); + + private Optional maybeTCPAddress; + private Optional maybeTLSConfiguration; + + private NetconfOperationServiceFactoryTracker factoriesTracker; + private DefaultCommitNotificationProducer commitNot; + private NetconfServerDispatcher dispatch; + + @Override + public void start(final BundleContext context) throws Exception { + final ConfigProvider configProvider = new ConfigProvider.ConfigProviderImpl(context); + maybeTCPAddress = NetconfConfigUtil.extractTCPNetconfAddress(configProvider); + maybeTLSConfiguration = NetconfConfigUtil.extractTLSConfiguration(configProvider); + if (maybeTCPAddress.isPresent() == false && maybeTLSConfiguration.isPresent() == false) { + throw new IllegalStateException("TCP nor TLS is configured, netconf not available."); + } + NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + factoriesTracker = new NetconfOperationServiceFactoryTracker(context, factoriesListener); + factoriesTracker.open(); + + SessionIdProvider idProvider = new SessionIdProvider(); + NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( + new HashedWheelTimer(), factoriesListener, idProvider); + + commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + + NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory( + factoriesListener, commitNot, idProvider); + + if (maybeTCPAddress.isPresent()) { + Optional maybeSSLContext = Optional.absent(); + InetSocketAddress address = maybeTCPAddress.get(); + dispatch = new NetconfServerDispatcher(maybeSSLContext, serverNegotiatorFactory, listenerFactory); + + logger.info("Starting TCP netconf server at {}", address); + dispatch.createServer(address); + } + if (maybeTLSConfiguration.isPresent()) { + Optional maybeSSLContext = Optional.of(maybeTLSConfiguration.get().getSslContext()); + InetSocketAddress address = maybeTLSConfiguration.get().getAddress(); + dispatch = new NetconfServerDispatcher(maybeSSLContext, serverNegotiatorFactory, listenerFactory); + + logger.info("Starting TLS netconf server at {}", address); + dispatch.createServer(address); + } + } + + @Override + public void stop(final BundleContext context) throws Exception { + logger.info("Shutting down netconf because YangStoreService service was removed"); + + commitNot.close(); + dispatch.close(); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java new file mode 100644 index 0000000000..c7e1740ebc --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.impl.osgi; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; +import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; +import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession; +import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit; +import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultGetSchema; +import org.opendaylight.controller.netconf.mapping.api.*; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; + +import java.util.*; + +public class NetconfOperationRouterImpl implements NetconfOperationRouter { + + private static final Logger logger = LoggerFactory.getLogger(NetconfOperationRouterImpl.class); + + private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot; + + private final Set allNetconfOperations; + private final TreeSet allSortedFilters; + + private final CapabilityProvider capabilityProvider; + + public NetconfOperationRouterImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot, + CapabilityProvider capabilityProvider, DefaultCommitNotificationProducer commitNotifier) { + + this.netconfOperationServiceSnapshot = netconfOperationServiceSnapshot; + + this.capabilityProvider = capabilityProvider; + + Set defaultNetconfOperations = Sets.newHashSet(); + defaultNetconfOperations.add(new DefaultGetSchema(capabilityProvider, netconfOperationServiceSnapshot + .getNetconfSessionIdForReporting())); + defaultNetconfOperations.add(new DefaultCloseSession(netconfOperationServiceSnapshot + .getNetconfSessionIdForReporting())); + + allNetconfOperations = getAllNetconfOperations(defaultNetconfOperations, netconfOperationServiceSnapshot); + + DefaultCommit defaultCommit = new DefaultCommit(commitNotifier, capabilityProvider, + netconfOperationServiceSnapshot.getNetconfSessionIdForReporting()); + Set defaultFilters = Sets. newHashSet(defaultCommit); + allSortedFilters = getAllNetconfFilters(defaultFilters, netconfOperationServiceSnapshot); + } + + private static Set getAllNetconfOperations(Set defaultNetconfOperations, + NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) { + Set result = new HashSet<>(); + result.addAll(defaultNetconfOperations); + + for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) { + final Set netOpsFromService = netconfOperationService.getNetconfOperations(); + for (NetconfOperation netconfOperation : netOpsFromService) { + Preconditions.checkState(result.contains(netconfOperation) == false, + "Netconf operation %s already present", netconfOperation); + result.add(netconfOperation); + } + } + return Collections.unmodifiableSet(result); + } + + private static TreeSet getAllNetconfFilters(Set defaultFilters, + NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) { + TreeSet result = new TreeSet<>(defaultFilters); + for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) { + final Set filtersFromService = netconfOperationService.getFilters(); + for (NetconfOperationFilter filter : filtersFromService) { + Preconditions.checkState(result.contains(filter) == false, "Filter %s already present", filter); + result.add(filter); + } + } + return result; + } + + public CapabilityProvider getCapabilityProvider() { + return capabilityProvider; + } + + @Override + public synchronized Document onNetconfMessage(Document message) throws NetconfDocumentedException { + NetconfOperationExecution netconfOperationExecution = getNetconfOperationWithHighestPriority(message); + logger.debug("Forwarding netconf message {} to {}", XmlUtil.toString(message), + netconfOperationExecution.operationWithHighestPriority); + + final LinkedList chain = new LinkedList<>(); + chain.push(netconfOperationExecution); + + for (Iterator it = allSortedFilters.descendingIterator(); it.hasNext();) { + final NetconfOperationFilter filter = it.next(); + final NetconfOperationFilterChain prevItem = chain.getFirst(); + NetconfOperationFilterChain currentItem = new NetconfOperationFilterChain() { + @Override + public Document execute(Document message, NetconfOperationRouter operationRouter) + throws NetconfDocumentedException { + logger.trace("Entering {}", filter); + return filter.doFilter(message, operationRouter, prevItem); + } + }; + chain.push(currentItem); + } + return chain.getFirst().execute(message, this); + } + + private NetconfOperationExecution getNetconfOperationWithHighestPriority(Document message) { + + // TODO test + TreeMap> sortedPriority = getSortedNetconfOperationsWithCanHandle(message); + + Preconditions.checkState(sortedPriority.isEmpty() == false, "No %s available to handle message %s", + NetconfOperation.class.getName(), XmlUtil.toString(message)); + + HandlingPriority highestFoundPriority = sortedPriority.lastKey(); + + int netconfOperationsWithHighestPriority = sortedPriority.get(highestFoundPriority).size(); + + Preconditions.checkState(netconfOperationsWithHighestPriority == 1, + "Multiple %s available to handle message %s", NetconfOperation.class.getName(), message); + + return new NetconfOperationExecution(sortedPriority, highestFoundPriority); + } + + private TreeMap> getSortedNetconfOperationsWithCanHandle(Document message) { + TreeMap> sortedPriority = Maps.newTreeMap(); + + for (NetconfOperation netconfOperation : allNetconfOperations) { + final HandlingPriority handlingPriority = netconfOperation.canHandle(message); + + if (handlingPriority.equals(HandlingPriority.CANNOT_HANDLE) == false) { + Set netconfOperations = sortedPriority.get(handlingPriority); + netconfOperations = checkIfNoOperationsOnPriority(sortedPriority, handlingPriority, netconfOperations); + netconfOperations.add(netconfOperation); + } + } + return sortedPriority; + } + + private Set checkIfNoOperationsOnPriority( + TreeMap> sortedPriority, HandlingPriority handlingPriority, + Set netconfOperations) { + if (netconfOperations == null) { + netconfOperations = Sets.newHashSet(); + sortedPriority.put(handlingPriority, netconfOperations); + } + return netconfOperations; + } + + @Override + public void close() { + netconfOperationServiceSnapshot.close(); + } + + private class NetconfOperationExecution implements NetconfOperationFilterChain { + private final NetconfOperation operationWithHighestPriority; + + private NetconfOperationExecution(NetconfOperation operationWithHighestPriority) { + this.operationWithHighestPriority = operationWithHighestPriority; + } + + public NetconfOperationExecution(TreeMap> sortedPriority, + HandlingPriority highestFoundPriority) { + operationWithHighestPriority = sortedPriority.get(highestFoundPriority).iterator().next(); + sortedPriority.remove(highestFoundPriority); + } + + @Override + public Document execute(Document message, NetconfOperationRouter router) throws NetconfDocumentedException { + return operationWithHighestPriority.handle(message, router); + } + } + + @Override + public String toString() { + return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot + + '}'; + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java new file mode 100644 index 0000000000..385ad09754 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListener.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl.osgi; + +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; + +public interface NetconfOperationServiceFactoryListener { + + void onAddNetconfOperationServiceFactory(NetconfOperationServiceFactory service); + + void onRemoveNetconfOperationServiceFactory(NetconfOperationServiceFactory service); + + NetconfOperationServiceSnapshot getSnapshot(long sessionId); +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java new file mode 100644 index 0000000000..134c38ba97 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryListenerImpl.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.impl.osgi; + +import java.util.HashSet; +import java.util.Set; + +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; + +public class NetconfOperationServiceFactoryListenerImpl implements NetconfOperationServiceFactoryListener { + private final Set allFactories = new HashSet<>(); + + @Override + public synchronized void onAddNetconfOperationServiceFactory(NetconfOperationServiceFactory service) { + allFactories.add(service); + } + + @Override + public synchronized void onRemoveNetconfOperationServiceFactory(NetconfOperationServiceFactory service) { + allFactories.remove(service); + } + + @Override + public synchronized NetconfOperationServiceSnapshot getSnapshot(long sessionId) { + return new NetconfOperationServiceSnapshot(allFactories, sessionId); + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java new file mode 100644 index 0000000000..4d2bcb34fa --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceFactoryTracker.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.impl.osgi; + +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +class NetconfOperationServiceFactoryTracker extends + ServiceTracker { + private final NetconfOperationServiceFactoryListener operationRouter; + + NetconfOperationServiceFactoryTracker(BundleContext context, + final NetconfOperationServiceFactoryListener operationRouter) { + super(context, NetconfOperationServiceFactory.class, null); + this.operationRouter = operationRouter; + } + + @Override + public NetconfOperationServiceFactory addingService(ServiceReference reference) { + NetconfOperationServiceFactory netconfOperationServiceFactory = super.addingService(reference); + operationRouter.onAddNetconfOperationServiceFactory(netconfOperationServiceFactory); + return netconfOperationServiceFactory; + } + + @Override + public void removedService(ServiceReference reference, + NetconfOperationServiceFactory netconfOperationServiceFactory) { + operationRouter.onRemoveNetconfOperationServiceFactory(netconfOperationServiceFactory); + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java new file mode 100644 index 0000000000..cb4f53257e --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl.osgi; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfOperationServiceSnapshot implements AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceSnapshot.class); + + private final Set services; + private final String netconfSessionIdForReporting; + + public NetconfOperationServiceSnapshot(Set factories, long sessionId) { + Set services = new HashSet<>(); + netconfSessionIdForReporting = getNetconfSessionIdForReporting(sessionId); + for (NetconfOperationServiceFactory factory : factories) { + services.add(factory.createService(sessionId, netconfSessionIdForReporting)); + } + this.services = Collections.unmodifiableSet(services); + } + + private static String getNetconfSessionIdForReporting(long sessionId) { + return "netconf session id " + sessionId; + } + + public String getNetconfSessionIdForReporting() { + return netconfSessionIdForReporting; + } + + public Set getServices() { + return services; + } + + @Override + public void close() { + RuntimeException firstException = null; + for (NetconfOperationService service : services) { + try { + service.close(); + } catch (RuntimeException e) { + logger.warn("Got exception while closing {}", service, e); + if (firstException == null) { + firstException = e; + } else { + firstException.addSuppressed(e); + } + } + } + if (firstException != null) { + throw firstException; + } + } + + @Override + public String toString() { + return "NetconfOperationServiceSnapshot{" + netconfSessionIdForReporting + '}'; + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java new file mode 100644 index 0000000000..b27cd20172 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/DeserializerExceptionHandler.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl.util; + +import java.util.Map; + +import org.opendaylight.controller.netconf.api.NetconfDeserializerException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil; + +import com.google.common.collect.Maps; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; + +public final class DeserializerExceptionHandler implements ChannelHandler { + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + // NOOP + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + // NOOP + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (cause instanceof NetconfDeserializerException) { + handleDeserializerException(ctx, cause); + } + } + + private void handleDeserializerException(ChannelHandlerContext ctx, Throwable cause) { + + Map info = Maps.newHashMap(); + info.put("cause", cause.getMessage()); + NetconfDocumentedException ex = new NetconfDocumentedException(cause.getMessage(), + NetconfDocumentedException.ErrorType.rpc, NetconfDocumentedException.ErrorTag.malformed_message, + NetconfDocumentedException.ErrorSeverity.error, info); + + SendErrorExceptionUtil.sendErrorMessage(ctx.channel(), ex); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/NetconfUtil.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/NetconfUtil.java new file mode 100644 index 0000000000..0aa6fb31b7 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/util/NetconfUtil.java @@ -0,0 +1,37 @@ +package org.opendaylight.controller.netconf.impl.util; + +import java.io.*; + +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +// TODO purge nulls +public class NetconfUtil { + + private static final Logger logger = LoggerFactory.getLogger(NetconfUtil.class); + + public static NetconfMessage createMessage(final File f) { + try { + return createMessage(new FileInputStream(f)); + } catch (final FileNotFoundException e) { + logger.warn("File {} not found.", f, e); + } + return null; + } + + public static NetconfMessage createMessage(final InputStream is) { + Document doc = null; + try { + doc = XmlUtil.readXmlToDocument(is); + } catch (final IOException e) { + logger.warn("Error ocurred while parsing stream.", e); + } catch (final SAXException e) { + logger.warn("Error ocurred while final parsing stream.", e); + } + return (doc == null) ? null : new NetconfMessage(doc); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/resources/getConfig_candidate.xml b/opendaylight/netconf/netconf-impl/src/main/resources/getConfig_candidate.xml new file mode 100644 index 0000000000..3ac18b5cb7 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/resources/getConfig_candidate.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/opendaylight/netconf/netconf-impl/src/main/resources/server_hello.xml b/opendaylight/netconf/netconf-impl/src/main/resources/server_hello.xml new file mode 100644 index 0000000000..6a3f911cd4 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/resources/server_hello.xml @@ -0,0 +1,7 @@ + + + + urn:ietf:params:netconf:base:1.0 + + 1 + diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java new file mode 100644 index 0000000000..54a3482e34 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import com.google.common.base.Optional; +import com.google.common.collect.Sets; +import io.netty.channel.ChannelFuture; +import io.netty.util.HashedWheelTimer; +import org.apache.commons.io.IOUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.util.ConfigRegistryJMXClient; +import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreService; +import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.opendaylight.controller.netconf.client.NetconfClient; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; +import org.opendaylight.controller.netconf.mapping.api.*; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; + +import javax.management.ObjectName; +import javax.net.ssl.SSLContext; +import java.io.DataOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class ConcurrentClientsTest { + + private static final int CONCURRENCY = 16; + @Mock + private YangStoreService yangStoreService; + @Mock + private ConfigRegistryJMXClient jmxClient; + + private final InetSocketAddress netconfAddress = new InetSocketAddress("127.0.0.1", 8303); + + static final Logger logger = LoggerFactory.getLogger(ConcurrentClientsTest.class); + + private DefaultCommitNotificationProducer commitNot; + private NetconfServerDispatcher dispatch; + + @Before + public void setUp() throws Exception { + { // init mocks + MockitoAnnotations.initMocks(this); + final YangStoreSnapshot yStore = mock(YangStoreSnapshot.class); + doReturn(yStore).when(this.yangStoreService).getYangStoreSnapshot(); + doReturn(Collections.emptyMap()).when(yStore).getModuleMXBeanEntryMap(); + doReturn(Collections.emptyMap()).when(yStore).getModuleMap(); + + final ConfigTransactionJMXClient mockedTCl = mock(ConfigTransactionJMXClient.class); + doReturn(mockedTCl).when(this.jmxClient).getConfigTransactionClient(any(ObjectName.class)); + + doReturn(Collections.emptySet()).when(jmxClient).lookupConfigBeans(); + } + + NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + factoriesListener.onAddNetconfOperationServiceFactory(mockOpF()); + + SessionIdProvider idProvider = new SessionIdProvider(); + NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( + new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider); + + commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + + NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory( + factoriesListener, commitNot, idProvider); + dispatch = new NetconfServerDispatcher(Optional. absent(), serverNegotiatorFactory, listenerFactory); + + ChannelFuture s = dispatch.createServer(netconfAddress); + s.await(); + } + + private NetconfOperationServiceFactory mockOpF() { + return new NetconfOperationServiceFactory() { + @Override + public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) { + return new NetconfOperationService() { + @Override + public Set getCapabilities() { + return Collections.emptySet(); + } + + @Override + public Set getNetconfOperations() { + return Sets. newHashSet(new NetconfOperation() { + @Override + public HandlingPriority canHandle(Document message) { + return HandlingPriority.getHandlingPriority(Integer.MAX_VALUE); + } + + @Override + public Document handle(Document message, NetconfOperationRouter operationRouter) + throws NetconfDocumentedException { + try { + return XmlUtil.readXmlToDocument(""); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + @Override + public Set getFilters() { + return Collections.emptySet(); + } + + @Override + public void close() { + } + }; + } + }; + } + + @After + public void cleanUp() throws Exception { + commitNot.close(); + dispatch.close(); + } + + @Test + public void multipleClients() throws Exception { + List threads = new ArrayList<>(); + + final int attempts = 5; + for (int i = 0; i < CONCURRENCY; i++) { + TestingThread thread = new TestingThread(String.valueOf(i), attempts); + threads.add(thread); + thread.start(); + } + + for (TestingThread thread : threads) { + thread.join(); + assertTrue(thread.success); + } + } + + @Test + public void synchronizationTest() throws Exception { + new BlockingThread("foo").run2(); + } + + @Test + public void multipleBlockingClients() throws Exception { + List threads = new ArrayList<>(); + for (int i = 0; i < CONCURRENCY; i++) { + BlockingThread thread = new BlockingThread(String.valueOf(i)); + threads.add(thread); + thread.start(); + } + + for (BlockingThread thread : threads) { + thread.join(); + assertTrue(thread.success); + } + } + + class BlockingThread extends Thread { + Boolean success; + + public BlockingThread(String name) { + super("client-" + name); + } + + @Override + public void run() { + try { + run2(); + success = true; + } catch (Exception e) { + success = false; + throw new RuntimeException(e); + } + } + + private void run2() throws Exception { + InputStream clientHello = checkNotNull(XmlFileLoader + .getResourceAsStream("netconfMessages/client_hello.xml")); + InputStream getConfig = checkNotNull(XmlFileLoader.getResourceAsStream("netconfMessages/getConfig.xml")); + + Socket clientSocket = new Socket(netconfAddress.getHostString(), netconfAddress.getPort()); + DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream()); + InputStreamReader inFromServer = new InputStreamReader(clientSocket.getInputStream()); + + StringBuffer sb = new StringBuffer(); + while (sb.toString().endsWith("]]>]]>") == false) { + sb.append((char) inFromServer.read()); + } + logger.info(sb.toString()); + + outToServer.write(IOUtils.toByteArray(clientHello)); + outToServer.write("]]>]]>".getBytes()); + outToServer.flush(); + // Thread.sleep(100); + outToServer.write(IOUtils.toByteArray(getConfig)); + outToServer.write("]]>]]>".getBytes()); + outToServer.flush(); + Thread.sleep(100); + sb = new StringBuffer(); + while (sb.toString().endsWith("]]>]]>") == false) { + sb.append((char) inFromServer.read()); + } + logger.info(sb.toString()); + clientSocket.close(); + } + } + + class TestingThread extends Thread { + + private final String clientId; + private final int attempts; + private Boolean success; + + TestingThread(String clientId, int attempts) { + this.clientId = clientId; + this.attempts = attempts; + setName("client-" + clientId); + } + + @Override + public void run() { + try { + final NetconfClient netconfClient = new NetconfClient(clientId, netconfAddress); + long sessionId = netconfClient.getSessionId(); + logger.info("Client with sessionid {} hello exchanged", sessionId); + + final NetconfMessage getMessage = XmlFileLoader + .xmlFileToNetconfMessage("netconfMessages/getConfig.xml"); + NetconfMessage result = netconfClient.sendMessage(getMessage); + logger.info("Client with sessionid {} got result {}", sessionId, result); + netconfClient.close(); + logger.info("Client with session id {} ended", sessionId); + success = true; + } catch (final Exception e) { + success = false; + throw new RuntimeException(e); + } + } + } +} diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageHeaderTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageHeaderTest.java new file mode 100644 index 0000000000..aa3c5d4d9b --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageHeaderTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.netconf.util.messages.NetconfMessageHeader; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class MessageHeaderTest { + + private NetconfMessageHeader header = null; + + @Before + public void setUp() { + this.header = new NetconfMessageHeader(); + } + + @Test + public void testFromBytes() { + final byte[] raw = new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x35, (byte) 0x38, (byte) 0x0a }; + this.header.fromBytes(raw); + assertEquals(58, this.header.getLength()); + } + + @Test + public void testToBytes() { + this.header.setLength(123); + assertArrayEquals(new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x0a }, + this.header.toBytes()); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageParserTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageParserTest.java new file mode 100644 index 0000000000..39f45e0c49 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/MessageParserTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import javax.xml.parsers.ParserConfigurationException; + +import org.custommonkey.xmlunit.XMLAssert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.messages.FramingMechanism; +import org.opendaylight.controller.netconf.util.messages.NetconfMessageFactory; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.DeserializerException; +import org.opendaylight.protocol.framework.DocumentedException; +import org.opendaylight.protocol.util.ByteArray; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +public class MessageParserTest { + + private NetconfMessageFactory parser = null; + + @Before + public void setUp() { + this.parser = new NetconfMessageFactory(); + } + + @Test + public void testPutEOM() throws IOException, SAXException, ParserConfigurationException { + final NetconfMessage msg = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/client_hello.xml"); + final byte[] bytes = this.parser.put(msg); + assertArrayEquals(NetconfMessageFactory.endOfMessage, ByteArray.subByte(bytes, bytes.length + - NetconfMessageFactory.endOfMessage.length, NetconfMessageFactory.endOfMessage.length)); + } + + @Ignore + @Test + // TODO not working on WINDOWS + // arrays first differed at element [4]; expected:<49> but was:<53> + // at + // org.junit.internal.ComparisonCriteria.arrayEquals(ComparisonCriteria.java:52) + public void testPutChunk() throws IOException, SAXException, ParserConfigurationException { + final NetconfMessage msg = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/client_hello.xml"); + this.parser.setFramingMechanism(FramingMechanism.CHUNK); + final byte[] bytes = this.parser.put(msg); + final byte[] header = new byte[] { (byte) 0x0a, (byte) 0x23, (byte) 0x32, (byte) 0x31, (byte) 0x31, (byte) 0x0a }; + assertArrayEquals(header, ByteArray.subByte(bytes, 0, header.length)); + assertArrayEquals(NetconfMessageFactory.endOfChunk, ByteArray.subByte(bytes, bytes.length + - NetconfMessageFactory.endOfChunk.length, NetconfMessageFactory.endOfChunk.length)); + } + + @Test + public void testParseEOM() throws IOException, SAXException, DeserializerException, DocumentedException, + ParserConfigurationException { + final Document msg = XmlFileLoader.xmlFileToDocument("netconfMessages/client_hello.xml"); + final byte[] bytes = this.parser.put(new NetconfMessage(msg)); + final Document doc = this.parser + .parse(ByteArray.subByte(bytes, 0, bytes.length - NetconfMessageFactory.endOfMessage.length)) + .iterator().next().getDocument(); + assertEquals(XmlUtil.toString(msg), XmlUtil.toString(doc)); + XMLAssert.assertXMLEqual(msg, doc); + } + + @Test + public void testParseChunk() throws IOException, SAXException, DeserializerException, DocumentedException, + ParserConfigurationException { + final Document msg = XmlFileLoader.xmlFileToDocument("netconfMessages/client_hello.xml"); + this.parser.setFramingMechanism(FramingMechanism.CHUNK); + final byte[] bytes = this.parser.put(new NetconfMessage(msg)); + final Document doc = this.parser + .parse(ByteArray.subByte(bytes, 6, bytes.length - NetconfMessageFactory.endOfChunk.length - 6)) + .iterator().next().getDocument(); + assertEquals(XmlUtil.toString(msg), XmlUtil.toString(doc)); + XMLAssert.assertXMLEqual(msg, doc); + } + +} diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java new file mode 100644 index 0000000000..ecbde5904b --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/NetconfDispatcherImplTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; + +import javax.net.ssl.SSLContext; + +import org.junit.Test; +import org.opendaylight.controller.netconf.impl.*; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListener; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; + +import com.google.common.base.Optional; + +import io.netty.channel.ChannelFuture; +import io.netty.util.HashedWheelTimer; + +public class NetconfDispatcherImplTest { + + @Test + public void test() throws Exception { + + DefaultCommitNotificationProducer commitNot = new DefaultCommitNotificationProducer( + ManagementFactory.getPlatformMBeanServer()); + NetconfOperationServiceFactoryListener factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + + SessionIdProvider idProvider = new SessionIdProvider(); + NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( + new HashedWheelTimer(), factoriesListener, idProvider); + + NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory( + factoriesListener, commitNot, idProvider); + NetconfServerDispatcher dispatch = new NetconfServerDispatcher(Optional. absent(), + serverNegotiatorFactory, listenerFactory); + + InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 8333); + ChannelFuture s = dispatch.createServer(addr); + + commitNot.close(); + dispatch.close(); + } +} diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked1.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked1.txt new file mode 100644 index 0000000000..aad72393cb --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked1.txt @@ -0,0 +1,35 @@ + +#24 + + + 14 + fred + + < +#2 +/r +#3 +pc> +## diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked2.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked2.txt new file mode 100644 index 0000000000..a36a85ea1f --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked2.txt @@ -0,0 +1,48 @@ + +#22 + + + + + +#18 + + + +#19 + + +#8 + + +#77 + + + + + +## diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked3.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked3.txt new file mode 100644 index 0000000000..d9dc43d620 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked3.txt @@ -0,0 +1,43 @@ + +#43 + + +#26 + + + +#35 + + + +#39 + + < +#40 +top xmlns="http://example.com/schema/1.2 +#26 +/config"> + + +#36 + + f +#56 +red + + + +#28 + + + + +## diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked4.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked4.txt new file mode 100644 index 0000000000..0b8a102dff --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked4.txt @@ -0,0 +1,40 @@ + +#17 + + + + + + +#43 + + + +#16 + + +#22 + + + +## diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked5.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked5.txt new file mode 100644 index 0000000000..f8f3c4d7c2 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/chunked5.txt @@ -0,0 +1,42 @@ + +#43 + + + + + < +#4 +/tar +#18 +get> + + +#41 + + + +#29 + Ethernet0/0 + 1500 + +#61 + + + + + +## diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_config_bean_response.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_config_bean_response.txt new file mode 100644 index 0000000000..2ae32efb65 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_config_bean_response.txt @@ -0,0 +1,18 @@ +curl http://localhost:17777/jolokia/read/org.opendaylight.controller:instanceName=fixed1,type=ConfigBean,interfaceName=testing-threadpool | jsonpp +{ + "request": { + "mbean": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean", + "type": "read" + }, + "status": 200, + "timestamp": 1362416252, + "value": { + "ExportedInterfaces": [ + "testing-threadpool", + "modifiable-threadpool" + ], + "ImplementationName": "fixed", + "ThreadCount": 10, + "TriggerNewInstanceCreation": false + } +} \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_lookupConfigBeans.txt b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_lookupConfigBeans.txt new file mode 100644 index 0000000000..2ae705a54f --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/listener/databaseinteractions/jolokia_lookupConfigBeans.txt @@ -0,0 +1,18 @@ +$ curl 'http://localhost:17777/jolokia/exec/org.opendaylight.controller:type=ConfigRegistry/lookupConfigBeans()' | jsonpp +{ + "request": { + "mbean": "org.opendaylight.controller:type=ConfigRegistry", + "operation": "lookupConfigBeans()", + "type": "exec" + }, + "status": 200, + "timestamp": 1362417043, + "value": [ + { + "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean" + }, + { + "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean" + } + ] +} diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_commit.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_commit.xml new file mode 100644 index 0000000000..6eca609b6c --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_commit.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_candidate.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_candidate.xml new file mode 100644 index 0000000000..6a9ed639d8 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_candidate.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_running.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_running.xml new file mode 100644 index 0000000000..2d66c45906 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_lock_running.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_modify_candidate.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_modify_candidate.xml new file mode 100644 index 0000000000..ce67845de1 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_modify_candidate.xml @@ -0,0 +1,20 @@ + + + + + + none + test-then-set + stop-on-error + + + + 7 + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_candidate.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_candidate.xml new file mode 100644 index 0000000000..dd6fe1ba1e --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_candidate.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_running.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_running.xml new file mode 100644 index 0000000000..f94af4698d --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/client_unlock_running.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/server_error_missing_attribute.xml b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/server_error_missing_attribute.xml new file mode 100644 index 0000000000..c70184e2b4 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/org/opendaylight/controller/netconf/impl/notused/server_error_missing_attribute.xml @@ -0,0 +1,11 @@ + + + rpc + missing-attribute + error + + message-id + rpc + + + diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.json b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.json new file mode 100644 index 0000000000..5e03681d95 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.json @@ -0,0 +1,10 @@ +{ + "value":null, + "status":200, + "request": { + "type":"exec", + "mbean":"java.util.logging:type=Logging", + "operation":"setLoggerLevel", + "arguments":["global","INFO"] + } +} \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.xml new file mode 100644 index 0000000000..b04ace303f --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/input.xml @@ -0,0 +1,7 @@ + + org.opendaylight.controller:type=AppDeployer + EXEC + lookupConfigBeans + abc,bcd.aas + 64 + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputList.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputList.xml new file mode 100644 index 0000000000..5fc93532a4 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputList.xml @@ -0,0 +1,9 @@ + + org.opendaylight.controller:type=AppDeployer + EXEC + lookupConfigBeans + + 22 + 69 + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMap.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMap.xml new file mode 100644 index 0000000000..9bf5b58dc4 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMap.xml @@ -0,0 +1,14 @@ + + org.opendaylight.controller:type=AppDeployer + EXEC + lookupConfigBeans + + + single + mock + + 2 + 22 + arg + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMultiple.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMultiple.xml new file mode 100644 index 0000000000..23a3489310 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputMultiple.xml @@ -0,0 +1,29 @@ + + + org.opendaylight.controller:type=AppDeployer + EXEC + lookupConfigBeans + abc,bcd.aas + 22 + + + org.opendaylight.controller:type=AppDeployer + WRITE + attribute + 22 + + + org.opendaylight.controller:type=AppDeployer + EXEC + lookupConfigBeans + + + single + mock + + 2 + 22 + arg + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetter.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetter.xml new file mode 100644 index 0000000000..abad54d27a --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetter.xml @@ -0,0 +1,6 @@ + + org.opendaylight.controller:type=AppDeployer + WRITE + attribute + 22 + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterList.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterList.xml new file mode 100644 index 0000000000..7162b8f056 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterList.xml @@ -0,0 +1,8 @@ + + org.opendaylight.controller:type=AppDeployer + WRITE + attribute + 22 + 222 + 223 + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterMap.xml b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterMap.xml new file mode 100644 index 0000000000..00a0536c0c --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/inputSetterMap.xml @@ -0,0 +1,9 @@ + + org.opendaylight.controller:type=AppDeployer + WRITE + setAtr + + 1 + 2 + + diff --git a/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/map.json b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/map.json new file mode 100644 index 0000000000..60fabb62d1 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/test/resources/testConfigs/map.json @@ -0,0 +1,134 @@ +{ + "timestamp":1362488209, + "status":200, + "request":{ + "mbean":"org.opendaylight.controller:type=ConfigRegistry", + "attribute":"AvailableInterfacesAndImplementations", + "type":"read" + }, + "value":{ + "topology-registry":[ + "single" + ], + "bgp-update":[ + "mock", + "bgp-impl" + ], + "positioning-service":[ + "combine", + "onehop", + "ondemand", + "pxe", + "precompute" + ], + "serializer":[ + "serializer-impl" + ], + "network-topology-factory":[ + "mock-xml", + "bgp-network-topology-factory", + "transient" + ], + "dwe-topology":[ + "ebgp", + "defaultmetric", + "igp", + "network" + ], + "thread-factory":[ + "naming-thread-factory" + ], + "bgp-parser":[ + "parser" + ], + "pcep-dispatcher":[ + "dispatcher" + ], + "threadpool":[ + "flexible", + "fixed", + "scheduled" + ], + "scheduled-threadpool":[ + "scheduled" + ], + "positioning-onehop":[ + "onehop" + ], + "bgp-dispatcher":[ + "bgp-dispatcher-impl" + ], + "cost-combiner":[ + "pxe" + ], + "apsp-provider":[ + "jgrapht", + "parallel", + "single-threaded" + ], + "topology":[ + "ebgp", + "defaultmetric", + "igp", + "network" + ], + "soap-resource":[ + "positioning-adaptor-pxe" + ], + "database-provider-factory":[ + "transient" + ], + "bgp-proposal-checker":[ + "bgp-proposal-checker-impl" + ], + "bgp-proposal":[ + "bgp-proposal-impl" + ], + "listenable-network-topology-factory":[ + "transient" + ], + "event-bus":[ + "sync", + "async" + ], + "topology-registry-provider":[ + "single" + ], + "topology-provider-factory":[ + "transient" + ], + "rest-resource":[ + "topology-resource-holder", + "alto-resource-holder", + "topology-visual-holder", + "network-resource-holder", + "path-resource-holder" + ], + "listenable-database-provider-factory":[ + "transient" + ], + "topology-validator":[ + "accept-all", + "threshold" + ], + "replicator":[ + "replicator-impl" + ], + "server":[ + "soap", + "rest" + ], + "combiner-pxe":[ + "pxe" + ], + "rest":[ + "rest" + ], + "soap":[ + "soap" + ], + "path-service":[ + "cariden" + ] + } +} \ No newline at end of file diff --git a/opendaylight/netconf/netconf-it/pom.xml b/opendaylight/netconf/netconf-it/pom.xml new file mode 100644 index 0000000000..a3377d7501 --- /dev/null +++ b/opendaylight/netconf/netconf-it/pom.xml @@ -0,0 +1,165 @@ + + + 4.0.0 + + + netconf-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + + + netconf-it + ${project.artifactId} + + + + + ${project.groupId} + config-api + ${config.version} + test + + + ${project.groupId} + config-util + ${config.version} + test + + + ${project.groupId} + yang-store-api + ${config.version} + test + + + ${project.groupId} + netconf-api + ${project.version} + test + + + org.opendaylight.bgpcep + util + test + + + ${project.groupId} + netconf-client + ${project.version} + test + + + ${project.groupId} + config-netconf-connector + ${project.version} + test + + + ${project.groupId} + yang-test + test + + + ${project.groupId} + config-manager + test + + + ${project.groupId} + config-persister-impl + ${project.version} + test + + + ${project.groupId} + config-manager + ${config.version} + test + test-jar + + + ${project.groupId} + netconf-impl + ${project.version} + test + + + ${project.groupId} + netconf-mapping-api + ${project.version} + test + + + ${project.groupId} + netconf-util + ${project.version} + test + test-jar + + + ${project.groupId} + yang-store-impl + test + + + ${project.groupId} + yang-store-impl + ${config.version} + test + test-jar + + + org.opendaylight.controller + logback-config + 0.2.1-SNAPSHOT + + + + org.slf4j + slf4j-api + + + + org.opendaylight.bgpcep + mockito-configuration + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 1 + false + false + + + + default-test + + true + + + + integration-tests + integration-test + + test + + + + **/org/opendaylight/controller/netconf/it/*.java + + false + + + + + + + diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java new file mode 100644 index 0000000000..8a51b7c84d --- /dev/null +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.it; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import io.netty.channel.ChannelFuture; +import io.netty.util.HashedWheelTimer; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.opendaylight.controller.config.api.ModuleIdentifier; +import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; +import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator; +import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl; +import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler; +import org.opendaylight.controller.config.persist.api.Persister; +import org.opendaylight.controller.config.spi.ModuleFactory; +import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; +import org.opendaylight.controller.config.yang.store.api.YangStoreException; +import org.opendaylight.controller.config.yang.store.impl.HardcodedYangStoreService; +import org.opendaylight.controller.config.yang.test.impl.*; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.client.NetconfClient; +import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; +import org.opendaylight.controller.netconf.impl.*; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.util.SSLUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import javax.management.ObjectName; +import javax.net.ssl.SSLContext; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.internal.util.Checks.checkNotNull; + +public class NetconfITTest extends AbstractConfigTest { + + // private static final Logger logger = + // LoggerFactory.getLogger(NetconfITTest.class); + // + private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); + private static final InetSocketAddress tlsAddress = new InetSocketAddress("127.0.0.1", 12024); + + private NetconfMessage getConfig, getConfigCandidate, editConfig, closeSession; + private DefaultCommitNotificationProducer commitNot; + private NetconfServerDispatcher dispatch, dispatchS; + + @Before + public void setUp() throws Exception { + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray( + new ModuleFactory[0]))); + + loadMessages(); + + NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore())); + + commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + + dispatch = createDispatcher(Optional. absent(), factoriesListener); + ChannelFuture s = dispatch.createServer(tcpAddress); + s.await(); + + dispatchS = createDispatcher(Optional.of(getSslContext()), factoriesListener); + s = dispatchS.createServer(tlsAddress); + s.await(); + } + + private NetconfServerDispatcher createDispatcher(Optional sslC, + NetconfOperationServiceFactoryListenerImpl factoriesListener) { + SessionIdProvider idProvider = new SessionIdProvider(); + NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( + new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider); + + NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory( + factoriesListener, commitNot, idProvider); + + return new NetconfServerDispatcher(sslC, serverNegotiatorFactory, listenerFactory); + } + + @After + public void tearDown() throws Exception { + commitNot.close(); + dispatch.close(); + dispatchS.close(); + } + + private SSLContext getSslContext() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, + IOException, UnrecoverableKeyException, KeyManagementException { + final InputStream keyStore = getClass().getResourceAsStream("/keystore.jks"); + final InputStream trustStore = getClass().getResourceAsStream("/keystore.jks"); + SSLContext sslContext = SSLUtil.initializeSecureContext("password", keyStore, trustStore, "SunX509"); + keyStore.close(); + trustStore.close(); + return sslContext; + } + + private void loadMessages() throws IOException, SAXException, ParserConfigurationException { + this.editConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/edit_config.xml"); + this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml"); + this.getConfigCandidate = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig_candidate.xml"); + this.closeSession = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/closeSession.xml"); + } + + private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException { + final Collection yangDependencies = getBasicYangs(); + return new HardcodedYangStoreService(yangDependencies); + } + + private Collection getBasicYangs() throws IOException { + List paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang", + "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang", + "/META-INF/yang/ietf-inet-types.yang"); + final Collection yangDependencies = new ArrayList<>(); + for (String path : paths) { + final InputStream is = checkNotNull(getClass().getResourceAsStream(path), path + " not found"); + yangDependencies.add(is); + } + return yangDependencies; + } + + protected List getModuleFactories() { + return getModuleFactoriesS(); + } + + static List getModuleFactoriesS() { + return Lists.newArrayList(new TestImplModuleFactory(), new DepTestImplModuleFactory(), + new NetconfTestImplModuleFactory()); + } + + @Test + public void testTwoSessions() throws Exception { + try (NetconfClient netconfClient = new NetconfClient("1", tcpAddress, 4000)) { + try (NetconfClient netconfClient2 = new NetconfClient("2", tcpAddress, 4000)) { + } + } + } + + @Test(timeout = 10000) + public void testPersister() throws Exception { + Persister persister = mock(Persister.class); + doReturn("mockPersister").when(persister).toString(); + doReturn(Optional.absent()).when(persister).loadLastConfig(); + ConfigPersisterNotificationHandler h = new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer()); + h.init(); + } + + @Test + public void testSecure() throws Exception { + try (NetconfClient netconfClient = new NetconfClient("1", tlsAddress, 4000, Optional.of(getSslContext()))) { + + } + } + + @Ignore + @Test + public void waitingTest() throws Exception { + final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction(); + transaction.createModule(DepTestImplModuleFactory.NAME, "eb"); + transaction.commit(); + Thread.currentThread().suspend(); + } + + @Test + public void rpcReplyContainsAllAttributesTest() throws Exception { + try (NetconfClient netconfClient = createSession(tcpAddress, "1")) { + final String rpc = "" + + "" + ""; + final Document doc = XmlUtil.readXmlToDocument(rpc); + final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc)); + assertNotNull(message); + final NamedNodeMap expectedAttributes = doc.getDocumentElement().getAttributes(); + final NamedNodeMap returnedAttributes = message.getDocument().getDocumentElement().getAttributes(); + + assertSameAttributes(expectedAttributes, returnedAttributes); + } + } + + private void assertSameAttributes(final NamedNodeMap expectedAttributes, final NamedNodeMap returnedAttributes) { + assertNotNull("Expecting 4 attributes", returnedAttributes); + assertEquals(expectedAttributes.getLength(), returnedAttributes.getLength()); + + for (int i = 0; i < expectedAttributes.getLength(); i++) { + final Node expAttr = expectedAttributes.item(i); + final Node attr = returnedAttributes.item(i); + assertEquals(expAttr.getNodeName(), attr.getNodeName()); + assertEquals(expAttr.getNamespaceURI(), attr.getNamespaceURI()); + assertEquals(expAttr.getTextContent(), attr.getTextContent()); + } + } + + @Test + public void rpcReplyErrorContainsAllAttributesTest() throws Exception { + try (NetconfClient netconfClient = createSession(tcpAddress, "1")) { + final String rpc = "" + + "" + ""; + final Document doc = XmlUtil.readXmlToDocument(rpc); + final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc)); + final NamedNodeMap expectedAttributes = doc.getDocumentElement().getAttributes(); + final NamedNodeMap returnedAttributes = message.getDocument().getDocumentElement().getAttributes(); + + assertSameAttributes(expectedAttributes, returnedAttributes); + } + } + + @Test + public void rpcOutputContainsCorrectNamespace() throws Exception { + final ConfigTransactionJMXClient transaction = this.configRegistryClient.createTransaction(); + ObjectName dep = transaction.createModule(DepTestImplModuleFactory.NAME, "instanceD"); + ObjectName impl = transaction.createModule(NetconfTestImplModuleFactory.NAME, "instance"); + NetconfTestImplModuleMXBean proxy = configRegistryClient + .newMXBeanProxy(impl, NetconfTestImplModuleMXBean.class); + proxy.setTestingDep(dep); + registerRuntimeBean(); + + transaction.commit(); + + try (NetconfClient netconfClient = createSession(tcpAddress, "1")) { + final String expectedNamespace = "urn:opendaylight:params:xml:ns:yang:controller:test:impl"; + + final String rpc = "" + + " " + + "/data/modules/module[name='impl-netconf']/instance[name='instance']" + + "argument1" + "" + ""; + final Document doc = XmlUtil.readXmlToDocument(rpc); + final NetconfMessage message = netconfClient.sendMessage(new NetconfMessage(doc)); + + final Element rpcReply = message.getDocument().getDocumentElement(); + final XmlElement resultElement = XmlElement.fromDomElement(rpcReply).getOnlyChildElement(); + assertEquals("result", resultElement.getName()); + final String namespace = resultElement.getNamespaceAttribute(); + assertEquals(expectedNamespace, namespace); + } + } + + private void registerRuntimeBean() { + BaseJMXRegistrator baseJMXRegistrator = new BaseJMXRegistrator(ManagementFactory.getPlatformMBeanServer()); + RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = baseJMXRegistrator + .createRuntimeBeanRegistrator(new ModuleIdentifier(NetconfTestImplModuleFactory.NAME, "instance")); + NetconfTestImplRuntimeRegistrator reg = new NetconfTestImplRuntimeRegistrator(runtimeBeanRegistrator); + reg.register(new NetconfTestImplRuntimeMXBean() { + @Override + public Asdf getAsdf() { + return null; + } + + @Override + public Long getCreatedSessions() { + return null; + } + + @Override + public String noArg(String arg1) { + return "from no arg"; + } + }); + } + + @Test + public void testCloseSession() throws Exception { + try (NetconfClient netconfClient = createSession(tcpAddress, "1")) { + + // edit config + Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument(); + assertIsOK(rpcReply); + + rpcReply = netconfClient.sendMessage(this.closeSession).getDocument(); + + assertIsOK(rpcReply); + } + } + + @Test + public void testEditConfig() throws Exception { + try (NetconfClient netconfClient = createSession(tcpAddress, "1")) { + // send edit_config.xml + final Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument(); + assertIsOK(rpcReply); + } + } + + @Test + public void testValidate() throws Exception { + try (NetconfClient netconfClient = createSession(tcpAddress, "1")) { + // begin transaction + Document rpcReply = netconfClient.sendMessage(getConfigCandidate).getDocument(); + assertEquals("data", XmlElement.fromDomDocument(rpcReply).getOnlyChildElement().getName()); + + // operations empty + rpcReply = netconfClient.sendMessage(XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/validate.xml")) + .getDocument(); + assertIsOK(rpcReply); + } + } + + private void assertIsOK(final Document rpcReply) { + assertEquals("rpc-reply", rpcReply.getDocumentElement().getTagName()); + assertEquals("ok", XmlElement.fromDomDocument(rpcReply).getOnlyChildElement().getName()); + } + + @Ignore + @Test + // TODO can only send NetconfMessage - it must be valid xml + public void testClientHelloWithAuth() throws Exception { + final String fileName = "netconfMessages/client_hello_with_auth.xml"; + // final InputStream resourceAsStream = + // AbstractListenerTest.class.getResourceAsStream(fileName); + // assertNotNull(resourceAsStream); + try (NetconfClient netconfClient = new NetconfClient("test", tcpAddress, 5000)) { + // IOUtils.copy(resourceAsStream, netconfClient.getStream()); + // netconfClient.getOutputStream().write(NetconfMessageFactory.endOfMessage); + // server should not write anything back + // assertEquals(null, netconfClient.readMessage()); + assertGetConfigWorks(netconfClient); + } + } + + private Document assertGetConfigWorks(final NetconfClient netconfClient) throws InterruptedException { + return assertGetConfigWorks(netconfClient, this.getConfig); + } + + private Document assertGetConfigWorks(final NetconfClient netconfClient, final NetconfMessage getConfigMessage) + throws InterruptedException { + final NetconfMessage rpcReply = netconfClient.sendMessage(getConfigMessage); + assertNotNull(rpcReply); + assertEquals("data", XmlElement.fromDomDocument(rpcReply.getDocument()).getOnlyChildElement().getName()); + return rpcReply.getDocument(); + } + + @Test + public void testGetConfig() throws Exception { + try (NetconfClient netconfClient = createSession(tcpAddress, "1")) { + assertGetConfigWorks(netconfClient); + } + } + + @Test + public void createYangTestBasedOnYuma() throws Exception { + try (NetconfClient netconfClient = createSession(tcpAddress, "1")) { + Document rpcReply = netconfClient.sendMessage( + XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/editConfig_merge_yang-test.xml")) + .getDocument(); + assertEquals("rpc-reply", rpcReply.getDocumentElement().getTagName()); + assertIsOK(rpcReply); + assertGetConfigWorks(netconfClient, this.getConfigCandidate); + rpcReply = netconfClient.sendMessage(XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/commit.xml")) + .getDocument(); + assertIsOK(rpcReply); + + final ObjectName on = new ObjectName( + "org.opendaylight.controller:instanceName=impl-dep-instance,type=Module,moduleFactoryName=impl-dep"); + Set cfgBeans = configRegistryClient.lookupConfigBeans(); + assertEquals(cfgBeans, Sets.newHashSet(on)); + } + } + + private NetconfClient createSession(final InetSocketAddress address, final String expected) throws Exception { + final NetconfClient netconfClient = new NetconfClient("test " + address.toString(), address, 5000); + + assertEquals(expected, Long.toString(netconfClient.getSessionId())); + + return netconfClient; + } + +} diff --git a/opendaylight/netconf/netconf-it/src/test/resources/keystore.jks b/opendaylight/netconf/netconf-it/src/test/resources/keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..201d3758af81fc0e93e9a04d6dd32581b8dbea93 GIT binary patch literal 2251 zcmchY={wYo7sls1GsZGu#+rRkmY5+Uk0csdpRrDs=+}^CEJKZCD>d1d#+tH4RJO5n zkzGhx9!oP+NTlo;Swb;*rd~aN!SBU+aerQ%>sNHM&wVh4G@R}grY!MC_WgE8VrB}s$e7laDbo`=ptqiYm?G6#Q>?a+-U#m8sGxl zTIDZ)0_1R6vYJcF)mSUiHh+oCBpC}I&HqwPBWNEeyp#KI zciGb9z&g)f>0Ak|q}yPLlE#9sb-P~~1NfBl`anm85jrYU4AgM``MR#JgiU)5IKNa0 zdP`YZmv$X#NwABj^dXKVDKQ#|`|~^}IYU^HFr}=)GkkAiiX&Lq33ndf==f|u$q@ zMLkX8p3J)>UfkPPo85K#4c%cQHX*?>kAyp87tdPFRnonPK2*#3;@L&>w|B8~pN+JW zptgh0p7I&*H*ZWHr=8n5gYOs@#f7CcA5CKPHf%Ya>rk{aa2@`!0yAB15n0RaCM|Ui zy;!k-MNfBxnz`0Rv=q=$%d9Kj*(dGQq|2C%wWN!_eB6?_bQv)lO(H}1`4aO2t@N@- zE&LgT2NEQ@IRE$(_jJv2xw(;*s*=R@(PFA^>pY%hJ`EKkbOU$8J7zHsIs7OUAtn zdNU|yFrueKJ!8MVFJ2U1MX`OQmwd2j?;P{Z-fNjBcNNFwapTl$jE0)n_u^pDTcG;G z&w9iEtmsntYI^b-)myKgGS4dcFtPo$qQK-irp-=a?!f)7^FMS~?(TOr-H>SF26S&8ucMX z>Up?^po>bID>CD&e|<<>Nh- z@ZhBQb*m)QK2Hdt^H9V$-=DNa?V_>PAZ>+KMH^lST5W)ri^i9)30vi#R*W$Z^zza~;kmawn zr%~Guocljs;)impawi|~BxsXWfT>btS~vI+9p!s1s;-g9KpY;YIi zn!HtJYh$0OG|WiXv{(({)?OZxil^whTeeDl#+cNIy~pvz_v8t-=Y&8lhkWy9b~?rV z*WLDV*NUWWW8R@&gM!wDJazK<ez$Y?8XDrwG*u3AV zcd92&P{y$S60<#UkHL-PcC*Ky!R8XumqInVE^pMNftYt)O?vZ0lgO*5g{7C*xR9m1 zc&|pT(6xY4C6M<0@EvTf@CU=<@E_OVZz4yB*GzB-#e;s^Cq==S?9QRRNHx$}n*u3C zfNBtfknyG}EPzFD?Ce70{oLLMj%(HM^|Ed{pjL%@u#J%k+*2#G>hejW6U)ON4KkVU zfGdR%Kl5x_Z3Mkpc8X$VTF@s~`#m$QN4y#LgsE^dtnB^->GPjxe0WcWq5jPu(;)QXm#+t?M@B#pw9hiY=IEotv!9!t!V0%k662<>pa0znR z`v-;){D=g!APV^#aSHMnUn2w)$^L{80$La)_#5&F9gRsq6AbYH73eANl_uhyuv} z*A({0fKUK__A3E+z!U%g#p`_Um=7JG+yo|8rjVm)Gr^E!d`s!(Pn;?da89dU$&$#5+91wD`xR+=VFEFxt|sb%Gw5B z$KfH}UhTP;ozX$Nt|F9klaa6i6>M@YV!A-pY827RI)I39jM7Ex&(pmWOOA=AS|nzy zt}tuo+@+XA;jTK)UemC;N~yytU38V&=F&stlG_kcd^h~j%#;EYGqr6HxhW7=JTmT< bocGYXXzx9oyu~{mpywLs)wYyQ^CbNX=`G<> literal 0 HcmV?d00001 diff --git a/opendaylight/netconf/netconf-mapping-api/pom.xml b/opendaylight/netconf/netconf-mapping-api/pom.xml new file mode 100644 index 0000000000..5b6003f185 --- /dev/null +++ b/opendaylight/netconf/netconf-mapping-api/pom.xml @@ -0,0 +1,53 @@ + + + + + netconf-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + + 4.0.0 + netconf-mapping-api + ${project.artifactId} + + bundle + + + + ${project.groupId} + netconf-api + ${project.version} + + + + com.google.guava + guava + + + + + + + + org.apache.felix + maven-bundle-plugin + + + + + + com.google.common.base, + org.opendaylight.controller.netconf.api, + org.w3c.dom + + + org.opendaylight.controller.netconf.mapping.api, + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java new file mode 100644 index 0000000000..6351c617cf --- /dev/null +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/Capability.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mapping.api; + +import com.google.common.base.Optional; + +/** + * Contains capability URI announced by server hello message and optionally its + * corresponding yang schema that can be retrieved by get-schema rpc. + */ +public interface Capability { + + public String getCapabilityUri(); + + public Optional getModuleName(); + + public Optional getRevision(); + + public Optional getCapabilitySchema(); +} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java new file mode 100644 index 0000000000..5a0688c8d1 --- /dev/null +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mapping.api; + +import com.google.common.base.Optional; + +public class HandlingPriority implements Comparable { + + public static final HandlingPriority CANNOT_HANDLE = new HandlingPriority(); + public static final HandlingPriority HANDLE_WITH_DEFAULT_PRIORITY = new HandlingPriority(Integer.MIN_VALUE); + public static final HandlingPriority HANDLE_WITH_MAX_PRIORITY = new HandlingPriority(Integer.MAX_VALUE); + + private Integer priority; + + public static HandlingPriority getHandlingPriority(int priority) { + return new HandlingPriority(priority); + } + + private HandlingPriority(int priority) { + this.priority = priority; + } + + private HandlingPriority() { + } + + /** + * @return priority number or Optional.absent otherwise + */ + public Optional getPriority() { + return Optional.of(priority).or(Optional. absent()); + } + + // TODO test + + @Override + public int compareTo(HandlingPriority o) { + if (this == o) + return 0; + if (this == CANNOT_HANDLE) + return -1; + if (o == CANNOT_HANDLE) + return 1; + + if (priority > o.priority) + return 1; + if (priority == o.priority) + return 0; + if (priority < o.priority) + return -1; + + throw new IllegalStateException("Unexpected state"); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof HandlingPriority)) + return false; + + HandlingPriority that = (HandlingPriority) o; + + if (priority != null ? !priority.equals(that.priority) : that.priority != null) + return false; + + return true; + } + + @Override + public int hashCode() { + return priority != null ? priority.hashCode() : 0; + } +} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperation.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperation.java new file mode 100644 index 0000000000..58857b4438 --- /dev/null +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperation.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mapping.api; + +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.w3c.dom.Document; + +public interface NetconfOperation { + + HandlingPriority canHandle(Document message); + + Document handle(Document message, NetconfOperationRouter operationRouter) throws NetconfDocumentedException; +} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilter.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilter.java new file mode 100644 index 0000000000..22a730726d --- /dev/null +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilter.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mapping.api; + +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.w3c.dom.Document; + +/** + * Filters wrap each netconf operation, if there is one found. Filters are + * sorted and applied from the greatest to smallest sorting order. + */ +public interface NetconfOperationFilter extends Comparable { + + Document doFilter(Document message, NetconfOperationRouter operationRouter, NetconfOperationFilterChain filterChain) + throws NetconfDocumentedException; + + int getSoringOrder(); + +} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilterChain.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilterChain.java new file mode 100644 index 0000000000..7a0eb9150d --- /dev/null +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationFilterChain.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mapping.api; + +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.w3c.dom.Document; + +public interface NetconfOperationFilterChain { + + Document execute(Document message, NetconfOperationRouter operationRouter) throws NetconfDocumentedException; + +} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java new file mode 100644 index 0000000000..858636a389 --- /dev/null +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationService.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mapping.api; + +import java.util.Set; + +/** + * + */ +public interface NetconfOperationService extends AutoCloseable { + + /** + * Get capabilities announced by server hello message. + */ + Set getCapabilities(); + + /** + * Get set of netconf operations that are handled by this service. + */ + Set getNetconfOperations(); + + Set getFilters(); + + /** + * Called when netconf session is destroyed. + */ + @Override + void close(); + +} diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java new file mode 100644 index 0000000000..46b9cd22e0 --- /dev/null +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mapping.api; + +/** + * Factory that must be registered in OSGi service registry in order to be used + * by netconf-impl. Responsible for creating per-session instances of + * {@link NetconfOperationService}. + */ +public interface NetconfOperationServiceFactory { + + NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting); + +} diff --git a/opendaylight/netconf/netconf-util/pom.xml b/opendaylight/netconf/netconf-util/pom.xml new file mode 100644 index 0000000000..cb198e7361 --- /dev/null +++ b/opendaylight/netconf/netconf-util/pom.xml @@ -0,0 +1,117 @@ + + 4.0.0 + + + netconf-subsystem + org.opendaylight.controller + 0.2.1-SNAPSHOT + + netconf-util + ${project.artifactId} + bundle + + + + + + ${project.groupId} + netconf-api + ${project.version} + + + ${project.groupId} + netconf-mapping-api + ${project.version} + + + org.opendaylight.controller + config-api + 0.2.1-SNAPSHOT + + + org.opendaylight.bgpcep + framework + + + org.opendaylight.bgpcep + util + + + + org.osgi + org.osgi.core + + + com.google.guava + guava + + + org.slf4j + slf4j-api + + + + + + + org.apache.felix + maven-bundle-plugin + + + + org.opendaylight.controller.netconf.util, + org.opendaylight.controller.netconf.util.xml, + org.opendaylight.controller.netconf.util.osgi, + org.opendaylight.controller.netconf.util.mapping, + org.opendaylight.controller.netconf.util.messages, + + + org.opendaylight.controller.config.stat, + com.google.common.base, + com.google.common.collect, + io.netty.buffer, + io.netty.channel, + io.netty.channel.socket, + io.netty.handler.codec, + io.netty.handler.ssl, + io.netty.util, + io.netty.util.concurrent, + javax.annotation, + javax.net.ssl, + javax.xml.namespace, + javax.xml.parsers, + javax.xml.transform, + javax.xml.transform.dom, + javax.xml.transform.stream, + javax.xml.validation, + javax.xml.xpath, + org.opendaylight.controller.netconf.api, + org.opendaylight.controller.netconf.mapping.api, + org.opendaylight.protocol.framework, + org.opendaylight.protocol.util, + org.osgi.framework, + org.slf4j, + org.w3c.dom, + org.xml.sax, + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + package + + test-jar + + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractChannelInitializer.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractChannelInitializer.java new file mode 100644 index 0000000000..317a126bba --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractChannelInitializer.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; + +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfSession; +import org.opendaylight.controller.netconf.util.messages.NetconfMessageFactory; +import org.opendaylight.protocol.framework.ProtocolHandlerFactory; +import org.opendaylight.protocol.framework.ProtocolMessageDecoder; +import org.opendaylight.protocol.framework.ProtocolMessageEncoder; + +import com.google.common.base.Optional; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.ssl.SslHandler; +import io.netty.util.concurrent.Promise; + +public abstract class AbstractChannelInitializer { + + private final Optional maybeContext; + private final NetconfHandlerFactory handlerFactory; + + public AbstractChannelInitializer(Optional maybeContext) { + this.maybeContext = maybeContext; + this.handlerFactory = new NetconfHandlerFactory(new NetconfMessageFactory()); + } + + public void initialize(SocketChannel ch, Promise promise) { + if (maybeContext.isPresent()) { + initSsl(ch); + } + + ch.pipeline().addLast("frameDecoder", NetconfMessageFactory.getDelimiterFrameDecoder()); + ch.pipeline().addLast(handlerFactory.getDecoders()); + initializeAfterDecoder(ch, promise); + ch.pipeline().addLast(handlerFactory.getEncoders()); + } + + protected abstract void initializeAfterDecoder(SocketChannel ch, Promise promise); + + private void initSsl(SocketChannel ch) { + SSLEngine sslEngine = maybeContext.get().createSSLEngine(); + initSslEngine(sslEngine); + final SslHandler handler = new SslHandler(sslEngine); + ch.pipeline().addLast("ssl", handler); + } + + protected abstract void initSslEngine(SSLEngine sslEngine); + + private static final class NetconfHandlerFactory extends ProtocolHandlerFactory { + + public NetconfHandlerFactory(final NetconfMessageFactory msgFactory) { + super(msgFactory); + } + + @Override + public ChannelHandler[] getEncoders() { + return new ChannelHandler[] { new ProtocolMessageEncoder(this.msgFactory) }; + } + + @Override + public ChannelHandler[] getDecoders() { + return new ChannelHandler[] { new ProtocolMessageDecoder(this.msgFactory) }; + } + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java new file mode 100644 index 0000000000..9069d85d88 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/AbstractNetconfSessionNegotiator.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.netty.channel.Channel; +import io.netty.handler.ssl.SslHandler; +import io.netty.util.Timeout; +import io.netty.util.Timer; +import io.netty.util.TimerTask; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import io.netty.util.concurrent.Promise; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfSession; +import org.opendaylight.controller.netconf.api.NetconfSessionPreferences; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.AbstractSessionNegotiator; +import org.opendaylight.protocol.framework.SessionListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; + +import java.util.concurrent.TimeUnit; + +public abstract class AbstractNetconfSessionNegotiator

+ extends AbstractSessionNegotiator { + + // TODO what time ? + private static final long INITIAL_HOLDTIMER = 1; + private static final Logger logger = LoggerFactory.getLogger(AbstractNetconfSessionNegotiator.class); + + protected final P sessionPreferences; + + private final SessionListener sessionListener; + + /** + * Possible states for Finite State Machine + */ + private enum State { + IDLE, OPEN_WAIT, FAILED, ESTABLISHED + } + + private State state = State.IDLE; + private final Timer timer; + + protected AbstractNetconfSessionNegotiator(P sessionPreferences, Promise promise, Channel channel, Timer timer, + SessionListener sessionListener) { + super(promise, channel); + this.sessionPreferences = sessionPreferences; + this.timer = timer; + this.sessionListener = sessionListener; + } + + @Override + protected void startNegotiation() throws Exception { + final Optional sslHandler = getSslHandler(channel); + if (sslHandler.isPresent()) { + Future future = sslHandler.get().handshakeFuture(); + future.addListener(new GenericFutureListener>() { + @Override + public void operationComplete(Future future) throws Exception { + Preconditions.checkState(future.isSuccess(), "Ssl handshake was not successful"); + logger.debug("Ssl handshake complete"); + start(); + } + }); + } else + start(); + } + + private static Optional getSslHandler(Channel channel) { + final SslHandler sslHandler = channel.pipeline().get(SslHandler.class); + return sslHandler == null ? Optional. absent() : Optional.of(sslHandler); + } + + private void start() { + final NetconfMessage helloMessage = this.sessionPreferences.getHelloMessage(); + logger.debug("Session negotiation started with hello message {}", XmlUtil.toString(helloMessage.getDocument())); + + sendMessage(helloMessage); + changeState(State.OPEN_WAIT); + + this.timer.newTimeout(new TimerTask() { + @Override + public void run(final Timeout timeout) throws Exception { + synchronized (this) { + if (state != State.ESTABLISHED) { + final IllegalStateException cause = new IllegalStateException( + "Session was not established after " + timeout); + negotiationFailed(cause); + changeState(State.FAILED); + } + } + } + }, INITIAL_HOLDTIMER, TimeUnit.MINUTES); + } + + private void sendMessage(NetconfMessage message) { + this.channel.writeAndFlush(message); + } + + @Override + protected void handleMessage(NetconfMessage netconfMessage) { + final Document doc = netconfMessage.getDocument(); + + if (isHelloMessage(doc)) { + changeState(State.ESTABLISHED); + S session = getSession(sessionListener, channel, doc); + negotiationSuccessful(session); + } else { + final IllegalStateException cause = new IllegalStateException( + "Received message was not hello as expected, but was " + XmlUtil.toString(doc)); + negotiationFailed(cause); + } + } + + protected abstract S getSession(SessionListener sessionListener, Channel channel, Document doc); + + private boolean isHelloMessage(Document doc) { + try { + XmlElement.fromDomElementWithExpected(doc.getDocumentElement(), "hello", + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + + } catch (IllegalArgumentException | IllegalStateException e) { + return false; + } + return true; + } + + private void changeState(final State newState) { + logger.debug("Changing state from : {} to : {}", state, newState); + Preconditions.checkState(isStateChangePermitted(state, newState), "Cannot change state from %s to %s", state, + newState); + this.state = newState; + } + + private static boolean isStateChangePermitted(State state, State newState) { + if (state == State.IDLE && newState == State.OPEN_WAIT) + return true; + if (state == State.OPEN_WAIT && newState == State.ESTABLISHED) + return true; + if (state == State.OPEN_WAIT && newState == State.FAILED) + return true; + + return false; + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java new file mode 100644 index 0000000000..5850e64a05 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/mapping/AbstractNetconfOperation.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.mapping; + +import java.util.Map; + +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfOperationRouter; +import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public abstract class AbstractNetconfOperation implements NetconfOperation { + private final String netconfSessionIdForReporting; + + protected AbstractNetconfOperation(String netconfSessionIdForReporting) { + this.netconfSessionIdForReporting = netconfSessionIdForReporting; + } + + public String getNetconfSessionIdForReporting() { + return netconfSessionIdForReporting; + } + + @Override + public HandlingPriority canHandle(Document message) { + OperationNameAndNamespace operationNameAndNamespace = new OperationNameAndNamespace(message); + return canHandle(operationNameAndNamespace.getOperationName(), operationNameAndNamespace.getNamespace()); + } + + public static class OperationNameAndNamespace { + private final String operationName, namespace; + + public OperationNameAndNamespace(Document message) { + XmlElement requestElement = getRequestElementWithCheck(message); + + XmlElement operationElement = requestElement.getOnlyChildElement(); + operationName = operationElement.getName(); + namespace = operationElement.getNamespace(); + } + + public String getOperationName() { + return operationName; + } + + public String getNamespace() { + return namespace; + } + } + + protected static XmlElement getRequestElementWithCheck(Document message) { + return XmlElement.fromDomElementWithExpected(message.getDocumentElement(), XmlNetconfConstants.RPC_KEY, + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + } + + protected abstract HandlingPriority canHandle(String operationName, String netconfOperationNamespace); + + @Override + public Document handle(Document message, NetconfOperationRouter opRouter) throws NetconfDocumentedException { + + XmlElement requestElement = getRequestElementWithCheck(message); + + Document document = XmlUtil.newDocument(); + + XmlElement operationElement = requestElement.getOnlyChildElement(); + Map attributes = requestElement.getAttributes(); + + Element response = handle(document, operationElement, opRouter); + + Element rpcReply = document.createElementNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, + XmlNetconfConstants.RPC_REPLY_KEY); + rpcReply.appendChild(response); + + for (String attrName : attributes.keySet()) { + rpcReply.setAttribute(attrName, attributes.get(attrName).getNodeValue()); + } + + document.appendChild(rpcReply); + return document; + } + + protected abstract Element handle(Document document, XmlElement operationElement, NetconfOperationRouter opRouter) + throws NetconfDocumentedException; + + @Override + public String toString() { + return getClass() + "{" + netconfSessionIdForReporting + '}'; + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/FramingMechanism.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/FramingMechanism.java new file mode 100644 index 0000000000..c118ca81ef --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/FramingMechanism.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.messages; + +/** + * Known NETCONF framing mechanisms. + */ +public enum FramingMechanism { + /** + * @see Chunked + * framing mechanism + */ + CHUNK, + /** + * @see End-of-message + * framing mechanism + */ + EOM +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java new file mode 100644 index 0000000000..ca3079bb16 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageFactory.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.messages; + +import com.google.common.base.Charsets; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandler; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import org.opendaylight.controller.netconf.api.NetconfDeserializerException; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.protocol.framework.DeserializerException; +import org.opendaylight.protocol.framework.DocumentedException; +import org.opendaylight.protocol.framework.ProtocolMessageFactory; +import org.opendaylight.protocol.util.ByteArray; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Comment; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; + +/** + * NetconfMessageFactory for (de)serializing DOM documents. + */ +public final class NetconfMessageFactory implements ProtocolMessageFactory { + + private static final Logger logger = LoggerFactory.getLogger(NetconfMessageFactory.class); + + public static final byte[] endOfMessage = "]]>]]>".getBytes(Charsets.UTF_8); + + public static final byte[] endOfChunk = "\n##\n".getBytes(Charsets.UTF_8); + + private static final int MAX_CHUNK_SIZE = 1024; // Bytes + + private FramingMechanism framing = FramingMechanism.EOM; + + private final Optional clientId; + + public NetconfMessageFactory() { + clientId = Optional.absent(); + } + + public NetconfMessageFactory(Optional clientId) { + this.clientId = clientId; + } + + public static ChannelHandler getDelimiterFrameDecoder() { + return new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer(endOfMessage)); + } + + @Override + public List parse(byte[] bytes) throws DeserializerException, DocumentedException { + String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString(); + logger.debug("Parsing message \n{}", s); + if (bytes[0] == '[') { + // yuma sends auth information in the first line. Ignore until ]\n + // is found. + int endOfAuthHeader = ByteArray.findByteSequence(bytes, new byte[] { ']', '\n' }); + if (endOfAuthHeader > -1) { + bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length); + } + } + List messages = Lists.newArrayList(); + try { + Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes)); + messages.add(new NetconfMessage(doc)); + } catch (final SAXException | IOException | IllegalStateException e) { + throw new NetconfDeserializerException("Could not parse message from " + new String(bytes), e); + } + return messages; + } + + @Override + public byte[] put(NetconfMessage netconfMessage) { + if (clientId.isPresent()) { + Comment comment = netconfMessage.getDocument().createComment("clientId:" + clientId.get()); + netconfMessage.getDocument().appendChild(comment); + } + byte[] bytes = (this.framing == FramingMechanism.EOM) ? this.putEOM(netconfMessage) : this + .putChunked(netconfMessage); + String content = xmlToString(netconfMessage.getDocument()); + + logger.trace("Putting message \n{}", content); + return bytes; + } + + private byte[] putEOM(NetconfMessage msg) { + // create byte buffer from the String XML + // all Netconf messages are encoded using UTF-8 + final ByteBuffer msgBytes = Charsets.UTF_8.encode(xmlToString(msg.getDocument())); + final ByteBuffer result = ByteBuffer.allocate(msgBytes.limit() + endOfMessage.length); + result.put(msgBytes); + // put end of message + result.put(endOfMessage); + return result.array(); + } + + private byte[] putChunked(NetconfMessage msg) { + final ByteBuffer msgBytes = Charsets.UTF_8.encode(xmlToString(msg.getDocument())); + final NetconfMessageHeader h = new NetconfMessageHeader(); + if (msgBytes.limit() > MAX_CHUNK_SIZE) + logger.warn("Netconf message too long, should be split."); + h.setLength(msgBytes.limit()); + final byte[] headerBytes = h.toBytes(); + final ByteBuffer result = ByteBuffer.allocate(headerBytes.length + msgBytes.limit() + endOfChunk.length); + result.put(headerBytes); + result.put(msgBytes); + result.put(endOfChunk); + return result.array(); + } + + private String xmlToString(Document doc) { + return XmlUtil.toString(doc, false); + } + + /** + * For Hello message the framing is always EOM, but the framing mechanism + * may change. + * + * @param fm + * new framing mechanism + */ + public void setFramingMechanism(final FramingMechanism fm) { + logger.debug("Framing mechanism changed to {}", fm); + this.framing = fm; + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageHeader.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageHeader.java new file mode 100644 index 0000000000..ccf7a3fbd2 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageHeader.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.messages; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import org.opendaylight.protocol.util.ByteArray; + +import java.nio.ByteBuffer; + +/** + * Netconf message header is used only when chunked framing mechanism is + * supported. The header consists of only the length field. + */ +public final class NetconfMessageHeader { + + private long length; + + // \n#\n + private static final byte[] headerBegin = new byte[] { (byte) 0x0a, (byte) 0x23 }; + + private static final byte headerEnd = (byte) 0x0a; + + public static final int MIN_HEADER_LENGTH = 4; // bytes + + public static final int MAX_HEADER_LENGTH = 13; // bytes + + private boolean parsed = false; + + public NetconfMessageHeader() { + + } + + public NetconfMessageHeader fromBytes(final byte[] bytes) { + // the length is variable therefore bytes between headerBegin and + // headerEnd mark the length + // the length should be only numbers and therefore easily parsed with + // ASCII + this.length = Long.parseLong(Charsets.US_ASCII.decode( + ByteBuffer.wrap(ByteArray.subByte(bytes, headerBegin.length, bytes.length - headerBegin.length - 1))) + .toString()); + Preconditions.checkState(this.length < Integer.MAX_VALUE && this.length > 0); + this.parsed = true; + return this; + } + + public byte[] toBytes() { + final byte[] l = String.valueOf(this.length).getBytes(Charsets.US_ASCII); + final byte[] h = new byte[headerBegin.length + l.length + 1]; + System.arraycopy(headerBegin, 0, h, 0, headerBegin.length); + System.arraycopy(l, 0, h, headerBegin.length, l.length); + System.arraycopy(new byte[] { headerEnd }, 0, h, headerBegin.length + l.length, 1); + return h; + } + + // FIXME: improve precision to long + public int getLength() { + return (int) this.length; + } + + public void setLength(final int length) { + this.length = length; + } + + /** + * @return the parsed + */ + public boolean isParsed() { + return this.parsed; + } + + /** + * @param parsed + * the parsed to set + */ + public void setParsed() { + this.parsed = false; + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageUtil.java new file mode 100644 index 0000000000..46053e734e --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageUtil.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.messages; + +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.w3c.dom.Document; + +public class NetconfMessageUtil { + + public static boolean isOKMessage(NetconfMessage message) { + return isOKMessage(message.getDocument()); + } + + public static boolean isOKMessage(Document document) { + return isOKMessage(XmlElement.fromDomDocument(document)); + } + + public static boolean isOKMessage(XmlElement xmlElement) { + return xmlElement.getOnlyChildElement().getName().equals(XmlNetconfConstants.OK); + } + + public static boolean isErrorMEssage(NetconfMessage message) { + return isErrorMessage(message.getDocument()); + } + + public static boolean isErrorMessage(Document document) { + return isErrorMessage(XmlElement.fromDomDocument(document)); + } + + public static boolean isErrorMessage(XmlElement xmlElement) { + return xmlElement.getOnlyChildElement().getName().equals(XmlNetconfConstants.RPC_ERROR); + + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java new file mode 100644 index 0000000000..639b428e09 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/SendErrorExceptionUtil.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.messages; + +import com.google.common.base.Preconditions; +import io.netty.channel.Channel; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfSession; +import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil; +import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.*; + +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import java.io.InputStream; +import java.util.Map.Entry; + +public class SendErrorExceptionUtil { + private static final Logger logger = LoggerFactory.getLogger(SendErrorExceptionUtil.class); + + public static void sendErrorMessage(final NetconfSession session, + final NetconfDocumentedException sendErrorException) { + logger.info("Sending error {}", sendErrorException.getMessage(), sendErrorException); + final Document errorDocument = createDocument(sendErrorException); + session.sendMessage(new NetconfMessage(errorDocument)); + } + + public static void sendErrorMessage(Channel channel, NetconfDocumentedException sendErrorException) { + logger.info("Sending error {}", sendErrorException.getMessage(), sendErrorException); + final Document errorDocument = createDocument(sendErrorException); + channel.writeAndFlush(new NetconfMessage(errorDocument)); + } + + public static void sendErrorMessage(NetconfSession session, NetconfDocumentedException sendErrorException, + NetconfMessage incommingMessage) { + final Document errorDocument = createDocument(sendErrorException); + logger.info("Sending error {}", XmlUtil.toString(errorDocument)); + tryToCopyAttributes(incommingMessage.getDocument(), errorDocument, sendErrorException); + session.sendMessage(new NetconfMessage(errorDocument)); + } + + private static void tryToCopyAttributes(final Document incommingDocument, final Document errorDocument, + final NetconfDocumentedException sendErrorException) { + try { + final Element incommingRpc = incommingDocument.getDocumentElement(); + Preconditions.checkState(incommingRpc.getTagName().equals(XmlNetconfConstants.RPC_KEY), "Missing " + + XmlNetconfConstants.RPC_KEY + " " + "element"); + + final Element rpcReply = errorDocument.getDocumentElement(); + Preconditions.checkState(rpcReply.getTagName().equals(XmlNetconfConstants.RPC_REPLY_KEY), "Missing " + + XmlNetconfConstants.RPC_REPLY_KEY + " element"); + + final NamedNodeMap incomingAttributes = incommingRpc.getAttributes(); + for (int i = 0; i < incomingAttributes.getLength(); i++) { + final Attr attr = (Attr) incomingAttributes.item(i); + // skip namespace + if (attr.getNodeName().equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) + continue; + rpcReply.setAttributeNode((Attr) errorDocument.importNode(attr, true)); + } + } catch (final Exception e) { + logger.warn("Unable to copy incomming attributes to {}, returned rpc-error might be invalid for client", + sendErrorException, e); + } + } + + private static XPathExpression rpcErrorExpression = XMLNetconfUtil + .compileXPath("/netconf:rpc-reply/netconf:rpc-error"); + private static XPathExpression errorTypeExpression = XMLNetconfUtil.compileXPath("netconf:error-type"); + private static XPathExpression errorTagExpression = XMLNetconfUtil.compileXPath("netconf:error-tag"); + private static XPathExpression errorSeverityExpression = XMLNetconfUtil.compileXPath("netconf:error-severity"); + + private static Document createDocument(final NetconfDocumentedException sendErrorException) { + + final InputStream errIS = SendErrorExceptionUtil.class.getResourceAsStream("server_error.xml"); + Document originalErrorDocument; + try { + originalErrorDocument = XmlUtil.readXmlToDocument(errIS); + } catch (final Exception e) { + throw new IllegalStateException(e); + } + + final Document errorDocument = XmlUtil.createDocumentCopy(originalErrorDocument); + final Node rootNode = errorDocument.getFirstChild(); + + final Node rpcErrorNode = (Node) XmlUtil.evaluateXPath(rpcErrorExpression, rootNode, XPathConstants.NODE); + + final Node errorTypeNode = (Node) XmlUtil.evaluateXPath(errorTypeExpression, rpcErrorNode, XPathConstants.NODE); + errorTypeNode.setTextContent(sendErrorException.getErrorType().getTagValue()); + + final Node errorTagNode = (Node) XmlUtil.evaluateXPath(errorTagExpression, rpcErrorNode, XPathConstants.NODE); + errorTagNode.setTextContent(sendErrorException.getErrorTag().getTagValue()); + + final Node errorSeverityNode = (Node) XmlUtil.evaluateXPath(errorSeverityExpression, rpcErrorNode, + XPathConstants.NODE); + errorSeverityNode.setTextContent(sendErrorException.getErrorSeverity().getTagValue()); + + if (sendErrorException.getErrorInfo() != null && sendErrorException.getErrorInfo().isEmpty() == false) { + /* + * message-id + * rpc + */ + final Node errorInfoNode = errorDocument.createElementNS( + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, "error-info"); + + errorInfoNode.setPrefix(rootNode.getPrefix()); + rpcErrorNode.appendChild(errorInfoNode); + for (final Entry errorInfoEntry : sendErrorException.getErrorInfo().entrySet()) { + final Node node = errorDocument.createElementNS( + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, errorInfoEntry.getKey()); + node.setTextContent(errorInfoEntry.getValue()); + errorInfoNode.appendChild(node); + } + + } + return errorDocument; + } + +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java new file mode 100644 index 0000000000..5c9d823cc0 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/osgi/NetconfConfigUtil.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.osgi; + +import com.google.common.base.Optional; +import org.opendaylight.controller.config.stat.ConfigProvider; +import org.opendaylight.protocol.util.SSLUtil; + +import javax.net.ssl.SSLContext; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.InetSocketAddress; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +public class NetconfConfigUtil { + private static final String PREFIX_PROP = "netconf."; + + private enum InfixProp { + tcp, tls + } + + private static final String PORT_SUFFIX_PROP = ".port"; + private static final String ADDRESS_SUFFIX_PROP = ".address"; + + private static final String NETCONF_TLS_KEYSTORE_PROP = PREFIX_PROP + InfixProp.tls + ".keystore"; + private static final String NETCONF_TLS_KEYSTORE_PASSWORD_PROP = NETCONF_TLS_KEYSTORE_PROP + ".password"; + + public static Optional extractTCPNetconfAddress(ConfigProvider configProvider) { + return extractSomeNetconfAddress(configProvider, InfixProp.tcp); + } + + public static Optional extractTLSConfiguration(ConfigProvider configProvider) { + Optional address = extractSomeNetconfAddress(configProvider, InfixProp.tls); + if (address.isPresent()) { + String keystoreFileName = configProvider.getProperty(NETCONF_TLS_KEYSTORE_PROP); + File keystoreFile = new File(keystoreFileName); + checkState(keystoreFile.exists() && keystoreFile.isFile() && keystoreFile.canRead(), + "Keystore file %s does not exist or is not readable file", keystoreFileName); + keystoreFile = keystoreFile.getAbsoluteFile(); + String keystorePassword = configProvider.getProperty(NETCONF_TLS_KEYSTORE_PASSWORD_PROP); + checkNotNull(keystoreFileName, "Property %s must be defined for tls netconf server", + NETCONF_TLS_KEYSTORE_PROP); + keystorePassword = keystorePassword != null ? keystorePassword : ""; + return Optional.of(new TLSConfiguration(address.get(), keystoreFile, keystorePassword)); + } else { + return Optional.absent(); + } + } + + public static class TLSConfiguration { + private final InetSocketAddress address; + private final File keystoreFile; + private final String keystorePassword; + private final SSLContext sslContext; + + TLSConfiguration(InetSocketAddress address, File keystoreFile, String keystorePassword) { + this.address = address; + this.keystoreFile = keystoreFile; + this.keystorePassword = keystorePassword; + try { + try (InputStream keyStoreIS = new FileInputStream(keystoreFile)) { + try (InputStream trustStoreIS = new FileInputStream(keystoreFile)) { + sslContext = SSLUtil.initializeSecureContext("password", keyStoreIS, trustStoreIS, "SunX509"); + } + } + } catch (Exception e) { + throw new RuntimeException("Cannot initialize ssl context for netconf file " + keystoreFile, e); + } + } + + public SSLContext getSslContext() { + return sslContext; + } + + public InetSocketAddress getAddress() { + return address; + } + + public File getKeystoreFile() { + return keystoreFile; + } + + public String getKeystorePassword() { + return keystorePassword; + } + } + + /** + * @param configProvider + * from which properties are being read. + * @param infixProp + * either tcp or tls + * @return absent if address is missing, value if address and port are + * valid. + * @throws IllegalStateException + * if address or port are invalid + */ + private static Optional extractSomeNetconfAddress(ConfigProvider configProvider, + InfixProp infixProp) { + String address = configProvider.getProperty(PREFIX_PROP + infixProp + ADDRESS_SUFFIX_PROP); + if (address == null) { + return Optional.absent(); + } + String portKey = PREFIX_PROP + infixProp + PORT_SUFFIX_PROP; + String portString = configProvider.getProperty(portKey); + checkNotNull(portString, "Netconf port must be specified in properties file with " + portKey); + try { + int port = Integer.valueOf(portString); + return Optional.of(new InetSocketAddress(address, port)); + } catch (RuntimeException e) { + throw new IllegalStateException("Cannot create " + infixProp + " netconf address from address:" + address + + " and port:" + portString, e); + } + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolver.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolver.java new file mode 100644 index 0000000000..23fe7cdf41 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/HardcodedNamespaceResolver.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.xml; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +import javax.xml.namespace.NamespaceContext; + +import com.google.common.collect.ImmutableMap; + +// http://www.ibm.com/developerworks/library/x-nmspccontext/ +public class HardcodedNamespaceResolver implements NamespaceContext { + private final Map prefixesToNamespaces; + + public HardcodedNamespaceResolver(String prefix, String namespace) { + this(ImmutableMap.of(prefix, namespace)); + } + + public HardcodedNamespaceResolver(Map prefixesToNamespaces) { + this.prefixesToNamespaces = Collections.unmodifiableMap(prefixesToNamespaces); + } + + /** + * This method returns the uri for all prefixes needed. Wherever possible it + * uses XMLConstants. + * + * @param prefix + * @return uri + */ + @Override + public String getNamespaceURI(String prefix) { + if (prefixesToNamespaces.containsKey(prefix)) { + return prefixesToNamespaces.get(prefix); + } else { + throw new IllegalStateException("Prefix mapping not found for " + prefix); + } + } + + @Override + public String getPrefix(String namespaceURI) { + // Not needed in this context. + return null; + } + + @Override + public Iterator getPrefixes(String namespaceURI) { + // Not needed in this context. + return null; + } + +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XMLNetconfUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XMLNetconfUtil.java new file mode 100644 index 0000000000..d9303228f9 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XMLNetconfUtil.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.xml; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +public class XMLNetconfUtil { + + public static XPathExpression compileXPath(String xPath) { + final XPathFactory xPathfactory = XPathFactory.newInstance(); + final XPath xpath = xPathfactory.newXPath(); + xpath.setNamespaceContext(new HardcodedNamespaceResolver("netconf", + XmlNetconfConstants.RFC4741_TARGET_NAMESPACE)); + try { + return xpath.compile(xPath); + } catch (final XPathExpressionException e) { + throw new IllegalStateException("Error while compiling xpath expression " + xPath, e); + } + } + +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java new file mode 100644 index 0000000000..5a7fde4e3d --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlElement.java @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.xml; + +import java.io.IOException; +import java.util.*; + +import javax.annotation.Nullable; + +import org.w3c.dom.*; +import org.xml.sax.SAXException; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +public class XmlElement { + + public final Element element; + + private XmlElement(Element element) { + this.element = element; + } + + public static XmlElement fromDomElement(Element e) { + return new XmlElement(e); + } + + public static XmlElement fromDomDocument(Document xml) { + return new XmlElement(xml.getDocumentElement()); + } + + public static XmlElement fromString(String s) { + try { + return new XmlElement(XmlUtil.readXmlToElement(s)); + } catch (IOException | SAXException e) { + throw new IllegalArgumentException("Unable to create from " + s, e); + } + } + + public static XmlElement fromDomElementWithExpected(Element element, String expectedName) { + XmlElement xmlElement = XmlElement.fromDomElement(element); + xmlElement.checkName(expectedName); + return xmlElement; + } + + public static XmlElement fromDomElementWithExpected(Element element, String expectedName, String expectedNamespace) { + XmlElement xmlElement = XmlElement.fromDomElementWithExpected(element, expectedName); + xmlElement.checkNamespace(expectedNamespace); + return xmlElement; + } + + private static Map extractNamespaces(Element typeElement) { + Map namespaces = new HashMap<>(); + NamedNodeMap attributes = typeElement.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Node attribute = attributes.item(i); + String attribKey = attribute.getNodeName(); + if (attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY)) { + String prefix; + if (attribKey.equals(XmlUtil.XMLNS_ATTRIBUTE_KEY)) { + prefix = ""; + } else { + Preconditions.checkState(attribKey.startsWith(XmlUtil.XMLNS_ATTRIBUTE_KEY + ":")); + prefix = attribKey.substring(XmlUtil.XMLNS_ATTRIBUTE_KEY.length() + 1); + } + namespaces.put(prefix, attribute.getNodeValue()); + } + } + return namespaces; + } + + public void checkName(String expectedName) { + Preconditions.checkArgument(getName().equals(expectedName), "Expected %s xml element but was %s", expectedName, + getName()); + } + + public void checkNamespaceAttribute(String expectedNamespace) { + Preconditions.checkArgument(getNamespaceAttribute().equals(expectedNamespace), + "Unexpected namespace %s for element %s, should be %s", getNamespaceAttribute(), expectedNamespace); + } + + public void checkNamespace(String expectedNamespace) { + Preconditions.checkArgument(getNamespace().equals(expectedNamespace), + "Unexpected namespace %s for element %s, should be %s", getNamespace(), expectedNamespace); + } + + public String getName() { + return element.getTagName(); + } + + public String getAttribute(String attributeName) { + return element.getAttribute(attributeName); + } + + public String getAttribute(String attributeName, String namespace) { + return element.getAttributeNS(namespace, attributeName); + } + + public void appendChild(Element element) { + this.element.appendChild(element); + // Element newElement = (Element) element.cloneNode(true); + // newElement.appendChild(configElement); + // return XmlElement.fromDomElement(newElement); + } + + public Element getDomElement() { + return element; + } + + public Map getAttributes() { + + Map mappedAttributes = Maps.newHashMap(); + + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + Attr attr = (Attr) attributes.item(i); + mappedAttributes.put(attr.getNodeName(), attr); + } + + return mappedAttributes; + } + + /** + * Non recursive + */ + private List getChildElementsInternal(ElementFilteringStrategy strat) { + NodeList childNodes = element.getChildNodes(); + final List result = new ArrayList<>(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node item = childNodes.item(i); + if (item instanceof Element == false) + continue; + if (strat.accept((Element) item)) + result.add(new XmlElement((Element) item)); + } + + return result; + } + + public List getChildElements() { + return getChildElementsInternal(new ElementFilteringStrategy() { + @Override + public boolean accept(Element e) { + return true; + } + }); + } + + public List getChildElementsWithinNamespace(final String childName, String namespace) { + return Lists.newArrayList(Collections2.filter(getChildElementsWithinNamespace(namespace), + new Predicate() { + @Override + public boolean apply(@Nullable XmlElement xmlElement) { + return xmlElement.getName().equals(childName); + } + })); + } + + public List getChildElementsWithinNamespace(final String namespace) { + return getChildElementsInternal(new ElementFilteringStrategy() { + @Override + public boolean accept(Element e) { + return XmlElement.fromDomElement(e).getNamespace().equals(namespace); + } + + }); + } + + public List getChildElements(final String tagName) { + return getChildElementsInternal(new ElementFilteringStrategy() { + @Override + public boolean accept(Element e) { + return e.getTagName().equals(tagName); + } + }); + } + + public XmlElement getOnlyChildElement(String childName) { + List nameElements = getChildElements(childName); + Preconditions.checkState(nameElements.size() == 1, "One element " + childName + " expected in " + toString()); + return nameElements.get(0); + } + + public Optional getOnlyChildElementOptionally(String childName) { + try { + return Optional.of(getOnlyChildElement(childName)); + } catch (Exception e) { + return Optional.absent(); + } + } + + public Optional getOnlyChildElementOptionally(String childName, String namespace) { + try { + return Optional.of(getOnlyChildElement(childName, namespace)); + } catch (Exception e) { + return Optional.absent(); + } + } + + public XmlElement getOnlyChildElementWithSameNamespace(String childName) { + return getOnlyChildElement(childName, getNamespace()); + } + + public Optional getOnlyChildElementWithSameNamespaceOptionally(String childName) { + try { + return Optional.of(getOnlyChildElement(childName, getNamespace())); + } catch (Exception e) { + return Optional.absent(); + } + } + + public XmlElement getOnlyChildElementWithSameNamespace() { + XmlElement childElement = getOnlyChildElement(); + childElement.checkNamespace(getNamespace()); + return childElement; + } + + public Optional getOnlyChildElementWithSameNamespaceOptionally() { + try { + XmlElement childElement = getOnlyChildElement(); + childElement.checkNamespace(getNamespace()); + return Optional.of(childElement); + } catch (Exception e) { + return Optional.absent(); + } + } + + public XmlElement getOnlyChildElement(final String childName, String namespace) { + List children = getChildElementsWithinNamespace(namespace); + children = Lists.newArrayList(Collections2.filter(children, new Predicate() { + @Override + public boolean apply(@Nullable XmlElement xmlElement) { + return xmlElement.getName().equals(childName); + } + })); + Preconditions.checkState(children.size() == 1, "One element %s:%s expected in %s but was %s", namespace, + childName, toString(), children.size()); + return children.get(0); + } + + public XmlElement getOnlyChildElement() { + List children = getChildElements(); + Preconditions.checkState(children.size() == 1, "One element expected in %s but was %s", toString(), + children.size()); + return children.get(0); + } + + public String getTextContent() { + Node textChild = element.getFirstChild(); + Preconditions.checkState(textChild instanceof Text, getName() + " should contain text"); + String content = textChild.getTextContent(); + // Trim needed + return content.trim(); + } + + public String getNamespaceAttribute() { + String attribute = element.getAttribute(XmlUtil.XMLNS_ATTRIBUTE_KEY); + Preconditions.checkState(attribute != null && !attribute.equals(""), "Element %s must specify a %s attribute", + toString(), XmlUtil.XMLNS_ATTRIBUTE_KEY); + return attribute; + } + + public String getNamespace() { + String namespaceURI = element.getNamespaceURI(); + Preconditions.checkState(namespaceURI != null, "No namespace defined for %s", this); + return namespaceURI.toString(); + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("XmlElement{"); + sb.append("name='").append(getName()).append('\''); + if (element.getNamespaceURI() != null) { + sb.append(", namespace='").append(getNamespace()).append('\''); + } + sb.append('}'); + return sb.toString(); + } + + /** + * Search for element's attributes defining namespaces. Look for the one + * namespace that matches prefix of element's text content. E.g. + * + *

+     * <type
+     * xmlns:th-java="urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl">th-java:threadfactory-naming</type>
+     * 
+ * + * returns {"th-java","urn:.."}. If no prefix is matched, then default + * namespace is returned with empty string as key. If no default namespace + * is found value will be null. + */ + public Map.Entry findNamespaceOfTextContent() { + Map namespaces = extractNamespaces(element); + String textContent = getTextContent(); + int indexOfColon = textContent.indexOf(":"); + String prefix; + if (indexOfColon > -1) { + prefix = textContent.substring(0, indexOfColon); + } else { + prefix = ""; + } + if (namespaces.containsKey(prefix) == false) { + throw new IllegalArgumentException("Cannot find namespace for " + element + ". Prefix from content is " + + prefix + ". Found namespaces " + namespaces); + } + return Maps.immutableEntry(prefix, namespaces.get(prefix)); + } + + public List getChildElementsWithSameNamespace(final String childName) { + List children = getChildElementsWithinNamespace(getNamespace()); + return Lists.newArrayList(Collections2.filter(children, new Predicate() { + @Override + public boolean apply(@Nullable XmlElement xmlElement) { + return xmlElement.getName().equals(childName); + } + })); + } + + public void checkUnrecognisedElements(List recognisedElements, + XmlElement... additionalRecognisedElements) { + List childElements = getChildElements(); + childElements.removeAll(recognisedElements); + for (XmlElement additionalRecognisedElement : additionalRecognisedElements) { + childElements.remove(additionalRecognisedElement); + } + Preconditions.checkState(childElements.isEmpty(), "Unrecognised elements %s in %s", childElements, this); + } + + public void checkUnrecognisedElements(XmlElement... additionalRecognisedElements) { + checkUnrecognisedElements(Collections. emptyList(), additionalRecognisedElements); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + XmlElement that = (XmlElement) o; + + if (!element.isEqualNode(that.element)) + return false; + + return true; + } + + @Override + public int hashCode() { + return element.hashCode(); + } + + private static interface ElementFilteringStrategy { + boolean accept(Element e); + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java new file mode 100644 index 0000000000..0791812910 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfConstants.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.netconf.util.xml; + +public class XmlNetconfConstants { + + public static final String MOUNTPOINTS = "mountpoints"; + public static final String MOUNTPOINT = "mountpoint"; + public static final String ID = "id"; + public static final String CAPABILITY = "capability"; + public static final String CAPABILITIES = "capabilities"; + public static final String COMMIT = "commit"; + public static final String TYPE_KEY = "type"; + public static final String MODULE_KEY = "module"; + public static final String INSTANCE_KEY = "instance"; + public static final String OPERATION_ATTR_KEY = "operation"; + public static final String SERVICES_KEY = "services"; + public static final String CONFIG_KEY = "config"; + public static final String MODULES_KEY = "modules"; + public static final String CONFIGURATION_KEY = "configuration"; + public static final String DATA_KEY = "data"; + public static final String OK = "ok"; + public static final String FILTER = "filter"; + public static final String SOURCE_KEY = "source"; + public static final String RPC_KEY = "rpc"; + public static final String RPC_REPLY_KEY = "rpc-reply"; + public static final String RPC_ERROR = "rpc-error"; + public static final String NAME_KEY = "name"; + // + // + public static final String RFC4741_TARGET_NAMESPACE = "urn:ietf:params:xml:ns:netconf:base:1.0"; + public static final String URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0 = "urn:ietf:params:xml:ns:netconf:base:1.0"; + public static final String URN_IETF_PARAMS_XML_NS_YANG_IETF_NETCONF_MONITORING = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"; + // TODO where to store namespace of config ? + public static final String URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG = "urn:opendaylight:params:xml:ns:yang:controller:config"; +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfValidator.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfValidator.java new file mode 100644 index 0000000000..de0ebccdca --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlNetconfValidator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.xml; + +import java.io.IOException; +import java.io.InputStream; + +import javax.xml.transform.Source; +import javax.xml.transform.dom.DOMSource; +import javax.xml.validation.Schema; +import javax.xml.validation.Validator; + +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import com.google.common.base.Preconditions; + +public class XmlNetconfValidator { + static final Schema schema; + + static { + final InputStream xmlSchema = XmlNetconfValidator.class.getResourceAsStream("/xml.xsd"); + Preconditions.checkNotNull(xmlSchema, "Cannot find xml.xsd"); + + final InputStream rfc4714Schema = XmlNetconfValidator.class.getResourceAsStream("/rfc4741.xsd"); + Preconditions.checkNotNull(rfc4714Schema, "Cannot find rfc4741.xsd"); + schema = XmlUtil.loadSchema(xmlSchema, rfc4714Schema); + } + + public static void validate(Document inputDocument) throws SAXException, IOException { + final Validator validator = schema.newValidator(); + final Source source = new DOMSource(inputDocument); + validator.validate(source); + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlUtil.java new file mode 100644 index 0000000000..a2d43b2279 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/xml/XmlUtil.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.xml; + +import java.io.*; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.*; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import com.google.common.base.Charsets; + +public class XmlUtil { + + public static final String XMLNS_ATTRIBUTE_KEY = "xmlns"; + + public static Element readXmlToElement(String xmlContent) throws SAXException, IOException { + Document doc = readXmlToDocument(xmlContent); + return doc.getDocumentElement(); + } + + public static Element readXmlToElement(InputStream xmlContent) throws SAXException, IOException { + Document doc = readXmlToDocument(xmlContent); + return doc.getDocumentElement(); + } + + public static Document readXmlToDocument(String xmlContent) throws SAXException, IOException { + return readXmlToDocument(new ByteArrayInputStream(xmlContent.getBytes(Charsets.UTF_8))); + } + + public static Document readXmlToDocument(InputStream xmlContent) throws SAXException, IOException { + DocumentBuilderFactory factory = getDocumentBuilderFactory(); + DocumentBuilder dBuilder; + try { + dBuilder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + Document doc = dBuilder.parse(xmlContent); + + doc.getDocumentElement().normalize(); + return doc; + } + + public static Element readXmlToElement(File xmlFile) throws SAXException, IOException { + return readXmlToDocument(new FileInputStream(xmlFile)).getDocumentElement(); + } + + private static final DocumentBuilderFactory getDocumentBuilderFactory() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + factory.setCoalescing(true); + // factory.setValidating(true); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + return factory; + } + + public static Document newDocument() { + DocumentBuilderFactory factory = getDocumentBuilderFactory(); + try { + DocumentBuilder builder = factory.newDocumentBuilder(); + Document document = builder.newDocument(); + return document; + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + } + + public static Element createTextElement(Document document, String name, String content) { + Element typeElement = document.createElement(name); + typeElement.appendChild(document.createTextNode(content)); + return typeElement; + } + + public static void addNamespaceAttr(Element root, String namespace) { + root.setAttribute(XMLNS_ATTRIBUTE_KEY, namespace); + } + + public static void addPrefixedNamespaceAttr(Element root, String prefix, String namespace) { + root.setAttribute(concat(XMLNS_ATTRIBUTE_KEY, prefix), namespace); + } + + public static Element createPrefixedTextElement(Document document, String key, String prefix, String moduleName) { + return createTextElement(document, key, concat(prefix, moduleName)); + } + + private static String concat(String prefix, String value) { + return prefix + ":" + value; + } + + public static String toString(Document document) { + return toString(document.getDocumentElement()); + } + + public static String toString(Element xml) { + return toString(xml, false); + } + + public static String toString(XmlElement xmlElement) { + return toString(xmlElement.getDomElement(), false); + } + + public static String toString(Element xml, boolean addXmlDeclaration) { + try { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, addXmlDeclaration == true ? "no" : "yes"); + + StreamResult result = new StreamResult(new StringWriter()); + DOMSource source = new DOMSource(xml); + transformer.transform(source, result); + + String xmlString = result.getWriter().toString(); + return xmlString; + } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) { + throw new RuntimeException("Unable to serialize xml element " + xml, e); + } + } + + public static String toString(Document doc, boolean addXmlDeclaration) { + return toString(doc.getDocumentElement(), addXmlDeclaration); + } + + public static Schema loadSchema(InputStream... fromStreams) { + Source[] sources = new Source[fromStreams.length]; + int i = 0; + for (InputStream stream : fromStreams) { + sources[i++] = new StreamSource(stream); + } + + final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + try { + return schemaFactory.newSchema(sources); + } catch (SAXException e) { + throw new IllegalStateException("Failed to instantiate XML schema", e); + } + } + + public static Object evaluateXPath(XPathExpression expr, Object rootNode, QName returnType) { + try { + return expr.evaluate(rootNode, returnType); + } catch (XPathExpressionException e) { + throw new IllegalStateException("Error while evaluating xpath expression " + expr, e); + } + } + + public static Document createDocumentCopy(Document original) { + final Document copiedDocument = newDocument(); + final Node copiedRoot = copiedDocument.importNode(original.getDocumentElement(), true); + copiedDocument.appendChild(copiedRoot); + return copiedDocument; + } +} diff --git a/opendaylight/netconf/netconf-util/src/main/resources/org/opendaylight/controller/netconf/util/messages/server_error.xml b/opendaylight/netconf/netconf-util/src/main/resources/org/opendaylight/controller/netconf/util/messages/server_error.xml new file mode 100644 index 0000000000..ff0c0c52aa --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/resources/org/opendaylight/controller/netconf/util/messages/server_error.xml @@ -0,0 +1,16 @@ + + + + *** transport/rpc/protocol/application + *** RFC 4741 Appendix A. NETCONF Error List + *** error/warning + + + + + diff --git a/opendaylight/netconf/netconf-util/src/main/resources/rfc4741.xsd b/opendaylight/netconf/netconf-util/src/main/resources/rfc4741.xsd new file mode 100644 index 0000000000..990049d34a --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/resources/rfc4741.xsd @@ -0,0 +1,585 @@ + + + + + + + + This import accesses the xml: attribute groups for the + xml:lang as declared on the error-message element. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use of the rollback-on-error value requires + the :rollback-on-error capability. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use of the xpath value requires the :xpath capability. + + + + + + + + + + + + + + + + + + + + + The startup datastore can be used only if the :startup + capability is advertised. The candidate datastore can + be used only if the :candidate datastore is advertised. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use of the url element requires the :url capability. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Use of the test-option element requires the + :validate capability. Use of the url element + requires the :url capability. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The validate operation requires the :validate capability. + + + + + + + + + + + + + + + + + + + + + + + The commit operation requires the :candidate capability. + + + + + + + + Use of the confirmed and confirm-timeout elements + requires the :confirmed-commit capability. + + + + + + + + + + + + + + The discard-changes operation requires the + :candidate capability. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/main/resources/xml.xsd b/opendaylight/netconf/netconf-util/src/main/resources/xml.xsd new file mode 100644 index 0000000000..0f7db9d553 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/main/resources/xml.xsd @@ -0,0 +1,327 @@ + + + + + + +
+

About the XML namespace

+ +
+

+ This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + + http://www.w3.org/XML/1998/namespace.html + + and + + http://www.w3.org/TR/REC-xml + + for information + about this namespace. +

+

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

+ See further below in this document for more information about + how to refer to this schema document from your own + XSD schema documents + + and aboutthe + namespace-versioning policy governing this schema document. +

+
+
+
+
+ + + + +
+ +

lang (as an attribute name)

+

+ denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification. +

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

+ See BCP 47 at + + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + + for further information. +

+

+ The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + +
+ + + + +
+ +

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification. +

+ +
+
+
+ + + + + + +
+ + + + +
+ +

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification. +

+ +

+ See + http://www.w3.org/TR/xmlbase/ + + for information about this attribute. +

+
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification. +

+ +

+ See + http://www.w3.org/TR/xml-id/ + + for information about this attribute. +

+
+
+
+
+ + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + +
+

+ About this schema document +

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allowxml:base, + xml:lang, + xml:space + or + xml:id + attributes on elements they define. +

+

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+                        <schema . . .>
+                        . . .
+                        <import namespace="http://www.w3.org/XML/1998/namespace"
+                        schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+                    
+

+ or +

+
+                        <import namespace="http://www.w3.org/XML/1998/namespace"
+                        schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+                    
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+                        <type . . .>
+                        . . .
+                        <attributeGroup ref="xml:specialAttrs"/>
+                    
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+
+
+
+
+ + + +
+

+ Versioning policy for this schema document +

+
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ +
+ diff --git a/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/test/XmlFileLoader.java b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/test/XmlFileLoader.java new file mode 100644 index 0000000000..8b60719ebe --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/java/org/opendaylight/controller/netconf/util/test/XmlFileLoader.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.util.test; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.common.io.CharStreams; +import com.google.common.io.InputSupplier; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class XmlFileLoader { + + public static NetconfMessage xmlFileToNetconfMessage(final String fileName) throws IOException, SAXException, + ParserConfigurationException { + return new NetconfMessage(xmlFileToDocument(fileName)); + } + + public static Element xmlFileToElement(final String fileName) throws IOException, SAXException, + ParserConfigurationException { + return xmlFileToDocument(fileName).getDocumentElement(); + } + + public static String xmlFileToString(final String fileName) throws IOException, SAXException, + ParserConfigurationException { + return XmlUtil.toString(xmlFileToDocument(fileName)); + } + + public static Document xmlFileToDocument(final String fileName) throws IOException, SAXException, + ParserConfigurationException { + try (InputStream resourceAsStream = XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName)) { + Preconditions.checkNotNull(resourceAsStream); + final Document doc = XmlUtil.readXmlToDocument(resourceAsStream); + return doc; + } + } + + public static String fileToString(final String fileName) throws IOException { + try (InputStream resourceAsStream = XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName)) { + Preconditions.checkNotNull(resourceAsStream); + + InputSupplier supplier = new InputSupplier() { + @Override + public InputStream getInput() throws IOException { + return resourceAsStream; + } + }; + + InputSupplier readerSupplier = CharStreams.newReaderSupplier(supplier, Charsets.UTF_8); + + return CharStreams.toString(readerSupplier); + } + } + + public static InputStream getResourceAsStream(final String fileName) { + return XmlFileLoader.class.getClassLoader().getResourceAsStream(fileName); + } +} diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml new file mode 100644 index 0000000000..4e34591284 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello.xml @@ -0,0 +1,5 @@ + + + urn:ietf:params:netconf:base:1.0 + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml new file mode 100644 index 0000000000..174640c4f9 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/client_hello_with_auth.xml @@ -0,0 +1,8 @@ +[tomas;10.0.0.0/10000;tcp;1000;1000;;/home/tomas;;] + + + + urn:ietf:params:netconf:base:1.0 + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/close-session.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/close-session.xml new file mode 100644 index 0000000000..32d6446285 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/close-session.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/closeSession.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/closeSession.xml new file mode 100644 index 0000000000..2b0c0d01ca --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/closeSession.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/commit.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/commit.xml new file mode 100644 index 0000000000..ffdf132153 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/commit.xml @@ -0,0 +1,3 @@ + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_expectedResponse.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_expectedResponse.xml new file mode 100644 index 0000000000..4d85d336e1 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_expectedResponse.xml @@ -0,0 +1,11 @@ + + + + protocol + unknown-element + error + + rpc-reply + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_request.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_request.xml new file mode 100644 index 0000000000..72e7780352 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testClientSendsRpcReply_request.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_expectedResponse.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_expectedResponse.xml new file mode 100644 index 0000000000..c28b93dffb --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_expectedResponse.xml @@ -0,0 +1,12 @@ + + + + rpc + missing-attribute + error + + message-id + rpc + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_request.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_request.xml new file mode 100644 index 0000000000..419d006827 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/communicationError/testRpcWithoutMessageId_request.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/client_get_request_ConfigRegistry.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/client_get_request_ConfigRegistry.xml new file mode 100644 index 0000000000..dfed1a317a --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/client_get_request_ConfigRegistry.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/confg_subsystem_expected_reply.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/confg_subsystem_expected_reply.xml new file mode 100644 index 0000000000..ef8696dc52 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/databaseInteraction/confg_subsystem_expected_reply.xml @@ -0,0 +1,29 @@ + + + + + + testing-threadpool + modifiable-threadpool + + + fixed + 10 + false + + + + + testing-threadpool + modifiable-threadpool + + + fixed + 10 + false + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/discardChanges.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/discardChanges.xml new file mode 100644 index 0000000000..738a6657d3 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/discardChanges.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml new file mode 100644 index 0000000000..904c0a6322 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig.xml @@ -0,0 +1,117 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + + test1 + + 44 + 8 + 1 + 0 + configAttributeType + + 444 + 4444 + 454 + + 44 + + 4 + + 4 + + 4 + + 44 + 545 + 454545 + false + + + + 456 + + 44 + + + 4 + 999 + 4 + + port1 + 456 + 44 + + + port23 + 456 + 44 + + + testing + ref_dep + + + + + + test-impl:impl-netconf + + test2 + + testing + ref_dep + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + ref_dep_2 + /config/modules/module[name='impl-dep']/instance[name='dep2'] + + + + ref_test1 + /config/modules/module[name='impl-netconf']/instance[name='test1'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_expectedResult.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_expectedResult.xml new file mode 100644 index 0000000000..7f884dc43c --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_expectedResult.xml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_threadfactory.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_threadfactory.xml new file mode 100644 index 0000000000..ad7c84f3c9 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_threadfactory.xml @@ -0,0 +1,27 @@ + + + + + + + merge + set + + + + threadfactory-naming-instance + + th-java:threadfactory-naming + + + prefixDefinedInXML + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_yang-test.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_yang-test.xml new file mode 100644 index 0000000000..5711a68944 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_merge_yang-test.xml @@ -0,0 +1,23 @@ + + + + + + + merge + set + + + + impl-dep-instance + th-java:impl-dep + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml new file mode 100644 index 0000000000..42021c59a3 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_none.xml @@ -0,0 +1,109 @@ + + + + + + none + stop-on-error + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + test1 + 44 + 8 + 7 + 9 + + 444 + 4444 + 454 + + 44 + + 4 + + 4 + + 4 + + 44 + 545 + 454545 + false + + + + 456 + + 44 + + + 4 + 999 + 4 + + port1 + 456 + 44 + + + port23 + 456 + 44 + + + testing + ref_dep + + + + + test-impl:impl-netconf + + test2 + + testing + ref_dep + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + ref_dep_2 + /config/modules/module[name='impl-dep']/instance[name='dep2'] + + + + ref_test1 + /config/modules/module[name='impl-netconf']/instance[name='test1'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_remove.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_remove.xml new file mode 100644 index 0000000000..9d06d98f1c --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_remove.xml @@ -0,0 +1,43 @@ + + + + + + none + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + test1 + + + + + test-impl:impl-netconf + + test2 + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default.xml new file mode 100644 index 0000000000..3c06b85b49 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default.xml @@ -0,0 +1,27 @@ + + + + + + replace + + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default_ex.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default_ex.xml new file mode 100644 index 0000000000..91183cb079 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_default_ex.xml @@ -0,0 +1,21 @@ + + + + + + replace + + + + impl-dep + dep + + + impl-dep + dep2 + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module.xml new file mode 100644 index 0000000000..ed3ff6abc6 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module.xml @@ -0,0 +1,16 @@ + + + + + + + + + impl-dep + dep + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module_ex.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module_ex.xml new file mode 100644 index 0000000000..7c7679cfe2 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/editConfig_replace_module_ex.xml @@ -0,0 +1,16 @@ + + + + + + replace + + + + impl-dep + dep + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/edit_config.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/edit_config.xml new file mode 100644 index 0000000000..05866a6e73 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/edit_config.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get.xml new file mode 100644 index 0000000000..7daadbda8b --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig.xml new file mode 100644 index 0000000000..39efb4961b --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate.xml new file mode 100644 index 0000000000..5e65bfd2ed --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/getConfig_candidate.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema-no-version.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema-no-version.xml new file mode 100644 index 0000000000..fa59e59f8b --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema-no-version.xml @@ -0,0 +1,6 @@ + + + threadpool-api + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema.xml new file mode 100644 index 0000000000..79f046ea5e --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/get_schema.xml @@ -0,0 +1,10 @@ + + + threadpool-api + 2010-09-24 + ncm:yang + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_session_id.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_session_id.xml new file mode 100644 index 0000000000..e950e44899 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_session_id.xml @@ -0,0 +1,6 @@ + + + urn:ietf:params:netconf:base:1.0 + + 666 + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_wrong_namespace.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_wrong_namespace.xml new file mode 100644 index 0000000000..3f32f608f5 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/handshake/client_hello_with_wrong_namespace.xml @@ -0,0 +1,5 @@ + + + urn:ietf:params:netconf:base:1.0 + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/editConfig_merge_threadfactory.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/editConfig_merge_threadfactory.xml new file mode 100644 index 0000000000..e1e0237a1e --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/editConfig_merge_threadfactory.xml @@ -0,0 +1,68 @@ + + + + + + + merge + + + + threadfactory-naming-instance + + th-java:threadfactory-naming + + + prefixDefinedInXML + + + + + + + localhost:12002 + + + + threadfactory-naming-instance + + th-java:threadfactory-naming + + + prefixDefinedInXML + + + + + + + localhost:12003 + + + + threadfactory-naming-instance + + th-java:threadfactory-naming + + + prefixDefinedInXML + + + + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12002.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12002.xml new file mode 100644 index 0000000000..f524de3cf3 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12002.xml @@ -0,0 +1,7 @@ + + + + localhost:12002 + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12003.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12003.xml new file mode 100644 index 0000000000..64ee057006 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/mount12003.xml @@ -0,0 +1,7 @@ + + + + localhost:12003 + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/unmount12002.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/unmount12002.xml new file mode 100644 index 0000000000..c82db72b57 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/mount/unmount12002.xml @@ -0,0 +1,7 @@ + + + + localhost:12002 + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml new file mode 100644 index 0000000000..efa4690f6f --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_differentNamespaceTO.xml @@ -0,0 +1,117 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + + test1 + + 44 + 8 + 1 + 0 + configAttributeType + + 444 + 4444 + 454 + + 44 + + 4 + + 4 + + 4 + + 44 + 545 + 454545 + false + + + + 456 + + 44 + + + 4 + 999 + 4 + + port1 + 456 + 44 + + + port23 + 456 + 44 + + + testing + ref_dep + + + + + + test-impl:impl-netconf + + test2 + + testing + ref_dep + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + ref_dep_2 + /config/modules/module[name='impl-dep']/instance[name='dep2'] + + + + ref_test1 + /config/modules/module[name='impl-netconf']/instance[name='test1'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml new file mode 100644 index 0000000000..3dbb297c29 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespaces.xml @@ -0,0 +1,118 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + + test1 + + 44 + 44 + 8 + 1 + 0 + configAttributeType + + 444 + 4444 + 454 + + 44 + + 4 + + 4 + + 4 + + 44 + 545 + 454545 + false + + + + 456 + + 44 + + + 4 + 999 + 4 + + port1 + 456 + 44 + + + port23 + 456 + 44 + + + testing + ref_dep + + + + + + test-impl:impl-netconf + + test2 + + testing + ref_dep + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + ref_dep_2 + /config/modules/module[name='impl-dep']/instance[name='dep2'] + + + + ref_test1 + /config/modules/module[name='impl-netconf']/instance[name='test1'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml new file mode 100644 index 0000000000..504ccff78f --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_sameAttrDifferentNamespacesList.xml @@ -0,0 +1,117 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + + test1 + + 44 + 8 + 1 + 0 + configAttributeType + + 444 + 4444 + 454 + + 44 + + 4 + + 4 + + 4 + + 44 + 545 + 454545 + false + + + + 456 + + 44 + + + 4 + 999 + 4 + + port1 + 456 + 44 + + + port23 + 456 + 44 + + + testing + ref_dep + + + + + + test-impl:impl-netconf + + test2 + + testing + ref_dep + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + ref_dep_2 + /config/modules/module[name='impl-dep']/instance[name='dep2'] + + + + ref_test1 + /config/modules/module[name='impl-netconf']/instance[name='test1'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml new file mode 100644 index 0000000000..8398fdb588 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/namespaces/editConfig_typeNameConfigAttributeMatching.xml @@ -0,0 +1,116 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + + test1 + + 44 + 8 + 1 + 0 + + 444 + 4444 + 454 + + 44 + + 4 + + 4 + + 4 + + 44 + 545 + 454545 + false + + + + 456 + + 44 + + + 4 + 999 + 4 + + port1 + 456 + 44 + + + port23 + 456 + 44 + + + testing + ref_dep + + + + + + test-impl:impl-netconf + + test2 + + testing + ref_dep + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + ref_dep_2 + /config/modules/module[name='impl-dep']/instance[name='dep2'] + + + + ref_test1 + /config/modules/module[name='impl-netconf']/instance[name='test1'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpc.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpc.xml new file mode 100644 index 0000000000..b2a2ee30c8 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpc.xml @@ -0,0 +1,8 @@ + + + /data/modules/module[name='impl-netconf']/instance[name='instance'] + + testarg1 + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInner.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInner.xml new file mode 100644 index 0000000000..8bc504a56e --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInner.xml @@ -0,0 +1,7 @@ + + + + /data/modules/module[name='impl-netconf']/instance[name='instance2']/inner-running-data-additional[key='randomString_1003'] + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner.xml new file mode 100644 index 0000000000..2356398e64 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner.xml @@ -0,0 +1,16 @@ + + + + /data/modules/module[name='impl-netconf']/instance[name='instance2']/inner-running-data[key='1015']/inner-inner-running-data[key='1017'] + + + + 456 + + + true + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/threadpool-edit-config.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/threadpool-edit-config.xml new file mode 100644 index 0000000000..40f86e3155 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/threadpool-edit-config.xml @@ -0,0 +1,23 @@ + + + + + + + + + threadfactory-naming-instance + + th-java:threadfactory-naming + + + prefixDefinedInXML + + + + + + \ No newline at end of file diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised1.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised1.xml new file mode 100644 index 0000000000..6b267c57a3 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised1.xml @@ -0,0 +1,36 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + error + + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml new file mode 100644 index 0000000000..8ca7ee724a --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised2.xml @@ -0,0 +1,37 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + + + error + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml new file mode 100644 index 0000000000..d3705cc3c4 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised3.xml @@ -0,0 +1,38 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + + + + + error + l + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml new file mode 100644 index 0000000000..a485574969 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised4.xml @@ -0,0 +1,39 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + + + + + l + + testing + error + + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml new file mode 100644 index 0000000000..654a183359 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised5.xml @@ -0,0 +1,39 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + + + + + l + + testing + + + ref_dep + error + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml new file mode 100644 index 0000000000..fc43a77406 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised6.xml @@ -0,0 +1,36 @@ + + + + + + + set + + merge + + + + error + + dep + + test-impl:impl-dep + + + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml new file mode 100644 index 0000000000..1bea536934 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised7.xml @@ -0,0 +1,117 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + + test1 + + 44 + 8 + 1 + 0 + + error + 444 + 4444 + 454 + + 44 + + 4 + + 4 + + 4 + + 44 + 545 + 454545 + false + + + + 456 + + 44 + + + 4 + 999 + 4 + + port1 + 456 + 44 + + + port23 + 456 + 44 + + + testing + ref_dep + + + + + + test-impl:impl-netconf + + test2 + + testing + ref_dep + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + ref_dep_2 + /config/modules/module[name='impl-dep']/instance[name='dep2'] + + + + ref_test1 + /config/modules/module[name='impl-netconf']/instance[name='test1'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml new file mode 100644 index 0000000000..d0afb276aa --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/unrecognised/editConfig_unrecognised8.xml @@ -0,0 +1,117 @@ + + + + + + + set + + merge + + + + + dep + + test-impl:impl-dep + + + + + dep2 + + test-impl:impl-dep + + + + + + test-impl:impl-netconf + + + test1 + + 44 + 8 + 1 + 0 + + 444 + 4444 + 454 + + 44 + + 4 + + 4 + + 4 + + 44 + 545 + 454545 + false + + + + 456 + + 44 + + + 4 + 999 + 4 + + port1 + 456 + 44 + + + port23 + 456 + 44 + error + + + testing + ref_dep + + + + + + test-impl:impl-netconf + + test2 + + testing + ref_dep + + + + + + + testing + + ref_dep + /config/modules/module[name='impl-dep']/instance[name='dep'] + + + + ref_dep_2 + /config/modules/module[name='impl-dep']/instance[name='dep2'] + + + + ref_test1 + /config/modules/module[name='impl-netconf']/instance[name='test1'] + + + + + + + diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/validate.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/validate.xml new file mode 100644 index 0000000000..dab34b4757 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/validate.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml new file mode 100644 index 0000000000..cff6dfa538 --- /dev/null +++ b/opendaylight/netconf/pom.xml @@ -0,0 +1,148 @@ + + 4.0.0 + + + org.opendaylight.controller + commons.opendaylight + 1.4.1-SNAPSHOT + ../commons/opendaylight + + + org.opendaylight.controller + 0.2.1-SNAPSHOT + netconf-subsystem + pom + ${project.artifactId} + + 3.0.4 + + + + + netconf-api + netconf-impl + config-netconf-connector + netconf-util + netconf-it + config-persister-impl + netconf-mapping-api + netconf-client + + + + 5.0.0 + 2.3.7 + 1.7.2 + 1.7 + 1.7 + + + + + junit + junit + test + + + ch.qos.logback + logback-classic + test + + + + + + + org.osgi + org.osgi.core + ${osgi.version} + + + org.opendaylight.bgpcep + mockito-configuration + ${bgpcep.version} + test + + + ${project.groupId} + config-api + ${config.version} + + + ${project.groupId} + config-manager + ${config.version} + + + ${project.groupId} + yang-jmx-generator + ${config.version} + + + ${project.groupId} + config-util + ${config.version} + + + ${project.groupId} + yang-store-api + ${config.version} + + + ${project.groupId} + yang-store-impl + ${config.version} + + + ${project.groupId} + yang-test + ${config.version} + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.5.1 + + ${java.version.source} + ${java.version.target} + ${java.version.source} + ${java.version.target} + + + + + + + org.apache.felix + maven-bundle-plugin + ${maven.bundle.version} + true + + + ${project.groupId}.${project.artifactId} + + + + + + + + + diff --git a/pom.xml b/pom.xml index 4abcd81c36..692c0c6b21 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,7 @@ opendaylight/md-sal opendaylight/config + opendaylight/netconf -- 2.36.6