From: Jakub Toth Date: Wed, 7 Dec 2016 19:19:57 +0000 (+0100) Subject: Bug 5679 - implement ietf-restconf-monitoring - streams X-Git-Tag: release/carbon~101 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=dca9aa60cd4584298c414ff2722dff4f406fe0de;p=netconf.git Bug 5679 - implement ietf-restconf-monitoring - streams *removed service and tests for getting streams *implemented put/delete specific stream to operational DS *new mapping for data-change and yang notifications *new tests + fixed old Change-Id: I6c48149ba0bde1e6ba574b514d0fd70a976f1bf4 Signed-off-by: Jakub Toth --- diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java index ddaa7bf355..e2775381a1 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/rest/api/RestconfService.java @@ -27,7 +27,6 @@ import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext; import org.opendaylight.netconf.sal.restconf.impl.PATCHContext; import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext; import org.opendaylight.restconf.base.services.api.RestconfOperationsService; -import org.opendaylight.restconf.base.services.api.RestconfStreamsService; import org.opendaylight.restconf.restful.services.api.RestconfDataService; import org.opendaylight.restconf.restful.services.api.RestconfInvokeOperationsService; @@ -218,7 +217,7 @@ public interface RestconfService { /** * @deprecated do not use this method. It will be replaced by - * {@link RestconfStreamsService#getAvailableStreams(UriInfo)} + * {@link RestconfDataService#readData(String, UriInfo)} */ @Deprecated @GET diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java index ccc30a5616..331daf7344 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/ListenerAdapter.java @@ -21,11 +21,9 @@ import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; -import java.util.Collection; import java.util.Date; import java.util.Map; import java.util.Map.Entry; -import java.util.Random; import java.util.Set; import java.util.concurrent.Executors; import java.util.regex.Pattern; @@ -48,9 +46,15 @@ import javax.xml.xpath.XPathFactory; import org.json.JSONObject; import org.json.XML; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.restconf.Draft18.MonitoringModule; +import org.opendaylight.restconf.handlers.SchemaContextHandler; +import org.opendaylight.restconf.handlers.TransactionChainHandler; +import org.opendaylight.restconf.parser.IdentifierCodec; import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.common.QName; @@ -98,6 +102,8 @@ public class ListenerAdapter implements DOMDataChangeListener { private Date start = null; private Date stop = null; private String filter = null; + private TransactionChainHandler transactionChainHandler; + private SchemaContextHandler schemaHandler; /** * Creates new {@link ListenerAdapter} listener specified by path and stream @@ -594,27 +600,6 @@ public class ListenerAdapter implements DOMDataChangeListener { textContent.append(qName.getLocalName()); } - /** - * Generates new prefix which consists of four random characters . - * - * @param prefixes - * Collection of prefixes. - * @return New prefix which consists of four random characters . - */ - private static String generateNewPrefix(final Collection prefixes) { - StringBuilder result = null; - final Random random = new Random(); - do { - result = new StringBuilder(); - for (int i = 0; i < 4; i++) { - final int randomNumber = 0x61 + (Math.abs(random.nextInt()) % 26); - result.append(Character.toChars(randomNumber)); - } - } while (prefixes.contains(result.toString())); - - return result.toString(); - } - /** * Gets path pointed to data in data store. * @@ -643,9 +628,15 @@ public class ListenerAdapter implements DOMDataChangeListener { } /** - * Removes all subscribers and unregisters event bus change recorder form event bus. + * Removes all subscribers and unregisters event bus change recorder form + * event bus and delete data in DS */ public void close() throws Exception { + final DOMDataWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction(); + wTx.delete(LogicalDatastoreType.OPERATIONAL, IdentifierCodec.deserialize(MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + + this.path.getLastPathArgument().getNodeType().getLocalName(), this.schemaHandler.get())); + wTx.submit().checkedGet(); + this.subscribers = new ConcurrentSet<>(); this.registration.close(); this.registration = null; @@ -744,4 +735,29 @@ public class ListenerAdapter implements DOMDataChangeListener { this.filter = filter; } + /** + * Get output type + * + * @return outputType + */ + public String getOutputType() { + return this.outputType.getName(); + } + + /** + * Transaction chain to delete data in DS on close() + * + * @param transactionChainHandler + * - creating new write transaction to delete data on close + * @param schemaHandler + * - for getting schema to deserialize + * {@link MonitoringModule#PATH_TO_STREAM_WITHOUT_KEY} to + * {@link YangInstanceIdentifier} + */ + public void setCloseVars(final TransactionChainHandler transactionChainHandler, + final SchemaContextHandler schemaHandler) { + this.transactionChainHandler = transactionChainHandler; + this.schemaHandler = schemaHandler; + } + } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java index 608ddcbca1..2d3ad3a10b 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/netconf/sal/streams/listeners/NotificationListenerAdapter.java @@ -42,10 +42,17 @@ import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import org.json.JSONObject; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMNotification; import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener; import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; +import org.opendaylight.restconf.Draft18.MonitoringModule; +import org.opendaylight.restconf.handlers.SchemaContextHandler; +import org.opendaylight.restconf.handlers.TransactionChainHandler; +import org.opendaylight.restconf.parser.IdentifierCodec; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; @@ -92,6 +99,8 @@ public class NotificationListenerAdapter implements DOMNotificationListener { private DOMNotification notification; private ListenerRegistration registration; private Set subscribers = new ConcurrentSet<>(); + private TransactionChainHandler transactionChainHandler; + private SchemaContextHandler schemaHandler; /** * Set path of listener and stream name, register event bus. @@ -229,9 +238,20 @@ public class NotificationListenerAdapter implements DOMNotificationListener { } /** - * Reset lists, close registration and unregister bus event. + * Reset lists, close registration and unregister bus event and delete data in DS. */ public void close() { + final DOMDataWriteTransaction wTx = this.transactionChainHandler.get().newWriteOnlyTransaction(); + wTx.delete(LogicalDatastoreType.OPERATIONAL, + IdentifierCodec.deserialize( + MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + this.path.getLastComponent().getLocalName(), + this.schemaHandler.get())); + try { + wTx.submit().checkedGet(); + } catch (final TransactionCommitFailedException e) { + throw new RestconfDocumentedException("Problem while deleting data from DS.", e); + } + this.subscribers = new ConcurrentSet<>(); this.registration.close(); this.registration = null; @@ -512,4 +532,30 @@ public class NotificationListenerAdapter implements DOMNotificationListener { this.stop = stop; this.filter = filter; } + + /** + * Get outputType of listenere + * + * @return the outputType + */ + public String getOutputType() { + return this.outputType; + } + + /** + * Transaction chain to delete data in DS on close() + * + * @param transactionChainHandler + * - creating new write transaction to delete data on close + * @param schemaHandler + * - for getting schema to deserialize + * {@link MonitoringModule#PATH_TO_STREAM_WITHOUT_KEY} to + * {@link YangInstanceIdentifier} + */ + public void setCloseVars(final TransactionChainHandler transactionChainHandler, + final SchemaContextHandler schemaHandler) { + this.transactionChainHandler = transactionChainHandler; + this.schemaHandler = schemaHandler; + + } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft18.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft18.java index d2321dadef..61660e085a 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft18.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/Draft18.java @@ -198,6 +198,9 @@ public final class Draft18 { public static final String NAME = "ietf-restconf-monitoring"; public static final String NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"; public static final String REVISION = "2016-08-15"; + public static final String PATH_TO_STREAM_WITHOUT_KEY = + "ietf-restconf-monitoring:restconf-state/streams/stream="; + public static final String PATH_TO_STREAMS = "ietf-restconf-monitoring:restconf-state/streams"; public static Date DATE = null; public static URI URI_MODULE = null; @@ -251,10 +254,6 @@ public final class Draft18 { public static final String LEAF_LOCATION_ACCESS_NAME = "location"; public static final QName LEAF_LOCATION_ACCESS_QNAME = QName.create(MODULE_QNAME, LEAF_LOCATION_ACCESS_NAME); - // TODO remove after implement ietf-restconf monitoring - public static final String STREAMS_CONTAINER_SCHEMA_NODE = "streams"; - public static final String STREAM_LIST_SCHEMA_NODE = "stream"; - /** * Constants for capabilities */ diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/BaseServicesWrapper.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/BaseServicesWrapper.java index 8125405867..ff1bf3ae56 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/BaseServicesWrapper.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/BaseServicesWrapper.java @@ -11,10 +11,9 @@ package org.opendaylight.restconf.base.services.api; * Wrapper for all base services: *
    *
  • {@link RestconfOperationsService} - *
  • {@link RestconfStreamsService} *
  • {@link RestconfSchemaService} *
* */ -public interface BaseServicesWrapper extends RestconfOperationsService, RestconfStreamsService, RestconfSchemaService { +public interface BaseServicesWrapper extends RestconfOperationsService, RestconfSchemaService { } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/RestconfStreamsService.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/RestconfStreamsService.java deleted file mode 100644 index 4109a5c5ac..0000000000 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/api/RestconfStreamsService.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.restconf.base.services.api; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.UriInfo; -import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext; -import org.opendaylight.restconf.Draft18; -import org.opendaylight.restconf.utils.RestconfConstants; - -/** - * Container that provides access to the event notification streams supported by - * the server. - * - */ -public interface RestconfStreamsService { - - /** - * List of streams supported by the server. - * - * @param uriInfo - * - URI information - * @return {@link NormalizedNodeContext} - */ - @GET - @Path("data/ietf-restconf-monitoring:restconf-state/streams") - @Produces({ Draft18.MediaTypes.DATA + RestconfConstants.JSON, Draft18.MediaTypes.DATA, MediaType.APPLICATION_JSON, - MediaType.APPLICATION_XML, MediaType.TEXT_XML }) - public NormalizedNodeContext getAvailableStreams(@Context UriInfo uriInfo); -} \ No newline at end of file diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfStreamsServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfStreamsServiceImpl.java deleted file mode 100644 index c1815c7c82..0000000000 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/base/services/impl/RestconfStreamsServiceImpl.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ -package org.opendaylight.restconf.base.services.impl; - -import com.google.common.base.Preconditions; -import java.util.Set; -import javax.ws.rs.core.UriInfo; -import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext; -import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext; -import org.opendaylight.netconf.sal.streams.listeners.Notificator; -import org.opendaylight.restconf.Draft18; -import org.opendaylight.restconf.base.services.api.RestconfStreamsService; -import org.opendaylight.restconf.common.references.SchemaContextRef; -import org.opendaylight.restconf.handlers.SchemaContextHandler; -import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeUtil; -import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; -import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; - -/** - * Implementation of {@link RestconfStreamsService} - * - */ -public class RestconfStreamsServiceImpl implements RestconfStreamsService { - - private final SchemaContextHandler schemaContextHandler; - - /** - * Set {@link SchemaContextHandler} for getting actual {@link SchemaContext} - * . - * - * @param schemaContextHandler - * - handling schema context - */ - public RestconfStreamsServiceImpl(final SchemaContextHandler schemaContextHandler) { - this.schemaContextHandler = schemaContextHandler; - } - - @Override - public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) { - final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get()); - final Set availableStreams = Notificator.getStreamNames(); - - final DataSchemaNode streamListSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode( - schemaContextRef.getRestconfModule(), Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE); - Preconditions.checkState(streamListSchemaNode instanceof ListSchemaNode); - final CollectionNodeBuilder listStreamBuilder = Builders - .mapBuilder((ListSchemaNode) streamListSchemaNode); - - for (final String streamValue : availableStreams) { - listStreamBuilder.withChild(RestconfMappingNodeUtil.toStreamEntryNode(streamValue, streamListSchemaNode)); - } - - final DataSchemaNode streamContSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode( - schemaContextRef.getRestconfModule(), Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE); - Preconditions.checkState(streamContSchemaNode instanceof ContainerSchemaNode); - final DataContainerNodeAttrBuilder streamsContainerBuilder = Builders - .containerBuilder((ContainerSchemaNode) streamContSchemaNode); - - streamsContainerBuilder.withChild(listStreamBuilder.build()); - - return new NormalizedNodeContext( - new InstanceIdentifierContext<>(null, streamContSchemaNode, null, schemaContextRef.get()), - streamsContainerBuilder.build()); - } -} diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/common/wrapper/services/ServicesWrapperImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/common/wrapper/services/ServicesWrapperImpl.java index d3a2daf48d..dfc6620287 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/common/wrapper/services/ServicesWrapperImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/common/wrapper/services/ServicesWrapperImpl.java @@ -17,10 +17,8 @@ import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext; import org.opendaylight.restconf.base.services.api.BaseServicesWrapper; import org.opendaylight.restconf.base.services.api.RestconfOperationsService; import org.opendaylight.restconf.base.services.api.RestconfSchemaService; -import org.opendaylight.restconf.base.services.api.RestconfStreamsService; import org.opendaylight.restconf.base.services.impl.RestconfOperationsServiceImpl; import org.opendaylight.restconf.base.services.impl.RestconfSchemaServiceImpl; -import org.opendaylight.restconf.base.services.impl.RestconfStreamsServiceImpl; import org.opendaylight.restconf.handlers.DOMDataBrokerHandler; import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler; import org.opendaylight.restconf.handlers.NotificationServiceHandler; @@ -50,7 +48,6 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ private RestconfInvokeOperationsService delegRestconfInvokeOpsService; private RestconfStreamsSubscriptionService delegRestconfSubscrService; private RestconfOperationsService delegRestOpsService; - private RestconfStreamsService delegRestStrsService; private RestconfSchemaService delegRestSchService; private ServicesWrapperImpl() { @@ -74,11 +71,6 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ return this.delegRestOpsService.getOperations(identifier, uriInfo); } - @Override - public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) { - return this.delegRestStrsService.getAvailableStreams(uriInfo); - } - @Override public SchemaExportContext getSchema(final String mountAndModuleId) { return this.delegRestSchService.getSchema(mountAndModuleId); @@ -142,13 +134,12 @@ public class ServicesWrapperImpl implements BaseServicesWrapper, TransactionServ final RpcServiceHandler rpcServiceHandler, final NotificationServiceHandler notificationServiceHandler) { this.delegRestOpsService = new RestconfOperationsServiceImpl(schemaCtxHandler, domMountPointServiceHandler); this.delegRestSchService = new RestconfSchemaServiceImpl(schemaCtxHandler, domMountPointServiceHandler); - this.delegRestStrsService = new RestconfStreamsServiceImpl(schemaCtxHandler); this.delegRestconfDataService = new RestconfDataServiceImpl(schemaCtxHandler, transactionChainHandler, domMountPointServiceHandler); this.delegRestconfInvokeOpsService = new RestconfInvokeOperationsServiceImpl(rpcServiceHandler, schemaCtxHandler); this.delegRestconfSubscrService = new RestconfStreamsSubscriptionServiceImpl(domDataBrokerHandler, notificationServiceHandler, - schemaCtxHandler); + schemaCtxHandler, transactionChainHandler); } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java index 9852330a0e..3c81d2322c 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImpl.java @@ -20,6 +20,7 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.restconf.handlers.DOMDataBrokerHandler; import org.opendaylight.restconf.handlers.NotificationServiceHandler; import org.opendaylight.restconf.handlers.SchemaContextHandler; +import org.opendaylight.restconf.handlers.TransactionChainHandler; import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService; import org.opendaylight.restconf.restful.utils.RestconfStreamsConstants; import org.opendaylight.restconf.restful.utils.SubscribeToStreamUtil; @@ -45,11 +46,15 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu private final SchemaContextHandler schemaHandler; + private final TransactionChainHandler transactionChainHandler; + public RestconfStreamsSubscriptionServiceImpl(final DOMDataBrokerHandler domDataBrokerHandler, - final NotificationServiceHandler notificationServiceHandler, final SchemaContextHandler schemaHandler) { + final NotificationServiceHandler notificationServiceHandler, final SchemaContextHandler schemaHandler, + final TransactionChainHandler transactionChainHandler) { this.domDataBrokerHandler = domDataBrokerHandler; this.notificationServiceHandler = notificationServiceHandler; this.schemaHandler = schemaHandler; + this.transactionChainHandler = transactionChainHandler; } @Override @@ -95,10 +100,11 @@ public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSu URI response = null; if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCR)) { response = - SubscribeToStreamUtil.dataSubs(identifier, uriInfo, start, stop, this.domDataBrokerHandler, filter); + SubscribeToStreamUtil.notifiDataStream(identifier, uriInfo, start, stop, this.domDataBrokerHandler, filter, + this.transactionChainHandler, this.schemaHandler); } else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) { - response = SubscribeToStreamUtil.notifStream(identifier, uriInfo, start, stop, - this.notificationServiceHandler, filter); + response = SubscribeToStreamUtil.notifYangStream(identifier, uriInfo, start, stop, + this.notificationServiceHandler, filter, this.transactionChainHandler, this.schemaHandler); } if (response != null) { diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java index b513f81672..d199604aff 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java @@ -162,7 +162,7 @@ public final class CreateStreamUtil { return null; } - return StreamUtil.resolveEnum(clazz, (String) value); + return ResolveEnumUtil.resolveEnum(clazz, (String) value); } private static YangInstanceIdentifier preparePath(final ContainerNode data, final QName qName) { diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/StreamUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResolveEnumUtil.java similarity index 83% rename from restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/StreamUtil.java rename to restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResolveEnumUtil.java index 38cfc326ba..6ca5624f5e 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/StreamUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/ResolveEnumUtil.java @@ -8,16 +8,12 @@ package org.opendaylight.restconf.restful.utils; /** - * Common util class for stream - *
    - *
  • {@link SubscribeToStreamUtil} - *
  • {@link CreateStreamUtil} - *
+ * Common util class for resolve enum from String * */ -public final class StreamUtil { +public final class ResolveEnumUtil { - private StreamUtil() { + private ResolveEnumUtil() { throw new UnsupportedOperationException("Util class"); } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java index f6d45e5625..f387941b67 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/SubscribeToStreamUtil.java @@ -23,8 +23,11 @@ import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener; import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; @@ -34,18 +37,24 @@ import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter; import org.opendaylight.netconf.sal.streams.listeners.Notificator; import org.opendaylight.netconf.sal.streams.websockets.WebSocketServer; +import org.opendaylight.restconf.Draft18.MonitoringModule; import org.opendaylight.restconf.handlers.DOMDataBrokerHandler; import org.opendaylight.restconf.handlers.NotificationServiceHandler; import org.opendaylight.restconf.handlers.SchemaContextHandler; +import org.opendaylight.restconf.handlers.TransactionChainHandler; +import org.opendaylight.restconf.parser.IdentifierCodec; import org.opendaylight.restconf.utils.RestconfConstants; +import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeUtil; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.slf4j.Logger; @@ -72,11 +81,11 @@ public final class SubscribeToStreamUtil { * - string of enum value * @return enum */ - public static T parseURIEnum(final Class clazz, final String value) { + private static T parseURIEnum(final Class clazz, final String value) { if ((value == null) || value.equals("")) { return null; } - return StreamUtil.resolveEnum(clazz, value); + return ResolveEnumUtil.resolveEnum(clazz, value); } /** @@ -141,7 +150,9 @@ public final class SubscribeToStreamUtil { } /** - * Register listeners by streamName in identifier to listen to yang notifications + * Register listeners by streamName in identifier to listen to yang + * notifications, put or delete info about listener to DS according to + * ietf-restconf-monitoring * * @param identifier * - identifier as stream name @@ -155,10 +166,16 @@ public final class SubscribeToStreamUtil { * - DOMNotificationService handler for register listeners * @param filter * - indicate which subset of all possible events are of interest + * @param transactionChainHandler + * - to put new data about stream to DS and delete after close + * listener + * @param schemaHandler + * - for getting schema context * @return location for listening */ - public static URI notifStream(final String identifier, final UriInfo uriInfo, final Date start, final Date stop, - final NotificationServiceHandler notifiServiceHandler, final String filter) { + public static URI notifYangStream(final String identifier, final UriInfo uriInfo, Date start, final Date stop, + final NotificationServiceHandler notifiServiceHandler, final String filter, + final TransactionChainHandler transactionChainHandler, final SchemaContextHandler schemaHandler) { final String streamName = Notificator.createStreamNameFromUri(identifier); if (Strings.isNullOrEmpty(streamName)) { throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); @@ -169,11 +186,6 @@ public final class SubscribeToStreamUtil { ErrorTag.UNKNOWN_ELEMENT); } - for (final NotificationListenerAdapter listener : listeners) { - registerToListenNotification(listener, notifiServiceHandler); - listener.setQueryParams(start, stop, filter); - } - final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); int notificationPort = RestconfStreamsConstants.NOTIFICATION_PORT; try { @@ -185,9 +197,41 @@ public final class SubscribeToStreamUtil { final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws"); final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build(); + final DOMDataReadWriteTransaction wTx = transactionChainHandler.get().newReadWriteTransaction(); + final boolean exist = checkExist(schemaHandler, wTx); + final Module monitoringModule = schemaHandler.get() + .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE); + if (start == null) { + start = new Date(); + } + for (final NotificationListenerAdapter listener : listeners) { + registerToListenNotification(listener, notifiServiceHandler); + listener.setQueryParams(start, stop, filter); + listener.setCloseVars(transactionChainHandler, schemaHandler); + final NormalizedNode mapToStreams = + RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(listener.getSchemaPath().getLastComponent(), + schemaHandler.get().getNotifications(), start, listener.getOutputType(), + uriToWebsocketServer, monitoringModule, exist); + writeDataToDS(schemaHandler, listener.getSchemaPath().getLastComponent().getLocalName(), wTx, exist, mapToStreams); + } + submitData(wTx); + return uriToWebsocketServer; } + private static boolean checkExist(final SchemaContextHandler schemaHandler, final DOMDataReadWriteTransaction wTx) { + boolean exist; + try { + exist = wTx + .exists(LogicalDatastoreType.OPERATIONAL, IdentifierCodec + .deserialize(MonitoringModule.PATH_TO_STREAMS, schemaHandler.get())) + .checkedGet(); + } catch (final ReadFailedException e1) { + throw new RestconfDocumentedException("Problem while checking data if exists", e1); + } + return exist; + } + private static void registerToListenNotification(final NotificationListenerAdapter listener, final NotificationServiceHandler notificationServiceHandler) { if (listener.isListening()) { @@ -222,7 +266,9 @@ public final class SubscribeToStreamUtil { } /** - * Register listener by streamName in identifier to listen to yang notifications + * Register listener by streamName in identifier to listen to data change + * notifications, put or delete info about listener to DS according to + * ietf-restconf-monitoring * * @param identifier * - identifier as stream name @@ -236,10 +282,16 @@ public final class SubscribeToStreamUtil { * - DOMDataBroker handler for register listener * @param filter * - indicate which subset of all possible events are of interest + * @param schemaHandler + * - for getting schema context + * @param transactionChainHandler + * - to put new data about stream to DS and delete after close + * listener * @return location for listening */ - public static URI dataSubs(final String identifier, final UriInfo uriInfo, final Date start, final Date stop, - final DOMDataBrokerHandler domDataBrokerHandler, final String filter) { + public static URI notifiDataStream(final String identifier, final UriInfo uriInfo, Date start, final Date stop, + final DOMDataBrokerHandler domDataBrokerHandler, final String filter, + final TransactionChainHandler transactionChainHandler, final SchemaContextHandler schemaHandler) { final Map mapOfValues = SubscribeToStreamUtil.mapValuesFromUri(identifier); final LogicalDatastoreType ds = SubscribeToStreamUtil.parseURIEnum(LogicalDatastoreType.class, @@ -263,7 +315,12 @@ public final class SubscribeToStreamUtil { final ListenerAdapter listener = Notificator.getListenerFor(streamName); Preconditions.checkNotNull(listener, "Listener doesn't exist : " + streamName); + if (start == null) { + start = new Date(); + } + listener.setQueryParams(start, stop, filter); + listener.setCloseVars(transactionChainHandler, schemaHandler); SubscribeToStreamUtil.registration(ds, scope, listener, domDataBrokerHandler.get()); @@ -272,9 +329,50 @@ public final class SubscribeToStreamUtil { final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); final UriBuilder uriToWebSocketServer = uriBuilder.port(port).scheme(RestconfStreamsConstants.SCHEMA_SUBSCIBRE_URI); - return uriToWebSocketServer.replacePath(streamName).build(); + final URI uri = uriToWebSocketServer.replacePath(streamName).build(); + + final Module monitoringModule = schemaHandler.get() + .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE); + final DOMDataReadWriteTransaction wTx = transactionChainHandler.get().newReadWriteTransaction(); + final boolean exist = checkExist(schemaHandler, wTx); + + final NormalizedNode mapToStreams = RestconfMappingNodeUtil.mapDataChangeNotificationStreamByIetfRestconfMonitoring(listener.getPath(), start, + listener.getOutputType(), uri, monitoringModule, exist, schemaHandler.get()); + writeDataToDS(schemaHandler, listener.getPath().getLastPathArgument().getNodeType().getLocalName(), wTx, exist, + mapToStreams); + submitData(wTx); + return uri; + } + + private static void writeDataToDS(final SchemaContextHandler schemaHandler, final String name, + final DOMDataReadWriteTransaction wTx, final boolean exist, final NormalizedNode mapToStreams) { + String pathId = ""; + if (exist) { + pathId = MonitoringModule.PATH_TO_STREAM_WITHOUT_KEY + name; + } else { + pathId = MonitoringModule.PATH_TO_STREAMS; + } + wTx.merge(LogicalDatastoreType.OPERATIONAL, IdentifierCodec.deserialize(pathId, schemaHandler.get()), + mapToStreams); } + private static void submitData(final DOMDataReadWriteTransaction wTx) { + try { + wTx.submit().checkedGet(); + } catch (final TransactionCommitFailedException e) { + throw new RestconfDocumentedException("Problem while putting data to DS.", e); + } + } + + /** + * Parse input of query parameters - start-time or stop-time - from + * {@link DateAndTime} format to {@link Date} format + * + * @param entry + * - start-time or stop-time as string in {@link DateAndTime} + * format + * @return parsed {@link Date} by entry + */ public static Date parseDateFromQueryParam(final Entry> entry) { final DateAndTime event = new DateAndTime(entry.getValue().iterator().next()); String numOf_ms = ""; diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java index 4b92d5549d..a081989469 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtil.java @@ -7,17 +7,19 @@ */ package org.opendaylight.restconf.utils.mapping; -import com.google.common.base.Preconditions; +import java.net.URI; import java.text.SimpleDateFormat; import java.util.Collection; +import java.util.Date; import java.util.Set; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.restconf.Draft18.IetfYangLibrary; import org.opendaylight.restconf.Draft18.MonitoringModule; import org.opendaylight.restconf.Draft18.MonitoringModule.QueryParams; -import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil; +import org.opendaylight.restconf.utils.parser.ParserIdentifier; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160621.module.list.Module.ConformanceType; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; @@ -41,7 +43,9 @@ import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; 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.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; /** * Util class for mapping nodes @@ -365,66 +369,6 @@ public final class RestconfMappingNodeUtil { throw new RestconfDocumentedException(qnameOfSchema.getLocalName() + " doesn't exist."); } - /** - * Mapping {@link MapEntryNode} stream entries of stream to - * {@link ListSchemaNode} - * - * @param streamName - * - stream name - * @param streamListSchemaNode - * - mapped {@link DataSchemaNode} - * @return {@link MapEntryNode} - */ - public static MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamListSchemaNode) { - Preconditions.checkState(streamListSchemaNode instanceof ListSchemaNode); - final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamListSchemaNode; - final DataContainerNodeAttrBuilder streamNodeValues = Builders - .mapEntryBuilder(listStreamSchemaNode); - - // STREAM NAME - fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.NAME, streamName); - - // STREAM DESCRIPTION - fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.DESCRIPTION, - RestconfMappingStreamConstants.DESCRIPTION); - - // STREAM REPLAY_SUPPORT - fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.REPLAY_SUPPORT, - RestconfMappingStreamConstants.REPLAY_SUPPORT); - - // STREAM REPLAY_LOG - fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.REPLAY_LOG, - RestconfMappingStreamConstants.REPLAY_LOG); - - // STREAM EVENTS - fillListWithLeaf(listStreamSchemaNode, streamNodeValues, RestconfMappingNodeConstants.EVENTS, - RestconfMappingStreamConstants.EVENTS); - - return streamNodeValues.build(); - } - - /** - * Method for filling {@link ListSchemaNode} with {@link LeafSchemaNode} - * - * @param listStreamSchemaNode - * - {@link ListSchemaNode} - * @param streamNodeValues - * - filled {@link DataContainerNodeAttrBuilder} - * @param nameSchemaNode - * - name of mapped leaf - * @param value - * - value for mapped node - */ - private static void fillListWithLeaf( - final ListSchemaNode listStreamSchemaNode, - final DataContainerNodeAttrBuilder streamNodeValues, - final String nameSchemaNode, final Object value) { - final DataSchemaNode schemaNode = RestconfSchemaUtil - .findSchemaNodeInCollection(listStreamSchemaNode.getChildNodes(), nameSchemaNode); - Preconditions.checkState(schemaNode instanceof LeafSchemaNode); - streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) schemaNode).withValue(value).build()); - } - /** * Map capabilites by ietf-restconf-monitoring * @@ -459,7 +403,7 @@ public final class RestconfMappingNodeUtil { * - builder of parent for children * @param leafListSchema */ - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "rawtypes" }) private static void fillLeafListCapa(final ListNodeBuilder builder, final LeafListSchemaNode leafListSchema) { builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.DEPTH)); builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.FIELDS)); @@ -500,4 +444,170 @@ public final class RestconfMappingNodeUtil { throw new RestconfDocumentedException( childQName.getLocalName() + " doesn't exist in container " + MonitoringModule.CONT_RESTCONF_STATE_NAME); } + + /** + * Map data of yang notification to normalized node according to + * ietf-restconf-monitoring + * + * @param notifiQName + * - qname of notification from listener + * @param notifications + * - list of notifications for find schema of notification by + * notifiQName + * @param start + * - start-time query parameter of notification + * @param outputType + * - output type of notification + * @param uri + * - location of registered listener for sending data of + * notification + * @param monitoringModule + * - ietf-restconf-monitoring module + * @param existParent + * - true if data of parent - + * ietf-restconf-monitoring:restconf-state/streams - exist in DS + * @return mapped data of notification - map entry node if parent exists, + * container streams with list and map entry node if not + */ + @SuppressWarnings("rawtypes") + public static NormalizedNode mapYangNotificationStreamByIetfRestconfMonitoring(final QName notifiQName, + final Set notifications, final Date start, final String outputType, + final URI uri, final Module monitoringModule, final boolean existParent) { + for (final NotificationDefinition notificationDefinition : notifications) { + if (notificationDefinition.getQName().equals(notifiQName)) { + final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule + .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME)) + .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME)) + .getDataChildByName(MonitoringModule.LIST_STREAM_QNAME); + final DataContainerNodeAttrBuilder streamEntry = + Builders.mapEntryBuilder((ListSchemaNode) streamListSchema); + + final ListSchemaNode listSchema = ((ListSchemaNode) streamListSchema); + prepareLeafAndFillEntryBuilder(streamEntry, + listSchema.getDataChildByName(MonitoringModule.LEAF_NAME_STREAM_QNAME), + notificationDefinition.getQName().getLocalName()); + if ((notificationDefinition.getDescription() != null) + && !notificationDefinition.getDescription().equals("")) { + prepareLeafAndFillEntryBuilder(streamEntry, + listSchema.getDataChildByName(MonitoringModule.LEAF_DESCR_STREAM_QNAME), + notificationDefinition.getDescription()); + } + prepareLeafAndFillEntryBuilder(streamEntry, + listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true); + prepareLeafAndFillEntryBuilder(streamEntry, + listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start)); + prepareListAndFillEntryBuilder(streamEntry, + (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME), + outputType, uri); + + if (!existParent) { + final DataSchemaNode contStreamsSchema = ((ContainerSchemaNode) monitoringModule + .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME)) + .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME); + return Builders.containerBuilder((ContainerSchemaNode) contStreamsSchema).withChild(Builders + .mapBuilder((ListSchemaNode) streamListSchema).withChild(streamEntry.build()) + .build()).build(); + } + return streamEntry.build(); + } + } + + throw new RestconfDocumentedException(notifiQName + " doesn't exist in any modul"); + } + + private static void prepareListAndFillEntryBuilder( + final DataContainerNodeAttrBuilder streamEntry, + final ListSchemaNode listSchemaNode, final String outputType, final URI uriToWebsocketServer) { + final CollectionNodeBuilder accessListBuilder = Builders.mapBuilder(listSchemaNode); + final DataContainerNodeAttrBuilder entryAccessList = + Builders.mapEntryBuilder(listSchemaNode); + prepareLeafAndFillEntryBuilder(entryAccessList, + listSchemaNode.getDataChildByName(MonitoringModule.LEAF_ENCODING_ACCESS_QNAME), outputType); + prepareLeafAndFillEntryBuilder(entryAccessList, + listSchemaNode.getDataChildByName(MonitoringModule.LEAF_LOCATION_ACCESS_QNAME), + uriToWebsocketServer.toString()); + streamEntry.withChild(accessListBuilder.withChild(entryAccessList.build()).build()); + } + + /** + * @param streamEntry + * @param dataChildByName + * @param localName + */ + private static void prepareLeafAndFillEntryBuilder( + final DataContainerNodeAttrBuilder streamEntry, + final DataSchemaNode leafSchema, final Object value) { + streamEntry.withChild(Builders.leafBuilder((LeafSchemaNode) leafSchema).withValue(value).build()); + } + + /** + * Map data of data change notification to normalized node according to + * ietf-restconf-monitoring + * + * @param path + * - path of data to listen on + * @param start + * - start-time query parameter of notification + * @param outputType + * - output type of notification + * @param uri + * - location of registered listener for sending data of + * notification + * @param monitoringModule + * - ietf-restconf-monitoring module + * @param existParent + * - true if data of parent - + * ietf-restconf-monitoring:restconf-state/streams - exist in DS + * @param schemaContext + * - schemaContext for parsing instance identifier to get schema + * node of data + * @return mapped data of notification - map entry node if parent exists, + * container streams with list and map entry node if not + */ + @SuppressWarnings("rawtypes") + public static NormalizedNode mapDataChangeNotificationStreamByIetfRestconfMonitoring( + final YangInstanceIdentifier path, final Date start, + final String outputType, final URI uri, final Module monitoringModule, final boolean existParent, + final SchemaContext schemaContext) { + final SchemaNode schemaNode = ParserIdentifier + .toInstanceIdentifier(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext), + schemaContext, null) + .getSchemaNode(); + final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule + .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME)) + .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME)) + .getDataChildByName(MonitoringModule.LIST_STREAM_QNAME); + final DataContainerNodeAttrBuilder streamEntry = + Builders.mapEntryBuilder((ListSchemaNode) streamListSchema); + + final ListSchemaNode listSchema = ((ListSchemaNode) streamListSchema); + prepareLeafAndFillEntryBuilder(streamEntry, + listSchema.getDataChildByName(MonitoringModule.LEAF_NAME_STREAM_QNAME), + schemaNode.getQName().getLocalName()); + if ((schemaNode.getDescription() != null) && !schemaNode.getDescription().equals("")) { + prepareLeafAndFillEntryBuilder(streamEntry, + listSchema.getDataChildByName(MonitoringModule.LEAF_DESCR_STREAM_QNAME), + schemaNode.getDescription()); + } + prepareLeafAndFillEntryBuilder(streamEntry, + listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true); + prepareLeafAndFillEntryBuilder(streamEntry, + listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME), + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start)); + prepareListAndFillEntryBuilder(streamEntry, + (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME), outputType, + uri); + + if (!existParent) { + final DataSchemaNode contStreamsSchema = ((ContainerSchemaNode) monitoringModule + .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME)) + .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME); + return Builders + .containerBuilder((ContainerSchemaNode) contStreamsSchema).withChild(Builders + .mapBuilder((ListSchemaNode) streamListSchema).withChild(streamEntry.build()).build()) + .build(); + } + return streamEntry.build(); + } } diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java index 2b7e7559e3..18faa8c388 100644 --- a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java +++ b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtil.java @@ -7,17 +7,11 @@ */ package org.opendaylight.restconf.utils.schema.context; -import com.google.common.base.Preconditions; import java.util.Collection; -import java.util.Set; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag; import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType; -import org.opendaylight.restconf.Draft18; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; -import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaNode; /** @@ -30,73 +24,6 @@ public final class RestconfSchemaUtil { throw new UnsupportedOperationException("Util class"); } - /** - * Get {@link DataSchemaNode} from {@link Module} Restconf module by - * {@link String} schema node name. - * - * @param restconfModule - * - restconf module - * @param schemaNodeName - * - schema node name - * @return {@link DataSchemaNode} - */ - public static DataSchemaNode getRestconfSchemaNode(final Module restconfModule, final String schemaNodeName) { - - final Set groupings = restconfModule.getGroupings(); - final GroupingDefinition restGroup = findSchemaNodeInCollection(groupings, - Draft18.RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE); - final Collection childNodes = restGroup.getChildNodes(); - final DataSchemaNode restCont = childNodes.iterator().next(); - - return findSchemaNode(restCont, schemaNodeName); - } - - /** - * Find specific {@link DataSchemaNode} child in {@link DataNodeContainer} - * by {@link String} schema node name. - * - * @param restCont - * - restconf container - * @param schemaNodeName - * - schema node name - * @return {@link DataSchemaNode} - */ - private static DataSchemaNode findSchemaNode(final DataSchemaNode restCont, final String schemaNodeName) { - switch (schemaNodeName) { - //MODULES - case Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE: - final DataSchemaNode moduleListSchNode = findSchemaNodeInCollection( - ((DataNodeContainer) findSchemaNode(restCont, - Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE)).getChildNodes(), - Draft18.RestconfModule.MODULE_LIST_SCHEMA_NODE); - Preconditions.checkNotNull(moduleListSchNode); - return moduleListSchNode; - case Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE: - final DataSchemaNode modulesContSchNode = findSchemaNodeInCollection(((DataNodeContainer) restCont).getChildNodes(), - Draft18.RestconfModule.MODULES_CONTAINER_SCHEMA_NODE); - Preconditions.checkNotNull(modulesContSchNode); - return modulesContSchNode; - - //STREAMS - case Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE: - final DataSchemaNode streamListSchNode = findSchemaNodeInCollection( - ((DataNodeContainer) findSchemaNode(restCont, - Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE)).getChildNodes(), - Draft18.MonitoringModule.STREAM_LIST_SCHEMA_NODE); - Preconditions.checkNotNull(streamListSchNode); - return streamListSchNode; - case Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE: - final DataSchemaNode streamsContSchNode = findSchemaNodeInCollection( - ((DataNodeContainer) restCont).getChildNodes(), - Draft18.MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE); - Preconditions.checkNotNull(streamsContSchNode); - return streamsContSchNode; - default: - throw new RestconfDocumentedException("Schema node " + schemaNodeName + " does not exist in module.", - ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); - } - } - /** * Find child of {@link SchemaNode} in {@link Collection} by {@link String} * schema node name. diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java index 70f854973a..35c6e1de33 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/BrokerFacadeTest.java @@ -31,6 +31,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -46,6 +47,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService; import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession; import org.opendaylight.netconf.sal.restconf.impl.BrokerFacade; import org.opendaylight.netconf.sal.restconf.impl.ControllerContext; @@ -60,6 +62,8 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter; import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter; import org.opendaylight.netconf.sal.streams.listeners.Notificator; +import org.opendaylight.restconf.handlers.SchemaContextHandler; +import org.opendaylight.restconf.handlers.TransactionChainHandler; import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.common.QName; @@ -326,7 +330,7 @@ public class BrokerFacadeTest { * Create, register, close and remove notification listener. */ @Test - public void testRegisterToListenNotificationChanges() { + public void testRegisterToListenNotificationChanges() throws Exception { // create test notification listener final String identifier = "create-notification-stream/toaster:toastDone"; final SchemaPath path = SchemaPath.create(true, @@ -350,6 +354,16 @@ public class BrokerFacadeTest { // registrations should be invoked only once verify(this.domNotification, times(1)).registerNotificationListener(listener, listener.getSchemaPath()); + final DOMTransactionChain transactionChain = mock(DOMTransactionChain.class); + final DOMDataWriteTransaction wTx = mock(DOMDataWriteTransaction.class); + final CheckedFuture checked = Futures.immediateCheckedFuture(""); + when(wTx.submit()).thenReturn(checked); + when(transactionChain.newWriteOnlyTransaction()).thenReturn(wTx); + final TransactionChainHandler transactionChainHandler = new TransactionChainHandler(transactionChain); + final SchemaContextHandler schemaHandler = Mockito.mock(SchemaContextHandler.class); + final SchemaContext schCtx = TestUtils.loadSchemaContext("/modules"); + when(schemaHandler.get()).thenReturn(schCtx); + listener.setCloseVars(transactionChainHandler, schemaHandler); // close and remove test notification listener listener.close(); Notificator.removeNotificationListenerIfNoSubscriberExists(listener); diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfStreamsServiceTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfStreamsServiceTest.java deleted file mode 100644 index 2ceacd9111..0000000000 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/rest/services/impl/RestconfStreamsServiceTest.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 which accompanies this distribution, - * and is available at http://www.eclipse.org/legal/epl-v10.html - */ - -package org.opendaylight.restconf.rest.services.impl; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; -import static org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.EMPTY; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; -import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext; -import org.opendaylight.netconf.sal.streams.listeners.Notificator; -import org.opendaylight.restconf.Draft18; -import org.opendaylight.restconf.base.services.api.RestconfStreamsService; -import org.opendaylight.restconf.base.services.impl.RestconfStreamsServiceImpl; -import org.opendaylight.restconf.handlers.SchemaContextHandler; -import org.opendaylight.restconf.utils.mapping.RestconfMappingNodeConstants; -import org.opendaylight.restconf.utils.mapping.RestconfMappingStreamConstants; -import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; -import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; -import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerAttrNode; -import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; - -/** - * Unit tests for {@link RestconfStreamsServiceImpl} - */ -public class RestconfStreamsServiceTest { - private static final List expectedStreams = Arrays.asList(new String[] {"stream-1", "stream-2", "stream-3"}); - - @Rule public ExpectedException thrown = ExpectedException.none(); - - @Mock private SchemaContextHandler contextHandler; - @Mock private SchemaContext mockSchemaContext; - - // service under test - private RestconfStreamsService streamsService; - - // schema context with testing Restconf modules - private SchemaContext schemaContext; - - @Before - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - - this.schemaContext = TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing"); - this.streamsService = new RestconfStreamsServiceImpl(this.contextHandler); - } - - @BeforeClass - public static void setupTestStreams() { - // clean - Notificator.removeAllListeners(); - - // put test streams - Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(0), - NotificationOutputType.XML); - Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(1), - NotificationOutputType.XML); - Notificator.createListener(EMPTY, RestconfStreamsServiceTest.expectedStreams.get(2), - NotificationOutputType.XML); - } - - @AfterClass - public static void removeTestStreams() { - Notificator.removeAllListeners(); - } - - /** - * Test of successful initialization of streams service. - */ - @Test - public void restconfStreamsServiceImplInitTest() { - assertNotNull("Streams service should be initialized and not null", this.streamsService); - } - - /** - * Positive test to get all available streams supported by the server. Loaded streams are compared to expected - * streams. - */ - @Test - public void getAvailableStreamsTest() throws Exception { - // prepare conditions - get correct Restconf module - when(this.contextHandler.get()).thenReturn(this.mockSchemaContext); - when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME - .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision())) - .thenReturn(getTestingRestconfModule("ietf-restconf")); - - // make test - final NormalizedNodeContext nodeContext = this.streamsService.getAvailableStreams(null); - - // verify loaded streams - assertNotNull("Normalized node context should not be null", nodeContext); - verifyStreams(((ContainerNode) nodeContext.getData()).getValue()); - } - - /** - * Try to get all available streams supported by the server when current SchemaContext is - * null expecting NullPointerException. - */ - @Test - public void getAvailableStreamsNullSchemaContextNegativeTest() { - // prepare conditions - returned SchemaContext is null - when(this.contextHandler.get()).thenReturn(null); - - // make test - this.thrown.expect(NullPointerException.class); - this.streamsService.getAvailableStreams(null); - } - - /** - * Try to get all available streams supported by the server when Restconf module is missing in - * SchemaContext expecting NullPointerException. - */ - @Test - public void getAvailableStreamsMissingRestconfModuleNegativeTest() { - // prepare conditions - get null Restconf module - when(this.contextHandler.get()).thenReturn(this.mockSchemaContext); - when(this.mockSchemaContext.findModuleByNamespaceAndRevision(Draft18.RestconfModule.IETF_RESTCONF_QNAME - .getNamespace(), Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision())).thenReturn(null); - - // make test - this.thrown.expect(NullPointerException.class); - this.streamsService.getAvailableStreams(null); - } - - /** - * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by - * name or by namespace. This method is looking for Restconf test module by its name. - * @param s Testing Restconf module name - * @return Restconf module - */ - private Module getTestingRestconfModule(final String s) { - return this.schemaContext.findModuleByName(s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()); - } - - /** - * Verify loaded streams - * @param streams Streams to be verified - */ - private void verifyStreams(final Collection> streams) { - assertNotNull("Collection of streams should not be empty", streams); - assertFalse("Collection of streams should not be empty", Iterables.isEmpty(streams)); - final Iterator> iterator = streams.iterator(); - - final List loadedStreams = new ArrayList<>(); - for (final Object stream : (Collection) iterator.next().getValue()) { - final Iterator mapEntries = ((AbstractImmutableDataContainerAttrNode) stream) - .getChildren().entrySet().iterator(); - - final List allowedKeys = Lists.newArrayList( - RestconfMappingNodeConstants.NAME, - RestconfMappingNodeConstants.DESCRIPTION, - RestconfMappingNodeConstants.REPLAY_SUPPORT, - RestconfMappingNodeConstants.REPLAY_LOG, - RestconfMappingNodeConstants.EVENTS); - - while (mapEntries.hasNext()) { - final Map.Entry e = ((AbstractMap.SimpleImmutableEntry) mapEntries.next()); - final String key = ((NodeIdentifier) e.getKey()).getNodeType().getLocalName(); - - assertTrue("Not allowed key", allowedKeys.contains(key)); - - switch (key) { - case RestconfMappingNodeConstants.NAME : - loadedStreams.add((String) ((LeafNode) e.getValue()).getValue()); - break; - case RestconfMappingNodeConstants.DESCRIPTION : - assertEquals("Stream description value is not as expected", - RestconfMappingStreamConstants.DESCRIPTION, ((LeafNode) e.getValue()).getValue()); - break; - case RestconfMappingNodeConstants.REPLAY_SUPPORT : - assertEquals("Stream replay support value is not as expected", - RestconfMappingStreamConstants.REPLAY_SUPPORT, ((LeafNode) e.getValue()).getValue()); - break; - case RestconfMappingNodeConstants.REPLAY_LOG : - assertEquals("Stream replay log value is not as expected", - RestconfMappingStreamConstants.REPLAY_LOG, ((LeafNode) e.getValue()).getValue()); - break; - case RestconfMappingNodeConstants.EVENTS : - assertEquals("Stream events value is not as expected", - RestconfMappingStreamConstants.EVENTS, ((LeafNode) e.getValue()).getValue()); - break; - } - } - } - - // sort and compare - loadedStreams.sort((s1, s2) -> s1.compareTo(s2)); - assertEquals("Returned streams are not as expected", expectedStreams, loadedStreams); - } -} diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImplTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImplTest.java index 323198fe54..619cab5013 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImplTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/restful/services/impl/RestconfStreamsSubscriptionServiceImplTest.java @@ -13,6 +13,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; @@ -33,6 +34,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; @@ -44,6 +46,8 @@ import org.opendaylight.restconf.handlers.DOMDataBrokerHandler; import org.opendaylight.restconf.handlers.NotificationServiceHandler; import org.opendaylight.restconf.handlers.SchemaContextHandler; import org.opendaylight.restconf.handlers.TransactionChainHandler; +import org.opendaylight.restconf.parser.IdentifierCodec; +import org.opendaylight.yang.gen.v1.urn.sal.restconf.event.subscription.rev140708.NotificationOutputTypeGrouping.NotificationOutputType; import org.opendaylight.yangtools.concepts.ListenerRegistration; public class RestconfStreamsSubscriptionServiceImplTest { @@ -58,6 +62,8 @@ public class RestconfStreamsSubscriptionServiceImplTest { private UriInfo uriInfo; @Mock private NotificationServiceHandler notificationServiceHandler; + @Mock + private TransactionChainHandler transactionHandler; private SchemaContextHandler schemaHandler; @@ -67,9 +73,16 @@ public class RestconfStreamsSubscriptionServiceImplTest { final TransactionChainHandler txHandler = Mockito.mock(TransactionChainHandler.class); final DOMTransactionChain domTx = Mockito.mock(DOMTransactionChain.class); + Mockito.when(this.transactionHandler.get()).thenReturn(domTx); Mockito.when(txHandler.get()).thenReturn(domTx); final DOMDataWriteTransaction wTx = Mockito.mock(DOMDataWriteTransaction.class); Mockito.when(domTx.newWriteOnlyTransaction()).thenReturn(wTx); + final DOMDataReadWriteTransaction rwTx = Mockito.mock(DOMDataReadWriteTransaction.class); + final CheckedFuture checkedFuture = Futures.immediateCheckedFuture(true); + Mockito.when(rwTx.exists(Mockito.any(), Mockito.any())).thenReturn(checkedFuture); + final CheckedFuture checkedFutureEmpty = Futures.immediateCheckedFuture(""); + Mockito.when(rwTx.submit()).thenReturn(checkedFutureEmpty); + Mockito.when(domTx.newReadWriteTransaction()).thenReturn(rwTx); final CheckedFuture checked = Mockito.mock(CheckedFuture.class); Mockito.when(wTx.submit()).thenReturn(checked); final Object valueObj = null; @@ -109,12 +122,16 @@ public class RestconfStreamsSubscriptionServiceImplTest { } @Test - public void testSubscribeToStream() { + public void testSubscribeToStream() throws Exception { final UriBuilder uriBuilder = UriBuilder.fromUri(uri); + final ListenerAdapter createListener = Notificator.createListener( + IdentifierCodec.deserialize("toaster:toaster/toasterStatus", this.schemaHandler.get()), + "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", + NotificationOutputType.XML); doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder(); final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler, - this.schemaHandler); + this.schemaHandler, this.transactionHandler); final NormalizedNodeContext response = streamsSubscriptionService .subscribeToStream( "data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE", @@ -130,7 +147,7 @@ public class RestconfStreamsSubscriptionServiceImplTest { doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder(); final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler, - this.schemaHandler); + this.schemaHandler, this.transactionHandler); streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/scope=ONE", this.uriInfo); } @@ -140,7 +157,7 @@ public class RestconfStreamsSubscriptionServiceImplTest { doReturn(uriBuilder).when(this.uriInfo).getAbsolutePathBuilder(); final RestconfStreamsSubscriptionServiceImpl streamsSubscriptionService = new RestconfStreamsSubscriptionServiceImpl(this.dataBrokerHandler, this.notificationServiceHandler, - this.schemaHandler); + this.schemaHandler, this.transactionHandler); streamsSubscriptionService.subscribeToStream("toaster:toaster/toasterStatus/datastore=OPERATIONAL", this.uriInfo); } diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java index c078a8468f..f650f85a56 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/mapping/RestconfMappingNodeUtilTest.java @@ -8,22 +8,16 @@ package org.opendaylight.restconf.utils.mapping; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.google.common.collect.Sets; -import java.util.AbstractMap; +import java.net.URI; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; +import java.util.Date; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import org.junit.Assert; import org.junit.Before; @@ -34,15 +28,11 @@ import org.junit.rules.ExpectedException; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; -import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; -import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag; -import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType; -import org.opendaylight.restconf.Draft18; import org.opendaylight.restconf.Draft18.IetfYangLibrary; import org.opendaylight.restconf.Draft18.MonitoringModule; import org.opendaylight.restconf.Draft18.MonitoringModule.QueryParams; import org.opendaylight.restconf.Draft18.RestconfModule; -import org.opendaylight.restconf.utils.schema.context.RestconfSchemaUtil; +import org.opendaylight.restconf.utils.parser.ParserIdentifier; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -56,9 +46,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.impl.schema.nodes.AbstractImmutableDataContainerAttrNode; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; @@ -80,20 +67,17 @@ public class RestconfMappingNodeUtilTest { private static Set modules; private static SchemaContext schemaContext; - private static SchemaContext schemaContextCapabilites; + private static SchemaContext schemaContextMonitoring; private static Set modulesRest; - private Set allStreamChildNodes; - @BeforeClass public static void loadTestSchemaContextAndModules() throws Exception { - RestconfMappingNodeUtilTest.schemaContext = TestRestconfUtils.loadSchemaContext( + schemaContext = TestRestconfUtils.loadSchemaContext( "/modules/restconf-module-testing"); - RestconfMappingNodeUtilTest.schemaContextCapabilites = TestRestconfUtils.loadSchemaContext("/modules"); - RestconfMappingNodeUtilTest.modules = schemaContextCapabilites.getModules(); - RestconfMappingNodeUtilTest.modulesRest = - TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing").getModules(); + schemaContextMonitoring = TestRestconfUtils.loadSchemaContext("/modules"); + modules = schemaContextMonitoring.getModules(); + modulesRest = TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing").getModules(); } @Before @@ -106,9 +90,6 @@ public class RestconfMappingNodeUtilTest { QName.create("", RestconfMappingNodeConstants.REPLAY_SUPPORT)); when(this.leafReplayLog.getQName()).thenReturn(QName.create(RestconfMappingNodeConstants.REPLAY_LOG)); when(this.leafEvents.getQName()).thenReturn(QName.create("", RestconfMappingNodeConstants.EVENTS)); - - this.allStreamChildNodes = Sets.newHashSet( - this.leafName, this.leafDescription, this.leafReplaySupport, this.leafReplayLog, this.leafEvents); } /** @@ -130,7 +111,7 @@ public class RestconfMappingNodeUtilTest { @Test public void restconfStateCapabilitesTest() { - final Module monitoringModule = schemaContextCapabilites + final Module monitoringModule = schemaContextMonitoring .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE); final NormalizedNode>> normNode = RestconfMappingNodeUtil.mapCapabilites(monitoringModule); @@ -154,251 +135,67 @@ public class RestconfMappingNodeUtilTest { Assert.assertTrue(listOfValues.contains(QueryParams.WITH_DEFAULTS)); } - /** - * Positive test of writing one stream to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} and checking if stream - * was correctly written. - */ - @Test - public void toStreamEntryNodeTest() { - // test stream name - final String stream1 = "stream-1"; - - // get list stream node from Restconf module - final ListSchemaNode listStream = (ListSchemaNode) RestconfSchemaUtil.getRestconfSchemaNode( - getTestingRestconfModule("ietf-restconf"), MonitoringModule.STREAM_LIST_SCHEMA_NODE); - - // write stream to list stream node - final MapEntryNode mapEntryNode = RestconfMappingNodeUtil.toStreamEntryNode(stream1, listStream); - - // verify - verifyStream(stream1, mapEntryNode); - } - - /** - * Try to map streams when {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} is null. - * Test is expected to fail catching IllegalStateException. - */ - @Test - public void toStreamEntryNodeNullListStreamNegativeTest() { - this.thrown.expect(IllegalStateException.class); - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", null); - } - - /** - * Test trying to map streams to {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} which is not of type list. - * Test is expected to fail with IllegalStateException. - */ @Test - public void toStreamEntryNodeIllegalListStreamNegativeTest() { - this.thrown.expect(IllegalStateException.class); - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mock(LeafSchemaNode.class)); - } - - /** - * Test case with target {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} which does not contain any child nodes. - * Test is catching RestconfDocumentedException and error type, error tag and error status code are - * compared to expected values. - */ - @Test - public void toStreamEntryNodeSchemaNodeWithoutChildsNegativeTest() { - final ListSchemaNode mockListNode = mock(ListSchemaNode.class); - when(mockListNode.getChildNodes()).thenReturn(Collections.EMPTY_SET); - - try { - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", mockListNode); - fail("Test should fail due to no child nodes in" - + MonitoringModule.STREAM_LIST_SCHEMA_NODE - + " node"); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); - } - } + public void toStreamEntryNodeTest() throws Exception { + final YangInstanceIdentifier path = + ParserIdentifier.toInstanceIdentifier("nested-module:depth1-cont/depth2-leaf1", schemaContextMonitoring, null).getInstanceIdentifier(); + final Date start = new Date(); + final String outputType = "XML"; + final URI uri = new URI("uri"); + final Module monitoringModule = schemaContextMonitoring + .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE); + final boolean exist = true; - /** - * Test case when target list stream does not contain child with name {@link RestconfMappingNodeConstants#NAME}. - * Test is catching RestconfDocumentedException and error type, error tag and error status code are - * compared to expected values. - */ - @Test - public void toStreamEntryNodeMissingStreamNameNegativeTest() { - prepareMockListWithMissingLeaf(this.leafName); + final Map map = + prepareMap(path.getLastPathArgument().getNodeType().getLocalName(), uri, start, outputType); - try { - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - fail("Test should fail due to missing " - + RestconfMappingNodeConstants.NAME - + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); - } + final NormalizedNode mappedData = RestconfMappingNodeUtil.mapDataChangeNotificationStreamByIetfRestconfMonitoring(path, start, outputType, uri, + monitoringModule, exist, schemaContextMonitoring); + assertNotNull(mappedData); + testData(map, mappedData); } - /** - * Test case when target list stream does not contain child with name - * {@link RestconfMappingNodeConstants#DESCRIPTION}. Test is catching RestconfDocumentedException and - * checking error type and error tag. - */ @Test - public void toStreamEntryNodeMissingStreamDescriptionNegativeTest() { - prepareMockListWithMissingLeaf(this.leafDescription); - - try { - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - fail("Test should fail due to missing " - + RestconfMappingNodeConstants.DESCRIPTION - + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); - } - } + public void toStreamEntryNodeNotifiTest() throws Exception { + final Date start = new Date(); + final String outputType = "JSON"; + final URI uri = new URI("uri"); + final Module monitoringModule = schemaContextMonitoring + .findModuleByNamespaceAndRevision(MonitoringModule.URI_MODULE, MonitoringModule.DATE); + final boolean exist = true; - /** - * Test case when target list stream does not contain child with name - * {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}. Test is catching RestconfDocumentedException - * and checking error type and error tag. - */ - @Test - public void toStreamEntryNodeMissingStreamReplaySupportNegativeTest() { - prepareMockListWithMissingLeaf(this.leafReplaySupport); + final Map map = prepareMap("notifi", uri, start, outputType); + map.put(MonitoringModule.LEAF_DESCR_STREAM_QNAME, "Notifi"); - try { - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - fail("Test should fail due to missing " - + RestconfMappingNodeConstants.REPLAY_SUPPORT - + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); - } + final QName notifiQName = QName.create("urn:nested:module", "2014-06-3", "notifi"); + final NormalizedNode mappedData = + RestconfMappingNodeUtil.mapYangNotificationStreamByIetfRestconfMonitoring(notifiQName, schemaContextMonitoring.getNotifications(), start, + outputType, uri, monitoringModule, exist); + assertNotNull(mappedData); + testData(map, mappedData); } - /** - * Test case when target list stream does not contain child with name - * {@link RestconfMappingNodeConstants#REPLAY_LOG}. Test is catching RestconfDocumentedException and - * checking error type and error tag. - */ - @Test - public void toStreamEntryNodeMissingStreamReplayLogNegativeTest() { - prepareMockListWithMissingLeaf(this.leafReplayLog); - - try { - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - fail("Test should fail due to missing " - + RestconfMappingNodeConstants.REPLAY_LOG - + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); - } + private Map prepareMap(final String name, final URI uri, final Date start, final String outputType) { + final Map map = new HashMap<>(); + map.put(MonitoringModule.LEAF_NAME_STREAM_QNAME, name); + map.put(MonitoringModule.LEAF_LOCATION_ACCESS_QNAME, uri.toString()); + map.put(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME, true); + map.put(MonitoringModule.LEAF_START_TIME_STREAM_QNAME, + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'XXX").format(start)); + map.put(MonitoringModule.LEAF_ENCODING_ACCESS_QNAME, outputType); + return map; } - /** - * Test case when target list stream does not contain child with name {@link RestconfMappingNodeConstants#EVENTS}. - * Test is catching RestconfDocumentedException and checking error type, error tag and error status - * code. - */ - @Test - public void toStreamEntryNodeMissingStreamEventsNegativeTest() { - prepareMockListWithMissingLeaf(this.leafEvents); - - try { - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - fail("Test should fail due to missing " - + RestconfMappingNodeConstants.EVENTS - + " node in " + MonitoringModule.STREAM_LIST_SCHEMA_NODE); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); + private void testData(final Map map, final NormalizedNode mappedData) { + for (final DataContainerChild child : ((MapEntryNode) mappedData).getValue()) { + if (child instanceof LeafNode) { + final LeafNode leaf = ((LeafNode) child); + Assert.assertTrue(map.containsKey(leaf.getNodeType())); + Assert.assertEquals(map.get(leaf.getNodeType()), leaf.getValue()); + } } } - /** - * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#NAME}. Test is - * expecting IllegalStateException. - */ - @Test - public void toStreamEntryNodeStreamNameNegativeTest() { - prepareMockListWithIllegalLeaf(this.leafName); - - this.thrown.expect(IllegalStateException.class); - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - } - - /** - * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#DESCRIPTION}. - * Test is expecting IllegalStateException. - */ - @Test - public void toStreamEntryNodeStreamDescriptionNegativeTest() { - prepareMockListWithIllegalLeaf(this.leafDescription); - - this.thrown.expect(IllegalStateException.class); - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - } - - /** - * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_SUPPORT}. - * Test is expecting IllegalStateException. - */ - @Test - public void toStreamEntryNodeStreamReplaySupportNegativeTest() { - prepareMockListWithIllegalLeaf(this.leafReplaySupport); - - this.thrown.expect(IllegalStateException.class); - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - } - - /** - * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#REPLAY_LOG}. - * Test is expecting IllegalStateException. - */ - @Test - public void toStreamEntryNodeStreamReplayLogNegativeTest() { - prepareMockListWithIllegalLeaf(this.leafReplayLog); - - this.thrown.expect(IllegalStateException.class); - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - } - - /** - * Test case when target list stream contains child with name {@link RestconfMappingNodeConstants#EVENTS}. Test is - * expecting IllegalStateException. - */ - @Test - public void toStreamEntryNodeStreamEventsNegativeTest() { - prepareMockListWithIllegalLeaf(this.leafEvents); - - this.thrown.expect(IllegalStateException.class); - RestconfMappingNodeUtil.toStreamEntryNode("stream-1", this.mockStreamList); - } - /** * Verify loaded modules * @@ -457,93 +254,4 @@ public class RestconfMappingNodeUtilTest { loadedModules.remove(name); } } - - /** - * Verify if a stream was correctly written into {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} node in Restconf - * module. - * @param streamName Expected stream name - * @param streamNode Writetn strem node from Restconf module - */ - private final void verifyStream(final String streamName, final MapEntryNode streamNode) { - assertNotNull("Stream node can not be null", streamNode); - final Iterator entries = ((AbstractImmutableDataContainerAttrNode) streamNode) - .getChildren().entrySet().iterator(); - boolean notAllowedKey = false; - - while (entries.hasNext()) { - final Entry e = ((AbstractMap.SimpleImmutableEntry) entries.next()); - final String key = ((YangInstanceIdentifier.NodeIdentifier) e.getKey()).getNodeType().getLocalName(); - - switch (key) { - case RestconfMappingNodeConstants.NAME : - assertEquals("Stream name value is not as expected", - streamName, ((LeafNode) e.getValue()).getValue()); - break; - case RestconfMappingNodeConstants.DESCRIPTION : - assertEquals("Stream description value is not as expected", - RestconfMappingStreamConstants.DESCRIPTION, ((LeafNode) e.getValue()).getValue()); - break; - case RestconfMappingNodeConstants.REPLAY_SUPPORT : - assertEquals("Stream replay support value is not as expected", - RestconfMappingStreamConstants.REPLAY_SUPPORT, ((LeafNode) e.getValue()).getValue()); - break; - case RestconfMappingNodeConstants.REPLAY_LOG : - assertEquals("Stream replay log value is not as expected", - RestconfMappingStreamConstants.REPLAY_LOG, ((LeafNode) e.getValue()).getValue()); - break; - case RestconfMappingNodeConstants.EVENTS : - assertEquals("Stream events value is not as expected", - RestconfMappingStreamConstants.EVENTS, ((LeafNode) e.getValue()).getValue()); - break; - default: - notAllowedKey = true; - break; - } - } - - assertFalse("Not allowed key in list stream found", notAllowedKey); - } - - /** - * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by - * name or by namespace. This method is looking for Restconf test module by its name. - * @param s Testing Restconf module name - * @return Restconf module - */ - private Module getTestingRestconfModule(final String s) { - return RestconfMappingNodeUtilTest.schemaContext.findModuleByName( - s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()); - } - - /** - * Updates {@link this#mockStreamList} to NOT contains specified leaf. - * @param leaf Leaf to be missing - */ - private void prepareMockListWithMissingLeaf(final LeafSchemaNode leaf) { - // prepare set of leaf without selected leaf - final Set childLeafs = new HashSet<>(this.allStreamChildNodes); - childLeafs.remove(leaf); - - // mock list leaf nodes - when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs); - } - - /** - * Updates {@link this#mockStreamList} to contains specified leaf which is not of type {@link LeafSchemaNode}. - * @param leaf Leaf to be changes - */ - private void prepareMockListWithIllegalLeaf(final LeafSchemaNode leaf) { - // prepare set of leaf without selected leaf - final Set childLeafs = new HashSet<>(this.allStreamChildNodes); - childLeafs.remove(leaf); - - // add leaf-list with the same local name as removed leaf - final String localName = leaf.getQName().getLocalName(); - final LeafListSchemaNode mockLeafList = mock(LeafListSchemaNode.class); - when(mockLeafList.getQName()).thenReturn(QName.create("", localName)); - childLeafs.add(mockLeafList); - - // mock list leaf nodes - when(this.mockStreamList.getChildNodes()).thenReturn(childLeafs); - } } diff --git a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtilTest.java b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtilTest.java index 43bec1c518..c572bb67ce 100644 --- a/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtilTest.java +++ b/restconf/sal-rest-connector/src/test/java/org/opendaylight/restconf/utils/schema/context/RestconfSchemaUtilTest.java @@ -8,259 +8,47 @@ package org.opendaylight.restconf.utils.schema.context; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import com.google.common.collect.Sets; -import org.junit.Before; -import org.junit.Rule; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import org.junit.Assert; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils; +import org.mockito.Mockito; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; -import org.opendaylight.netconf.sal.restconf.impl.RestconfError; -import org.opendaylight.restconf.Draft18; -import org.opendaylight.restconf.Draft18.MonitoringModule; -import org.opendaylight.restconf.Draft18.RestconfModule; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.SchemaNode; /** * Unit tests for {@link RestconfSchemaUtil} */ public class RestconfSchemaUtilTest { - // schema with testing modules - private SchemaContext schemaContext; - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Before - public void setup() throws Exception { - this.schemaContext = TestRestconfUtils.loadSchemaContext("/modules/restconf-module-testing"); - } - - /** - * Positive test for getting DataSchemaNode from Restconf module for schema node name equals to - * {@link RestconfModule#MODULE_LIST_SCHEMA_NODE} when this node can be found. - */ - @Test - public void getRestconfSchemaNodeListModuleTest() { - final DataSchemaNode dataSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode( - getTestingRestconfModule("ietf-restconf"), - RestconfModule.MODULE_LIST_SCHEMA_NODE); - - assertNotNull("Existing schema node "+ RestconfModule.MODULE_LIST_SCHEMA_NODE + " should be found", - dataSchemaNode); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getLocalName(), RestconfModule.MODULE_LIST_SCHEMA_NODE); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION); - } - - /** - * Positive test for getting DataSchemaNode from Restconf module for schema node name equals to - * {@link MonitoringModule#STREAM_LIST_SCHEMA_NODE} when this node can be found. - */ - @Test - public void getRestconfSchemaNodeListStreamTest() { - final DataSchemaNode dataSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode( - getTestingRestconfModule("ietf-restconf"), - MonitoringModule.STREAM_LIST_SCHEMA_NODE); - - assertNotNull("Existing schema node " + MonitoringModule.STREAM_LIST_SCHEMA_NODE + " should be found", - dataSchemaNode); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getLocalName(), MonitoringModule.STREAM_LIST_SCHEMA_NODE); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION); - } - - /** - * Positive test for getting DataSchemaNode from Restconf module for schema node name equals to - * {@link RestconfModule#MODULES_CONTAINER_SCHEMA_NODE} when this node can be found. - */ - @Test - public void getRestconfSchemaNodeContainerModulesTest() { - final DataSchemaNode dataSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode( - getTestingRestconfModule("ietf-restconf"), - RestconfModule.MODULES_CONTAINER_SCHEMA_NODE); - - assertNotNull("Existing schema node " + RestconfModule.MODULES_CONTAINER_SCHEMA_NODE + "should be found", - dataSchemaNode); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getLocalName(), RestconfModule.MODULES_CONTAINER_SCHEMA_NODE); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION); - } - - /** - * Positive test for getting DataSchemaNode from Restconf module for schema node name equals to - * {@link MonitoringModule#STREAMS_CONTAINER_SCHEMA_NODE} when this node can be found. - */ - @Test - public void getRestconfSchemaNodeContainerStreamsTest() { - final DataSchemaNode dataSchemaNode = RestconfSchemaUtil.getRestconfSchemaNode( - getTestingRestconfModule("ietf-restconf"), - MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE); - - assertNotNull("Existing schema node " + MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE + " should be found", - dataSchemaNode); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getLocalName(), MonitoringModule.STREAMS_CONTAINER_SCHEMA_NODE); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE); - assertEquals("Incorrect schema node was returned", - dataSchemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION); - } - - /** - * Negative test for getting DataSchemaNode from Restconf module when Restconf module is - * null. Test is expected to fail catching NullPointerException. - */ - @Test - public void getRestconfSchemaNodeNullRestconfModuleNegativeTest() { - this.thrown.expect(NullPointerException.class); - RestconfSchemaUtil.getRestconfSchemaNode(null, RestconfModule.RESTCONF_CONTAINER_SCHEMA_NODE); - } - - /** - * Negative test for getting DataSchemaNode from Restconf module when name of the schema node name is - * null. Test is expected to fail with NullPointerException. - */ - @Test - public void getRestconfSchemaNodeNullSchemaNodeNameNegativeTest() { - this.thrown.expect(NullPointerException.class); - RestconfSchemaUtil.getRestconfSchemaNode(getTestingRestconfModule("ietf-restconf"), null); - } - - /** - * Negative test for getting DataSchemaNode from Restconf module when name of the schema node name - * references to not existing node. Test is expected to fail catching code>RestconfDocumentedException and - * checking expected error type, error tag and error status code. - */ - @Test - public void getRestconfSchemaNodeNotExistingSchemaNodeNameNegativeTest() { - try { - RestconfSchemaUtil.getRestconfSchemaNode(getTestingRestconfModule("ietf-restconf"), "not-existing-node"); - fail("Test should fail due to not-existing node name"); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); - } - } - - /** - * Positive test trying to find DataSchemaNode of {@link RestconfModule#RESTCONF_GROUPING_SCHEMA_NODE} - * in Restconf module groupings collection. - */ - @Test - public void findSchemaNodeInCollectionTest() { - final SchemaNode schemaNode = RestconfSchemaUtil.findSchemaNodeInCollection( - getTestingRestconfModule("ietf-restconf").getGroupings(), - RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE); - - assertNotNull("Restconf grouping schema node should be found", schemaNode); - assertEquals("Incorrect grouping was returned", - schemaNode.getQName().getLocalName(), RestconfModule.RESTCONF_GROUPING_SCHEMA_NODE); - assertEquals("Incorrect grouping was returned", - schemaNode.getQName().getNamespace().toString(), RestconfModule.NAMESPACE); - assertEquals("Incorrect grouping was returned", - schemaNode.getQName().getFormattedRevision(), RestconfModule.REVISION); - } - - /** - * Negative test trying to find DataSchemaNode of not existing groupings schema node name in Restconf - * module grouping collection. Test is expected to fail catching RestconfDocumentedException and - * checking for correct error type, error tag and error status code. - */ - @Test - public void findSchemaNodeInCollectionNegativeTest() { - try { - RestconfSchemaUtil.findSchemaNodeInCollection( - getTestingRestconfModule("ietf-restconf").getGroupings(), "not-existing-grouping"); - fail("Test should fail due to missing not-existing grouping in Restconf grouping collection"); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); - } - } - - /** - * Negative test trying to find DataSchemaNode of existing schema node name in null - * collection. Test is expected to fail with NullPointerException. - */ @Test - public void findSchemaNodeInCollectionNullCollectionNegativeTest() { - this.thrown.expect(NullPointerException.class); - RestconfSchemaUtil.findSchemaNodeInCollection(null, RestconfModule.MODULES_CONTAINER_SCHEMA_NODE); + public void findInCollectionTest() { + final SchemaNode origSchNode = mockSchemaNode("key"); + final SchemaNode actualSch = findSchemaNodeInCollection("key", origSchNode); + Assert.assertEquals(origSchNode, actualSch); } - /** - * Negative test trying to find DataSchemaNode for schema node name in empty collection. Test is - * expected to fail with RestconfDocumentedException. Error type, error tag and error status code - * are compared to expected values. - */ - @Test - public void findSchemaNodeInCollectionEmptyCollectionNegativeTest() { - try { - RestconfSchemaUtil.findSchemaNodeInCollection( - Sets.newHashSet(), RestconfModule.MODULES_CONTAINER_SCHEMA_NODE); - fail("Test should fail due to empty schema nodes collection"); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); - } + @Test(expected = RestconfDocumentedException.class) + public void findInCollectionFailedTest() { + final SchemaNode origSchNode = mockSchemaNode("key"); + findSchemaNodeInCollection("bad_key", origSchNode); } - /** - * Negative test trying to find DataSchemaNode of null schema node name in Restconf module - * groupings collection. Test is expected to fail with RestconfDocumentedException. Error type, error - * tag and error status code are compared to expected values. - */ - @Test - public void findSchemaNodeInCollectionNullSchemaNodeName() { - try { - RestconfSchemaUtil.findSchemaNodeInCollection( - getTestingRestconfModule("ietf-restconf").getGroupings(), null); - fail("Test should fail due to null schema node name"); - } catch (final RestconfDocumentedException e) { - assertEquals("Error type is not correct", - RestconfError.ErrorType.PROTOCOL, e.getErrors().get(0).getErrorType()); - assertEquals("Error tag is not correct", - RestconfError.ErrorTag.DATA_MISSING, e.getErrors().get(0).getErrorTag()); - assertEquals("Error status code is not correct", - 404, e.getErrors().get(0).getErrorTag().getStatusCode()); + private SchemaNode findSchemaNodeInCollection(final String key, final SchemaNode... origSchNode) { + final List collection = new ArrayList<>(); + for (int i = 0; i < origSchNode.length; i++) { + collection.add(origSchNode[i]); } + return RestconfSchemaUtil.findSchemaNodeInCollection(collection, key); } - /** - * There are multiple testing Restconf modules for different test cases. It is possible to distinguish them by - * name or by namespace. This method is looking for Restconf test module by its name. - * @param s Testing Restconf module name - * @return Restconf module - */ - private Module getTestingRestconfModule(final String s) { - return this.schemaContext.findModuleByName(s, Draft18.RestconfModule.IETF_RESTCONF_QNAME.getRevision()); + private SchemaNode mockSchemaNode(final String origKey) { + final SchemaNode mockSchNode = Mockito.mock(SchemaNode.class); + Mockito.when(mockSchNode.getQName()) + .thenReturn(QName.create("ns", new SimpleDateFormat("yyyy-MM-dd").format(new Date()), origKey)); + return mockSchNode; } } diff --git a/restconf/sal-rest-connector/src/test/resources/modules/nested-module.yang b/restconf/sal-rest-connector/src/test/resources/modules/nested-module.yang index 726b8d9a5b..aa2795afeb 100644 --- a/restconf/sal-rest-connector/src/test/resources/modules/nested-module.yang +++ b/restconf/sal-rest-connector/src/test/resources/modules/nested-module.yang @@ -62,4 +62,8 @@ module nested-module { type string; } } + + notification notifi{ + description "Notifi"; + } } diff --git a/restconf/sal-rest-connector/src/test/resources/notifications/toaster.yang b/restconf/sal-rest-connector/src/test/resources/notifications/toaster.yang new file mode 100644 index 0000000000..da68016d23 --- /dev/null +++ b/restconf/sal-rest-connector/src/test/resources/notifications/toaster.yang @@ -0,0 +1,200 @@ +module toaster { + + yang-version 1; + + namespace + "http://netconfcentral.org/ns/toaster"; + + prefix toast; + + organization "Netconf Central"; + + contact + "Andy Bierman "; + + description + "YANG version of the TOASTER-MIB."; + + revision "2009-11-20" { + description + "Toaster module in progress."; + } + + + identity toast-type { + description + "Base for all bread types supported by the toaster. + New bread types not listed here nay be added in the + future."; + } + + identity white-bread { + base toast:toast-type; + description "White bread."; + } + + identity wheat-bread { + base toast-type; + description "Wheat bread."; + } + + identity wonder-bread { + base toast-type; + description "Wonder bread."; + } + + identity frozen-waffle { + base toast-type; + description "Frozen waffle."; + } + + identity frozen-bagel { + base toast-type; + description "Frozen bagel."; + } + + identity hash-brown { + base toast-type; + description "Hash browned potatos."; + } + + typedef DisplayString { + type string { + length "0 .. 255"; + } + description + "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION."; + reference + "RFC 2579, section 2."; + + } + + container toaster { + presence + "Indicates the toaster service is available"; + description + "Top-level container for all toaster database objects."; + leaf toasterManufacturer { + type DisplayString; + config false; + mandatory true; + description + "The name of the toaster's manufacturer. For instance, + Microsoft Toaster."; + } + + leaf toasterModelNumber { + type DisplayString; + config false; + mandatory true; + description + "The name of the toaster's model. For instance, + Radiant Automatic."; + } + + leaf toasterStatus { + type enumeration { + enum "up" { + value 1; + description + "The toaster knob position is up. + No toast is being made now."; + } + enum "down" { + value 2; + description + "The toaster knob position is down. + Toast is being made now."; + } + } + config false; + mandatory true; + description + "This variable indicates the current state of + the toaster."; + } + + leaf darknessFactor { + type uint32; + config true; + default 1000; + description + "The darkness factor. Basically, the number of ms to multiple the doneness value by."; + } + } // container toaster + + rpc make-toast { + description + "Make some toast. + The toastDone notification will be sent when + the toast is finished. + An 'in-use' error will be returned if toast + is already being made. + A 'resource-denied' error will be returned + if the toaster service is disabled."; + input { + leaf toasterDoneness { + type uint32 { + range "1 .. 10"; + } + default '5'; + description + "This variable controls how well-done is the + ensuing toast. It should be on a scale of 1 to 10. + Toast made at 10 generally is considered unfit + for human consumption; toast made at 1 is warmed + lightly."; + } + + leaf toasterToastType { + type identityref { + base toast:toast-type; + } + default 'wheat-bread'; + description + "This variable informs the toaster of the type of + material that is being toasted. The toaster + uses this information, combined with + toasterDoneness, to compute for how + long the material must be toasted to achieve + the required doneness."; + } + } + } // rpc make-toast + + rpc cancel-toast { + description + "Stop making toast, if any is being made. + A 'resource-denied' error will be returned + if the toaster service is disabled."; + } // rpc cancel-toast + + rpc restock-toaster { + description + "Restocks the toaster with the amount of bread specified."; + + input { + leaf amountOfBreadToStock { + type uint32; + description + "Indicates the amount of bread to re-stock"; + } + } + } + + notification toasterOutOfBread { + description + "Indicates that the toaster has run of out bread."; + } // notification toasterOutOfStock + + notification toasterRestocked { + description + "Indicates that the toaster has run of out bread."; + leaf amountOfBread { + type uint32; + description + "Indicates the amount of bread that was re-stocked"; + } + } // notification toasterOutOfStock + + } // module toaster \ No newline at end of file