2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3 * Copyright (c) 2014 Brocade Communication Systems, Inc.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.sal.restconf.impl;
11 import com.google.common.base.Objects;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Predicates;
15 import com.google.common.base.Splitter;
16 import com.google.common.base.Strings;
17 import com.google.common.base.Throwables;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.Iterables;
20 import com.google.common.collect.Lists;
21 import com.google.common.util.concurrent.CheckedFuture;
22 import java.math.BigInteger;
24 import java.net.URISyntaxException;
25 import java.text.ParseException;
26 import java.text.SimpleDateFormat;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.List;
36 import java.util.concurrent.CancellationException;
37 import java.util.concurrent.ExecutionException;
38 import javax.ws.rs.core.Response;
39 import javax.ws.rs.core.Response.ResponseBuilder;
40 import javax.ws.rs.core.Response.Status;
41 import javax.ws.rs.core.UriBuilder;
42 import javax.ws.rs.core.UriInfo;
43 import org.apache.commons.lang3.StringUtils;
44 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
45 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
46 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
47 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
48 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
49 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
50 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
51 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
52 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
53 import org.opendaylight.controller.sal.rest.api.Draft02;
54 import org.opendaylight.controller.sal.rest.api.RestconfService;
55 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
56 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
57 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
58 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
59 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
60 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
61 import org.opendaylight.controller.sal.streams.listeners.Notificator;
62 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
63 import org.opendaylight.yangtools.concepts.Codec;
64 import org.opendaylight.yangtools.yang.common.QName;
65 import org.opendaylight.yangtools.yang.common.QNameModule;
66 import org.opendaylight.yangtools.yang.common.RpcResult;
67 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
68 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
69 import org.opendaylight.yangtools.yang.data.api.Node;
70 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
71 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
76 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
77 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
79 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
81 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
82 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
83 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
84 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
85 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
86 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
87 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
88 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
89 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
91 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
92 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
93 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
94 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
95 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
96 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
97 import org.opendaylight.yangtools.yang.model.api.Module;
98 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
99 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
100 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
101 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
102 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
103 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
104 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
105 import org.opendaylight.yangtools.yang.model.util.EmptyType;
106 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
107 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
108 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
109 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
110 import org.slf4j.Logger;
111 import org.slf4j.LoggerFactory;
113 public class RestconfImpl implements RestconfService {
115 private enum UriParameters {
116 PRETTY_PRINT("prettyPrint"),
119 private String uriParameterName;
121 UriParameters(final String uriParameterName) {
122 this.uriParameterName = uriParameterName;
126 public String toString() {
127 return uriParameterName;
131 private static class TypeDef {
132 public final TypeDefinition<? extends Object> typedef;
133 public final QName qName;
135 TypeDef(final TypeDefinition<? extends Object> typedef, final QName qName) {
136 this.typedef = typedef;
141 private final static RestconfImpl INSTANCE = new RestconfImpl();
143 private static final int NOTIFICATION_PORT = 8181;
145 private static final int CHAR_NOT_FOUND = -1;
147 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
149 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
151 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
153 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
155 private BrokerFacade broker;
157 private ControllerContext controllerContext;
159 private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class);
161 private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
163 private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION;
165 private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
167 private static final Date EVENT_SUBSCRIPTION_AUGMENT_REVISION;
169 private static final String DATASTORE_PARAM_NAME = "datastore";
171 private static final String SCOPE_PARAM_NAME = "scope";
173 private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
175 private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
177 private static final QName NETCONF_BASE_QNAME;
181 EVENT_SUBSCRIPTION_AUGMENT_REVISION = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
182 NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
183 } catch (final ParseException e) {
184 throw new RestconfDocumentedException(
185 "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
186 ErrorTag.OPERATION_FAILED);
187 } catch (final URISyntaxException e) {
188 throw new RestconfDocumentedException(
189 "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION,
190 ErrorTag.OPERATION_FAILED);
194 public void setBroker(final BrokerFacade broker) {
195 this.broker = broker;
198 public void setControllerContext(final ControllerContext controllerContext) {
199 this.controllerContext = controllerContext;
202 private RestconfImpl() {
205 public static RestconfImpl getInstance() {
210 public NormalizedNodeContext getModules(final UriInfo uriInfo) {
211 final Set<Module> allModules = controllerContext.getAllModules();
212 final MapNode allModuleMap = makeModuleMapNode(allModules);
214 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
216 final Module restconfModule = getRestconfModule();
217 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
218 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
219 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
221 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
222 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
223 moduleContainerBuilder.withChild(allModuleMap);
225 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
226 null, schemaContext), moduleContainerBuilder.build());
230 * Valid only for mount point
233 public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
234 Preconditions.checkNotNull(identifier);
235 if ( ! identifier.contains(ControllerContext.MOUNT)) {
236 final String errMsg = "URI has bad format. If modules behind mount point should be showed,"
237 + " URI has to end with " + ControllerContext.MOUNT;
238 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
241 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
242 final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
243 final Set<Module> modules = controllerContext.getAllModules(mountPoint);
244 final SchemaContext schemaContext = mountPoint.getSchemaContext();
245 final MapNode mountPointModulesMap = makeModuleMapNode(modules);
247 final Module restconfModule = getRestconfModule();
248 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
249 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
250 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
252 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
253 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
254 moduleContainerBuilder.withChild(mountPointModulesMap);
256 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
257 mountPoint, schemaContext), moduleContainerBuilder.build());
261 public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
262 Preconditions.checkNotNull(identifier);
263 final QName moduleNameAndRevision = getModuleNameAndRevision(identifier);
264 Module module = null;
265 DOMMountPoint mountPoint = null;
266 final SchemaContext schemaContext;
267 if (identifier.contains(ControllerContext.MOUNT)) {
268 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
269 mountPoint = mountPointIdentifier.getMountPoint();
270 module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
271 schemaContext = mountPoint.getSchemaContext();
273 module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
274 schemaContext = controllerContext.getGlobalSchema();
277 if (module == null) {
278 final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName()
279 + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.";
280 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
283 final Module restconfModule = getRestconfModule();
284 final Set<Module> modules = Collections.singleton(module);
285 final MapNode moduleMap = makeModuleMapNode(modules);
287 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
288 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
289 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
291 return new NormalizedNodeContext(new InstanceIdentifierContext(null, moduleSchemaNode, mountPoint,
292 schemaContext), moduleMap);
296 public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
297 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
298 final Set<String> availableStreams = Notificator.getStreamNames();
299 final Module restconfModule = getRestconfModule();
300 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
301 Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
302 Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
304 final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
305 .mapBuilder((ListSchemaNode) streamSchemaNode);
307 for (final String streamName : availableStreams) {
308 listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
311 final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
312 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
313 Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
315 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
316 Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
317 streamsContainerBuilder.withChild(listStreamsBuilder.build());
320 return new NormalizedNodeContext(new InstanceIdentifierContext(null, streamsContainerSchemaNode, null,
321 schemaContext), streamsContainerBuilder.build());
325 public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
326 final Set<Module> allModules = controllerContext.getAllModules();
327 return operationsFromModulesToNormalizedContext(allModules, null);
331 public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
332 Set<Module> modules = null;
333 DOMMountPoint mountPoint = null;
334 if (identifier.contains(ControllerContext.MOUNT)) {
335 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
336 mountPoint = mountPointIdentifier.getMountPoint();
337 modules = controllerContext.getAllModules(mountPoint);
340 final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
341 throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
344 return operationsFromModulesToNormalizedContext(modules, mountPoint);
347 private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
348 final DOMMountPoint mountPoint) {
350 // FIXME find best way to change restconf-netconf yang schema for provide this functionality
351 final String errMsg = "We are not able support view operations functionality yet.";
352 throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
356 * @deprecated method will be removed in Lithium release
359 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
360 final DOMMountPoint mountPoint, final boolean prettyPrint) {
361 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
362 final Module restconfModule = getRestconfModule();
363 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
364 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
365 final QName qName = operationsSchemaNode.getQName();
366 final SchemaPath path = operationsSchemaNode.getPath();
367 final ContainerSchemaNodeBuilder containerSchemaNodeBuilder = new ContainerSchemaNodeBuilder(
368 Draft02.RestConfModule.NAME, 0, qName, path);
369 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
370 for (final Module module : modules) {
371 final Set<RpcDefinition> rpcs = module.getRpcs();
372 for (final RpcDefinition rpc : rpcs) {
373 final QName rpcQName = rpc.getQName();
374 final SimpleNode<Object> immutableSimpleNode = NodeFactory.<Object> createImmutableSimpleNode(rpcQName, null,
376 operationsAsData.add(immutableSimpleNode);
378 final String name = module.getName();
379 final LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
380 SchemaPath.create(true, QName.create("dummy")));
381 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
382 fakeRpcSchemaNode.setAugmenting(true);
384 final EmptyType instance = EmptyType.getInstance();
385 fakeRpcSchemaNode.setType(instance);
386 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
390 final CompositeNode operationsNode = NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
391 final ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
392 return new StructuredData(operationsNode, schemaNode, mountPoint, prettyPrint);
395 private Module getRestconfModule() {
396 final Module restconfModule = controllerContext.getRestconfModule();
397 if (restconfModule == null) {
398 throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION,
399 ErrorTag.OPERATION_NOT_SUPPORTED);
402 return restconfModule;
405 private QName getModuleNameAndRevision(final String identifier) {
406 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
407 String moduleNameAndRevision = "";
408 if (mountIndex >= 0) {
409 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
411 moduleNameAndRevision = identifier;
414 final Splitter splitter = Splitter.on("/").omitEmptyStrings();
415 final Iterable<String> split = splitter.split(moduleNameAndRevision);
416 final List<String> pathArgs = Lists.<String> newArrayList(split);
417 if (pathArgs.size() < 2) {
418 throw new RestconfDocumentedException(
419 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
420 ErrorTag.INVALID_VALUE);
424 final String moduleName = pathArgs.get(0);
425 final String revision = pathArgs.get(1);
426 final Date moduleRevision = REVISION_FORMAT.parse(revision);
427 return QName.create(null, moduleRevision, moduleName);
428 } catch (final ParseException e) {
429 throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
430 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
435 * @deprecated method will be removed for Lithium release
436 * so, please use toStreamEntryNode method
439 * @param streamSchemaNode
443 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
444 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
445 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
446 ((DataNodeContainer) streamSchemaNode), "name");
447 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
449 .add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName));
451 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
452 ((DataNodeContainer) streamSchemaNode), "description");
453 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
454 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
455 "DESCRIPTION_PLACEHOLDER"));
457 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
458 ((DataNodeContainer) streamSchemaNode), "replay-support");
459 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
460 streamNodeValues.add(NodeFactory.<Boolean> createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
461 Boolean.valueOf(true)));
463 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
464 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
465 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
466 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
469 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
470 ((DataNodeContainer) streamSchemaNode), "events");
471 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
472 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(eventsSchemaNode.getQName(), null, ""));
474 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
478 * @deprecated method will be removed for Lithium release
479 * so, please use toModuleEntryNode method
482 * @param moduleSchemaNode
486 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
487 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
488 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
489 ((DataNodeContainer) moduleSchemaNode), "name");
490 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
491 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null,
494 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
495 ((DataNodeContainer) moduleSchemaNode), "revision");
496 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
497 final Date _revision = module.getRevision();
498 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
499 REVISION_FORMAT.format(_revision)));
501 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
502 ((DataNodeContainer) moduleSchemaNode), "namespace");
503 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
504 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
505 module.getNamespace().toString()));
507 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
508 ((DataNodeContainer) moduleSchemaNode), "feature");
509 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
510 for (final FeatureDefinition feature : module.getFeatures()) {
511 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(featureSchemaNode.getQName(), null,
512 feature.getQName().getLocalName()));
515 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
519 public Object getRoot() {
524 public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
525 final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
526 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
527 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
528 final SchemaContext schemaContext;
529 if (identifier.contains(MOUNT_POINT_MODULE_NAME) && mountPoint != null) {
530 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
531 if ( ! mountRpcServices.isPresent()) {
532 throw new RestconfDocumentedException("Rpc service is missing.");
534 schemaContext = mountPoint.getSchemaContext();
535 response = mountRpcServices.get().invokeRpc(type, payload.getData());
537 response = broker.invokeRpc(type, payload.getData());
538 schemaContext = controllerContext.getGlobalSchema();
541 final DOMRpcResult result = checkRpcResponse(response);
543 DataSchemaNode resultNodeSchema = null;
544 NormalizedNode<?, ?> resultData = null;
545 if (result != null && result.getResult() != null) {
546 resultData = result.getResult();
547 final ContainerSchemaNode rpcDataSchemaNode = SchemaContextUtil.getRpcDataSchema(schemaContext, type);
548 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
551 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
552 schemaContext), resultData);
555 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
556 if (response == null) {
560 final DOMRpcResult retValue = response.get();
561 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
564 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
566 catch (final InterruptedException e) {
567 throw new RestconfDocumentedException(
568 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
569 ErrorTag.PARTIAL_OPERATION);
571 catch (final ExecutionException e) {
572 Throwable cause = e.getCause();
573 if (cause instanceof CancellationException) {
574 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
575 ErrorTag.PARTIAL_OPERATION);
576 } else if (cause != null) {
577 while (cause.getCause() != null) {
578 cause = cause.getCause();
581 if (cause instanceof IllegalArgumentException) {
582 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
583 ErrorTag.INVALID_VALUE);
586 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
589 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
595 private void validateInput(final DataSchemaNode inputSchema, final NormalizedNodeContext payload) {
596 if (inputSchema != null && payload.getData() == null) {
597 // expected a non null payload
598 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
599 } else if (inputSchema == null && payload.getData() != null) {
600 // did not expect any input
601 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
605 // TODO: Validate "mandatory" and "config" values here??? Or should those be
607 // validate in a more central location inside MD-SAL core.
612 * @deprecated method will be removed for Lithium release
618 private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
619 if (inputSchema != null && payload == null) {
620 // expected a non null payload
621 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
622 } else if (inputSchema == null && payload != null) {
623 // did not expect any input
624 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
628 // TODO: Validate "mandatory" and "config" values here??? Or should those be
630 // validate in a more central location inside MD-SAL core.
635 * @deprecated method wil be removed for Lithium release
638 private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload, final RpcDefinition rpc,
639 final boolean prettyPrint) {
640 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
641 final SimpleNode<? extends Object> pathNode = value == null ? null : value.getFirstSimpleByName(QName.create(
642 rpc.getQName(), "path"));
643 final Object pathValue = pathNode == null ? null : pathNode.getValue();
645 if (!(pathValue instanceof YangInstanceIdentifier)) {
646 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
647 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
650 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
651 String streamName = null;
652 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
653 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
655 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
656 DATASTORE_PARAM_NAME);
657 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
659 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
660 scope = scope == null ? DEFAULT_SCOPE : scope;
662 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
663 + "/scope=" + scope);
666 if (Strings.isNullOrEmpty(streamName)) {
667 throw new RestconfDocumentedException(
668 "Path is empty or contains data node which is not Container or List build-in type.",
669 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
672 final SimpleNode<String> streamNameNode = NodeFactory.<String> createImmutableSimpleNode(
673 QName.create(rpc.getOutput().getQName(), "stream-name"), null, streamName);
674 final List<Node<?>> output = new ArrayList<Node<?>>();
675 output.add(streamNameNode);
677 final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(rpc.getOutput().getQName(),
678 null, output, null, null);
680 if (!Notificator.existListenerFor(streamName)) {
681 Notificator.createListener(pathIdentifier, streamName);
684 return new StructuredData(responseData, rpc.getOutput(), null, prettyPrint);
688 public StructuredData invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
689 if (StringUtils.isNotBlank(noPayload)) {
690 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
692 final CompositeNode payload = null;
693 final RpcExecutor rpc = resolveIdentifierInInvokeRpc(identifier);
694 final QName rpcName = rpc.getRpcDefinition().getQName();
695 final URI rpcNamespace = rpcName.getNamespace();
696 if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE)
697 && Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
698 return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition(), parsePrettyPrintParameter(uriInfo));
701 validateInput(rpc.getRpcDefinition().getInput(), payload);
703 return callRpc(rpc, payload, parsePrettyPrintParameter(uriInfo));
706 private void resolveInvokeRpc(final String identifier, final DOMMountPoint mountPoint) {
710 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
711 String identifierEncoded = null;
712 DOMMountPoint mountPoint = null;
713 if (identifier.contains(ControllerContext.MOUNT)) {
714 // mounted RPC call - look up mount instance.
715 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
716 mountPoint = mountPointId.getMountPoint();
718 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
719 + ControllerContext.MOUNT.length() + 1;
720 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
721 identifierEncoded = remoteRpcName;
723 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
724 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
725 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
726 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
728 identifierEncoded = identifier;
731 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
733 RpcDefinition rpc = null;
734 if (mountPoint == null) {
735 rpc = controllerContext.getRpcDefinition(identifierDecoded);
737 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
741 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
744 if (mountPoint == null) {
745 return new BrokerRpcExecutor(rpc, broker);
747 return new MountPointRpcExecutor(rpc, mountPoint);
752 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
753 final String[] splittedIdentifier = identifierDecoded.split(":");
754 if (splittedIdentifier.length != 2) {
755 throw new RestconfDocumentedException(identifierDecoded
756 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
757 ErrorTag.INVALID_VALUE);
759 for (final Module module : schemaContext.getModules()) {
760 if (module.getName().equals(splittedIdentifier[0])) {
761 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
762 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
763 return rpcDefinition;
772 * @deprecated method will be removed for Lithium release
775 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, final boolean prettyPrint) {
776 if (rpcExecutor == null) {
777 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
780 CompositeNode rpcRequest = null;
781 final RpcDefinition rpc = rpcExecutor.getRpcDefinition();
782 final QName rpcName = rpc.getQName();
784 if (payload == null) {
785 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
787 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
788 final List<Node<?>> input = Collections.<Node<?>> singletonList(value);
789 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
792 final RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
794 checkRpcSuccessAndThrowException(rpcResult);
796 if (rpcResult.getResult() == null) {
800 if (rpc.getOutput() == null) {
801 return null; // no output, nothing to send back.
804 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null, prettyPrint);
807 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
808 if (rpcResult.isSuccessful() == false) {
810 throw new RestconfDocumentedException("The operation was not successful", null,
811 rpcResult.getErrors());
816 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
817 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
818 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
819 NormalizedNode<?, ?> data = null;
820 YangInstanceIdentifier normalizedII;
821 if (mountPoint != null) {
822 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
823 .getInstanceIdentifier());
824 data = broker.readConfigurationData(mountPoint, normalizedII);
826 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
827 data = broker.readConfigurationData(normalizedII);
829 return new NormalizedNodeContext(iiWithData, data);
832 @SuppressWarnings("unchecked")
833 private <T extends Node<?>> T pruneDataAtDepth(final T node, final Integer depth) {
838 if (node instanceof CompositeNode) {
839 final ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
841 for (final Node<?> childNode : ((CompositeNode) node).getValue()) {
842 newChildNodes.add(pruneDataAtDepth(childNode, depth - 1));
846 return (T) ImmutableCompositeNode.create(node.getNodeType(), newChildNodes.build());
847 } else { // SimpleNode
852 private Integer parseDepthParameter(final UriInfo info) {
853 final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
854 if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
859 final Integer depth = Integer.valueOf(param);
861 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
862 "Invalid depth parameter: " + depth, null,
863 "The depth parameter must be an integer > 1 or \"unbounded\""));
867 } catch (final NumberFormatException e) {
868 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
869 "Invalid depth parameter: " + e.getMessage(), null,
870 "The depth parameter must be an integer > 1 or \"unbounded\""));
875 public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) {
876 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
877 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
878 NormalizedNode<?, ?> data = null;
879 YangInstanceIdentifier normalizedII;
880 if (mountPoint != null) {
881 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
882 .getInstanceIdentifier());
883 data = broker.readOperationalData(mountPoint, normalizedII);
885 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
886 data = broker.readOperationalData(normalizedII);
889 return new NormalizedNodeContext(iiWithData, data);
892 private boolean parsePrettyPrintParameter(final UriInfo info) {
893 final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
894 return Boolean.parseBoolean(param);
898 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
899 Preconditions.checkNotNull(identifier);
900 final InstanceIdentifierContext<DataSchemaNode> iiWithData = controllerContext.toInstanceIdentifier(identifier);
902 validateInput(iiWithData.getSchemaNode(), payload);
903 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
904 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
906 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
907 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
910 * There is a small window where another write transaction could be updating the same data
911 * simultaneously and we get an OptimisticLockFailedException. This error is likely
912 * transient and The WriteTransaction#submit API docs state that a retry will likely
913 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
914 * probably will never succeed so we'll fail in that case.
916 * By retrying we're attempting to hide the internal implementation of the data store and
917 * how it handles concurrent updates from the restconf client. The client has instructed us
918 * to put the data and we should make every effort to do so without pushing optimistic lock
919 * failures back to the client and forcing them to handle it via retry (and having to
920 * document the behavior).
925 if (mountPoint != null) {
926 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
928 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
932 } catch (final TransactionCommitFailedException e) {
933 if(e instanceof OptimisticLockFailedException) {
935 LOG.debug("Got OptimisticLockFailedException on last try - failing");
936 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
939 LOG.debug("Got OptimisticLockFailedException - trying again");
941 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
946 return Response.status(Status.OK).build();
949 private void validateTopLevelNodeName(final NormalizedNodeContext node,
950 final YangInstanceIdentifier identifier) {
952 final String payloadName = node.getData().getNodeType().getLocalName();
953 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
956 if ( ! pathArguments.hasNext()) {
958 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
959 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
960 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
964 final String identifierName = pathArguments.next().getNodeType().getLocalName();
965 if ( ! payloadName.equals(identifierName)) {
966 throw new RestconfDocumentedException("Payload name (" + payloadName
967 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
968 ErrorTag.MALFORMED_MESSAGE);
974 * @deprecated method will be removed for Lithium release
980 private void validateTopLevelNodeName(final Node<?> node,
981 final YangInstanceIdentifier identifier) {
982 final String payloadName = getName(node);
983 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
986 if (!pathArguments.hasNext()) {
988 if (!node.getNodeType().equals(NETCONF_BASE_QNAME)) {
989 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
990 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
994 final String identifierName = pathArguments.next().getNodeType().getLocalName();
995 if (!payloadName.equals(identifierName)) {
996 throw new RestconfDocumentedException("Payload name (" + payloadName
997 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
998 ErrorTag.MALFORMED_MESSAGE);
1004 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1006 * @throws RestconfDocumentedException
1007 * if key values or key count in payload and URI isn't equal
1010 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1011 final NormalizedNode<?, ?> payload) {
1012 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1013 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1014 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1015 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1016 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1018 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1024 * @deprecated method will be removed for Lithium release
1026 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1028 * @throws RestconfDocumentedException
1029 * if key values or key count in payload and URI isn't equal
1033 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1034 final CompositeNode payload) {
1035 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1036 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1037 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1038 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1039 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1041 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1046 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
1047 final List<QName> keyDefinitions) {
1048 for (final QName keyDefinition : keyDefinitions) {
1049 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1050 // should be caught during parsing URI to InstanceIdentifier
1051 if (uriKeyValue == null) {
1052 final String errMsg = "Missing key " + keyDefinition + " in URI.";
1053 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1055 // TODO thing about the possibility to fix
1056 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1057 // if (payloadKeyValues.isEmpty()) {
1058 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
1059 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1062 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1063 // if (!uriKeyValue.equals(payloadKeyValue)) {
1064 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
1065 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
1066 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1072 * @deprecated method will be removed for Lithium release
1074 * @param uriKeyValues
1076 * @param keyDefinitions
1079 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
1080 final List<QName> keyDefinitions) {
1081 for (final QName keyDefinition : keyDefinitions) {
1082 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1083 // should be caught during parsing URI to InstanceIdentifier
1084 if (uriKeyValue == null) {
1085 throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.", ErrorType.PROTOCOL,
1086 ErrorTag.DATA_MISSING);
1088 final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1089 if (payloadKeyValues.isEmpty()) {
1090 throw new RestconfDocumentedException("Missing key " + keyDefinition.getLocalName()
1091 + " in the message body.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1094 final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1095 if (!uriKeyValue.equals(payloadKeyValue)) {
1096 throw new RestconfDocumentedException("The value '" + uriKeyValue + "' for key '"
1097 + keyDefinition.getLocalName() + "' specified in the URI doesn't match the value '"
1098 + payloadKeyValue + "' specified in the message body. ", ErrorType.PROTOCOL,
1099 ErrorTag.INVALID_VALUE);
1105 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
1106 if (payload == null) {
1107 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1110 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1111 if (payloadNS == null) {
1112 throw new RestconfDocumentedException(
1113 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1114 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1117 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1119 final InstanceIdentifierContext iiWithData = mountPoint != null
1120 ? controllerContext.toMountPointIdentifier(identifier)
1121 : controllerContext.toInstanceIdentifier(identifier);
1122 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1125 if (mountPoint != null) {
1126 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
1128 broker.commitConfigurationDataPost(normalizedII, payload.getData());
1130 } catch(final RestconfDocumentedException e) {
1132 } catch (final Exception e) {
1133 throw new RestconfDocumentedException("Error creating data", e);
1137 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1138 final URI location = resolveLocation(uriInfo, "config", mountPoint, normalizedII);
1139 if (location != null) {
1140 responseBuilder.location(location);
1142 return responseBuilder.build();
1145 // FIXME create RestconfIdetifierHelper and move this method there
1146 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
1147 Preconditions.checkArgument(payload != null);
1148 Preconditions.checkArgument(payload.getData() != null);
1149 Preconditions.checkArgument(payload.getData().getNodeType() != null);
1150 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
1151 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
1153 final QName payloadNodeQname = payload.getData().getNodeType();
1154 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
1155 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
1158 final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext();
1159 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
1160 if(parentSchemaNode instanceof DataNodeContainer) {
1161 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
1162 for (final DataSchemaNode child : cast.getChildNodes()) {
1163 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
1164 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
1168 if (parentSchemaNode instanceof RpcDefinition) {
1171 final String errMsg = "Error parsing input: DataSchemaNode has not children";
1172 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1176 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
1177 if (payload == null) {
1178 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1181 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1182 if (payloadNS == null) {
1183 throw new RestconfDocumentedException(
1184 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1185 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1188 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1189 final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext();
1190 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1193 if (mountPoint != null) {
1194 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
1197 broker.commitConfigurationDataPost(normalizedII, payload.getData());
1199 } catch(final RestconfDocumentedException e) {
1201 } catch (final Exception e) {
1202 throw new RestconfDocumentedException("Error creating data", e);
1205 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1206 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
1207 if (location != null) {
1208 responseBuilder.location(location);
1210 return responseBuilder.build();
1213 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
1214 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
1215 uriBuilder.path("config");
1217 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
1218 } catch (final Exception e) {
1219 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
1222 return uriBuilder.build();
1226 public Response deleteConfigurationData(final String identifier) {
1227 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
1228 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
1229 YangInstanceIdentifier normalizedII;
1232 if (mountPoint != null) {
1233 normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
1234 .getInstanceIdentifier());
1235 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
1237 normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
1238 broker.commitConfigurationDataDelete(normalizedII).get();
1240 } catch (final Exception e) {
1241 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
1242 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
1243 if (searchedException.isPresent()) {
1244 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
1246 throw new RestconfDocumentedException("Error while deleting data", e);
1248 return Response.status(Status.OK).build();
1252 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
1254 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
1256 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
1257 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
1261 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
1262 final String streamName = Notificator.createStreamNameFromUri(identifier);
1263 if (Strings.isNullOrEmpty(streamName)) {
1264 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1267 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
1268 if (listener == null) {
1269 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
1272 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
1273 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
1274 paramToValues.get(DATASTORE_PARAM_NAME));
1275 if (datastore == null) {
1276 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
1277 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1279 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
1280 if (scope == null) {
1281 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
1282 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1285 broker.registerToListenDataChanges(datastore, scope, listener);
1287 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
1288 int notificationPort = NOTIFICATION_PORT;
1290 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
1291 notificationPort = webSocketServerInstance.getPort();
1292 } catch (final NullPointerException e) {
1293 WebSocketServer.createInstance(NOTIFICATION_PORT);
1295 final UriBuilder port = uriBuilder.port(notificationPort);
1296 final URI uriToWebsocketServer = port.replacePath(streamName).build();
1298 return Response.status(Status.OK).location(uriToWebsocketServer).build();
1302 * Load parameter for subscribing to stream from input composite node
1306 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
1308 private <T> T parseEnumTypeParameter(final CompositeNode compNode, final Class<T> classDescriptor,
1309 final String paramName) {
1310 final QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
1311 EVENT_SUBSCRIPTION_AUGMENT_REVISION);
1312 final SimpleNode<?> simpleNode = compNode.getFirstSimpleByName(QName.create(salRemoteAugment, paramName));
1313 if (simpleNode == null) {
1316 final Object rawValue = simpleNode.getValue();
1317 if (!(rawValue instanceof String)) {
1321 return resolveAsEnum(classDescriptor, (String) rawValue);
1325 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
1327 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
1330 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
1331 if (Strings.isNullOrEmpty(value)) {
1334 return resolveAsEnum(classDescriptor, value);
1337 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
1338 final T[] enumConstants = classDescriptor.getEnumConstants();
1339 if (enumConstants != null) {
1340 for (final T enm : classDescriptor.getEnumConstants()) {
1341 if (((Enum<?>) enm).name().equals(value)) {
1349 private Map<String, String> resolveValuesFromUri(final String uri) {
1350 final Map<String, String> result = new HashMap<>();
1351 final String[] tokens = uri.split("/");
1352 for (int i = 1; i < tokens.length; i++) {
1353 final String[] parameterTokens = tokens[i].split("=");
1354 if (parameterTokens.length == 2) {
1355 result.put(parameterTokens[0], parameterTokens[1]);
1361 private Module findModule(final DOMMountPoint mountPoint, final Node<?> data) {
1362 Module module = null;
1363 if (data instanceof NodeWrapper) {
1364 module = findModule(mountPoint, (NodeWrapper<?>) data);
1365 } else if (data != null) {
1366 final URI namespace = data.getNodeType().getNamespace();
1367 if (mountPoint != null) {
1368 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1370 module = controllerContext.findModuleByNamespace(namespace);
1373 if (module != null) {
1376 throw new RestconfDocumentedException(
1377 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
1378 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1381 private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper<?> data) {
1382 final URI namespace = data.getNamespace();
1383 Preconditions.<URI> checkNotNull(namespace);
1385 Module module = null;
1386 if (mountPoint != null) {
1387 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1388 if (module == null) {
1389 module = controllerContext.findModuleByName(mountPoint, namespace.toString());
1392 module = controllerContext.findModuleByNamespace(namespace);
1393 if (module == null) {
1394 module = controllerContext.findModuleByName(namespace.toString());
1401 private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode,
1402 final CompositeNode data, final DataSchemaNode schemaOfData, final SchemaContext schemaContext) {
1403 YangInstanceIdentifier instanceIdentifier = null;
1404 if (identifierWithSchemaNode != null) {
1405 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
1408 final YangInstanceIdentifier iiOriginal = instanceIdentifier;
1409 InstanceIdentifierBuilder iiBuilder = null;
1410 if (iiOriginal == null) {
1411 iiBuilder = YangInstanceIdentifier.builder();
1413 iiBuilder = YangInstanceIdentifier.builder(iiOriginal);
1416 if ((schemaOfData instanceof ListSchemaNode)) {
1417 final HashMap<QName, Object> keys = resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
1418 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
1420 iiBuilder.node(schemaOfData.getQName());
1423 final YangInstanceIdentifier instance = iiBuilder.toInstance();
1424 DOMMountPoint mountPoint = null;
1425 final SchemaContext schemaCtx = null;
1426 if (identifierWithSchemaNode != null) {
1427 mountPoint = identifierWithSchemaNode.getMountPoint();
1430 return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext);
1433 private HashMap<QName, Object> resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) {
1434 final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
1435 final List<QName> _keyDefinition = listNode.getKeyDefinition();
1436 for (final QName key : _keyDefinition) {
1437 SimpleNode<? extends Object> head = null;
1438 final String localName = key.getLocalName();
1439 final List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
1440 if (simpleNodesByName != null) {
1441 head = Iterables.getFirst(simpleNodesByName, null);
1444 Object dataNodeKeyValueObject = null;
1446 dataNodeKeyValueObject = head.getValue();
1449 if (dataNodeKeyValueObject == null) {
1450 throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName()
1451 + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL,
1452 ErrorTag.INVALID_VALUE);
1455 keyValues.put(key, dataNodeKeyValueObject);
1461 private boolean endsWithMountPoint(final String identifier) {
1462 return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
1465 private boolean representsMountPointRootData(final Node<?> data) {
1466 final URI namespace = namespace(data);
1467 return (SchemaContext.NAME.getNamespace().equals(namespace) /*
1468 * || MOUNT_POINT_MODULE_NAME .equals( namespace .
1471 && SchemaContext.NAME.getLocalName().equals(localName(data));
1474 private String addMountPointIdentifier(final String identifier) {
1475 final boolean endsWith = identifier.endsWith("/");
1477 return (identifier + ControllerContext.MOUNT);
1480 return identifier + "/" + ControllerContext.MOUNT;
1484 * @deprecated method will be removed in Lithium release
1485 * we don't wish to use Node and CompositeNode anywhere
1488 public CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
1489 if (schema == null) {
1490 final String localName = node == null ? null :
1491 node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
1492 node.getNodeType().getLocalName();
1494 throw new RestconfDocumentedException("Data schema node was not found for " + localName,
1495 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1498 if (!(schema instanceof DataNodeContainer)) {
1499 throw new RestconfDocumentedException("Root element has to be container or list yang datatype.",
1500 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1503 if ((node instanceof NodeWrapper<?>)) {
1504 NodeWrapper<?> nodeWrap = (NodeWrapper<?>) node;
1505 final boolean isChangeAllowed = ((NodeWrapper<?>) node).isChangeAllowed();
1506 if (isChangeAllowed) {
1507 nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper<?>) node, schema);
1509 this.normalizeNode(nodeWrap, schema, null, mountPoint);
1510 } catch (final IllegalArgumentException e) {
1511 final RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1512 restconfDocumentedException.addSuppressed(e);
1513 throw restconfDocumentedException;
1515 if (nodeWrap instanceof CompositeNodeWrapper) {
1516 return ((CompositeNodeWrapper) nodeWrap).unwrap();
1521 if (node instanceof CompositeNode) {
1522 return (CompositeNode) node;
1525 throw new RestconfDocumentedException("Top level element is not interpreted as composite node.",
1526 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
1529 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1530 final QName previousAugment, final DOMMountPoint mountPoint) {
1531 if (schema == null) {
1532 throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName()
1533 + "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1536 QName currentAugment = null;
1537 if (nodeBuilder.getQname() != null) {
1538 currentAugment = previousAugment;
1540 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1541 if (nodeBuilder.getQname() == null) {
1542 throw new RestconfDocumentedException(
1543 "Data has bad format.\nIf data is in XML format then namespace for \""
1544 + nodeBuilder.getLocalName() + "\" should be \"" + schema.getQName().getNamespace()
1545 + "\".\n" + "If data is in JSON format then module name for \""
1546 + nodeBuilder.getLocalName() + "\" should be corresponding to namespace \""
1547 + schema.getQName().getNamespace() + "\".", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1551 if (nodeBuilder instanceof CompositeNodeWrapper) {
1552 if (schema instanceof DataNodeContainer) {
1553 normalizeCompositeNode((CompositeNodeWrapper) nodeBuilder, (DataNodeContainer) schema, mountPoint,
1555 } else if (schema instanceof AnyXmlSchemaNode) {
1556 normalizeAnyXmlNode((CompositeNodeWrapper) nodeBuilder, (AnyXmlSchemaNode) schema);
1558 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
1559 normalizeSimpleNode((SimpleNodeWrapper) nodeBuilder, schema, mountPoint);
1560 } else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1561 normalizeEmptyNode((EmptyNodeWrapper) nodeBuilder, schema);
1565 private void normalizeAnyXmlNode(final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema) {
1566 final List<NodeWrapper<?>> children = compositeNode.getValues();
1567 for (final NodeWrapper<? extends Object> child : children) {
1568 child.setNamespace(schema.getQName().getNamespace());
1569 if (child instanceof CompositeNodeWrapper) {
1570 normalizeAnyXmlNode((CompositeNodeWrapper) child, schema);
1575 private void normalizeEmptyNode(final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema) {
1576 if ((schema instanceof LeafSchemaNode)) {
1577 emptyNodeBuilder.setComposite(false);
1579 if ((schema instanceof ContainerSchemaNode)) {
1580 // FIXME: Add presence check
1581 emptyNodeBuilder.setComposite(true);
1586 private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1587 final DOMMountPoint mountPoint) {
1588 final Object value = simpleNode.getValue();
1589 Object inputValue = value;
1590 final TypeDef typeDef = this.typeDefinition(schema);
1591 TypeDefinition<? extends Object> typeDefinition = typeDef != null ? typeDef.typedef : null;
1593 // For leafrefs, extract the type it is pointing to
1594 if(typeDefinition instanceof LeafrefTypeDefinition) {
1595 if (schema.getQName().equals(typeDef.qName)) {
1596 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema);
1598 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName);
1602 if (typeDefinition instanceof IdentityrefTypeDefinition) {
1603 inputValue = parseToIdentityValuesDTO(simpleNode, value, inputValue);
1606 Object outputValue = inputValue;
1608 if (typeDefinition != null) {
1609 final Codec<Object, Object> codec = RestCodec.from(typeDefinition, mountPoint);
1610 outputValue = codec == null ? null : codec.deserialize(inputValue);
1613 simpleNode.setValue(outputValue);
1616 private Object parseToIdentityValuesDTO(final SimpleNodeWrapper simpleNode, final Object value, Object inputValue) {
1617 if ((value instanceof String)) {
1618 inputValue = new IdentityValuesDTO(simpleNode.getNamespace().toString(), (String) value, null,
1620 } // else value is already instance of IdentityValuesDTO
1624 private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
1625 final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
1626 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1627 checkNodeMultiplicityAccordingToSchema(schema, children);
1628 for (final NodeWrapper<? extends Object> child : children) {
1629 final List<DataSchemaNode> potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName(
1630 schema, child.getLocalName());
1632 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1633 final StringBuilder builder = new StringBuilder();
1634 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1635 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()).append("\n");
1638 throw new RestconfDocumentedException("Node \"" + child.getLocalName()
1639 + "\" is added as augment from more than one module. "
1640 + "Therefore node must have namespace (XML format) or module name (JSON format)."
1641 + "\nThe node is added as augment from modules with namespaces:\n" + builder,
1642 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1645 boolean rightNodeSchemaFound = false;
1646 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1647 if (!rightNodeSchemaFound) {
1648 final QName potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode,
1649 currentAugment, mountPoint);
1650 if (child.getQname() != null) {
1651 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1652 rightNodeSchemaFound = true;
1657 if (!rightNodeSchemaFound) {
1658 throw new RestconfDocumentedException("Schema node \"" + child.getLocalName()
1659 + "\" was not found in module.", ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
1663 if ((schema instanceof ListSchemaNode)) {
1664 final ListSchemaNode listSchemaNode = (ListSchemaNode) schema;
1665 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1666 for (final QName listKey : listKeys) {
1667 boolean foundKey = false;
1668 for (final NodeWrapper<? extends Object> child : children) {
1669 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1675 throw new RestconfDocumentedException("Missing key in URI \"" + listKey.getLocalName()
1676 + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", ErrorType.PROTOCOL,
1677 ErrorTag.DATA_MISSING);
1683 private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
1684 final List<NodeWrapper<?>> nodes) {
1685 final Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
1686 for (final NodeWrapper<?> child : nodes) {
1687 Integer count = equalNodeNamesToCounts.get(child.getLocalName());
1688 equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
1691 for (final DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
1692 if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
1693 final String localName = childSchemaNode.getQName().getLocalName();
1694 final Integer count = equalNodeNamesToCounts.get(localName);
1695 if (count != null && count > 1) {
1696 throw new RestconfDocumentedException("Multiple input data elements were specified for '"
1697 + childSchemaNode.getQName().getLocalName()
1698 + "'. The data for this element type can only be specified once.", ErrorType.APPLICATION,
1699 ErrorTag.BAD_ELEMENT);
1705 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1706 final QName previousAugment, final DOMMountPoint mountPoint) {
1707 QName validQName = schema.getQName();
1708 QName currentAugment = previousAugment;
1709 if (schema.isAugmenting()) {
1710 currentAugment = schema.getQName();
1711 } else if (previousAugment != null
1712 && !Objects.equal(schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1713 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1716 String moduleName = null;
1717 if (mountPoint == null) {
1718 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1720 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1723 if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace())
1724 || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) {
1726 * || Note : this check is wrong -
1727 * can never be true as it compares
1728 * a URI with a String not sure what
1729 * the intention is so commented out
1730 * ... Objects . equal ( nodeBuilder
1731 * . getNamespace ( ) ,
1732 * MOUNT_POINT_MODULE_NAME )
1735 nodeBuilder.setQname(validQName);
1738 return currentAugment;
1741 private URI namespace(final Node<?> data) {
1742 if (data instanceof NodeWrapper) {
1743 return ((NodeWrapper<?>) data).getNamespace();
1744 } else if (data != null) {
1745 return data.getNodeType().getNamespace();
1747 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1751 private String localName(final Node<?> data) {
1752 if (data instanceof NodeWrapper) {
1753 return ((NodeWrapper<?>) data).getLocalName();
1754 } else if (data != null) {
1755 return data.getNodeType().getLocalName();
1757 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1762 * @deprecated method will be removed for Lithium release
1768 private String getName(final Node<?> data) {
1769 if (data instanceof NodeWrapper) {
1770 return ((NodeWrapper<?>) data).getLocalName();
1771 } else if (data != null) {
1772 return data.getNodeType().getLocalName();
1774 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1778 private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
1779 TypeDefinition<?> baseType = type;
1780 QName qName = nodeQName;
1781 while (baseType.getBaseType() != null) {
1782 if (baseType instanceof ExtendedType) {
1783 qName = baseType.getQName();
1785 baseType = baseType.getBaseType();
1788 return new TypeDef(baseType, qName);
1792 private TypeDef typeDefinition(final DataSchemaNode node) {
1793 if (node instanceof LeafListSchemaNode) {
1794 return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
1795 } else if (node instanceof LeafSchemaNode) {
1796 return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
1797 } else if (node instanceof AnyXmlSchemaNode) {
1800 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
1804 private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode<?, ?> dataNode, final DataSchemaNode schema) {
1805 Node<?> nodes = null;
1806 if (dataNode == null) {
1807 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING,
1808 "No data was found."));
1810 nodes = DataNormalizer.toLegacy(dataNode);
1811 if (nodes != null) {
1812 if (nodes instanceof CompositeNode) {
1813 return (CompositeNode) nodes;
1815 LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode.");
1818 LOG.error("Top level node isn't of type Container or List schema node but "
1819 + schema.getClass().getSimpleName());
1822 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1823 "It wasn't possible to correctly interpret data."));
1826 private NormalizedNode<?, ?> compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode,
1827 final DataSchemaNode schema) {
1828 final List<Node<?>> lst = new ArrayList<Node<?>>();
1830 if (schema instanceof ContainerSchemaNode) {
1831 return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
1832 .parse(lst, (ContainerSchemaNode) schema);
1833 } else if (schema instanceof ListSchemaNode) {
1834 return CnSnToNormalizedNodeParserFactory.getInstance().getMapEntryNodeParser()
1835 .parse(lst, (ListSchemaNode) schema);
1838 LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
1840 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1841 "It wasn't possible to translate specified data to datastore readable form."));
1844 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1845 final InstanceIdentifierContext iiWithSchemaNode) {
1846 return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
1849 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1850 final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) {
1851 return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode(
1852 iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
1853 iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext());
1856 private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(
1857 final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) {
1858 Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
1859 final List<PathArgument> result = new ArrayList<PathArgument>();
1860 final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
1861 while (iter.hasNext()) {
1862 final PathArgument pathArgument = iter.next();
1863 if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
1864 result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
1866 result.add(pathArgument);
1868 return YangInstanceIdentifier.create(result);
1871 private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper<?> node,
1872 final DataSchemaNode schemaNode) {
1873 if (node instanceof CompositeNodeWrapper) {
1874 return (CompositeNodeWrapper) node;
1875 } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) {
1876 final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node;
1877 return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper));
1880 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1881 "Top level element has to be composite node or has to represent data container node."));
1884 private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
1885 if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
1891 public BigInteger getOperationalReceived() {
1892 // TODO Auto-generated method stub
1896 private MapNode makeModuleMapNode(final Set<Module> modules) {
1897 Preconditions.checkNotNull(modules);
1898 final Module restconfModule = getRestconfModule();
1899 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1900 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1901 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1903 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1904 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1906 for (final Module module : modules) {
1907 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1909 return listModuleBuilder.build();
1912 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1913 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1914 "moduleSchemaNode has to be of type ListSchemaNode");
\r final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
\r final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
\r .mapEntryBuilder(listModuleSchemaNode);
\r\r List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
\r (listModuleSchemaNode), "name");
\r final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
\r Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
\r moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
\r .build());
\r\r instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
\r (listModuleSchemaNode), "revision");
\r final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
\r Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
\r final String revision = REVISION_FORMAT.format(module.getRevision());
\r moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
\r .build());
\r\r instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
\r (listModuleSchemaNode), "namespace");
\r final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
\r Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
\r moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
\r .withValue(module.getNamespace().toString()).build());
\r\r instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
\r (listModuleSchemaNode), "feature");
\r final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
\r Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
\r final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
\r .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
\r for (final FeatureDefinition feature : module.getFeatures()) {
\r featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
\r .withValue(feature.getQName().getLocalName()).build());
\r }
\r moduleNodeValues.withChild(featuresBuilder.build());
1915 \r return moduleNodeValues.build();
\r }
1917 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
1918 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
1919 "streamSchemaNode has to be of type ListSchemaNode");
1920 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
1921 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
1922 .mapEntryBuilder(listStreamSchemaNode);
1924 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1925 (listStreamSchemaNode), "name");
1926 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1927 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1928 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
1931 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1932 (listStreamSchemaNode), "description");
1933 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1934 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
1935 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
1936 .withValue("DESCRIPTION_PLACEHOLDER").build());
1938 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1939 (listStreamSchemaNode), "replay-support");
1940 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1941 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
1942 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
1943 .withValue(Boolean.valueOf(true)).build());
1945 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1946 (listStreamSchemaNode), "replay-log-creation-time");
1947 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1948 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
1949 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
1950 .withValue("").build());
1952 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1953 (listStreamSchemaNode), "events");
1954 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1955 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
1956 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
1957 .withValue("").build());
1959 return streamNodeValues.build();