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 com.google.common.util.concurrent.Futures;
23 import java.math.BigInteger;
25 import java.net.URISyntaxException;
26 import java.text.ParseException;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
37 import java.util.concurrent.CancellationException;
38 import java.util.concurrent.ExecutionException;
39 import javax.ws.rs.core.Response;
40 import javax.ws.rs.core.Response.ResponseBuilder;
41 import javax.ws.rs.core.Response.Status;
42 import javax.ws.rs.core.UriBuilder;
43 import javax.ws.rs.core.UriInfo;
44 import org.apache.commons.lang3.StringUtils;
45 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
46 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
47 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
48 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
49 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
50 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
51 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
52 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
53 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
54 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
55 import org.opendaylight.controller.sal.rest.api.Draft02;
56 import org.opendaylight.controller.sal.rest.api.RestconfService;
57 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
58 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
59 import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
60 import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
61 import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
62 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
63 import org.opendaylight.controller.sal.streams.listeners.Notificator;
64 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
65 import org.opendaylight.yangtools.concepts.Codec;
66 import org.opendaylight.yangtools.yang.common.QName;
67 import org.opendaylight.yangtools.yang.common.QNameModule;
68 import org.opendaylight.yangtools.yang.common.RpcResult;
69 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
70 import org.opendaylight.yangtools.yang.data.api.Node;
71 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
72 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
73 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
74 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
75 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
76 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
77 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
78 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
79 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
80 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
81 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
82 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
83 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
84 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
85 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
86 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
87 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
88 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
89 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
90 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
91 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
92 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
93 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
94 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
95 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
96 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
97 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
98 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
99 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
100 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
101 import org.opendaylight.yangtools.yang.model.api.Module;
102 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
103 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
104 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
105 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
106 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
107 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
108 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
109 import org.opendaylight.yangtools.yang.model.util.EmptyType;
110 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
111 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
112 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
113 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
114 import org.slf4j.Logger;
115 import org.slf4j.LoggerFactory;
117 public class RestconfImpl implements RestconfService {
119 private enum UriParameters {
120 PRETTY_PRINT("prettyPrint"),
123 private String uriParameterName;
125 UriParameters(final String uriParameterName) {
126 this.uriParameterName = uriParameterName;
130 public String toString() {
131 return uriParameterName;
135 private static class TypeDef {
136 public final TypeDefinition<? extends Object> typedef;
137 public final QName qName;
139 TypeDef(final TypeDefinition<? extends Object> typedef, final QName qName) {
140 this.typedef = typedef;
145 private final static RestconfImpl INSTANCE = new RestconfImpl();
147 private static final int NOTIFICATION_PORT = 8181;
149 private static final int CHAR_NOT_FOUND = -1;
151 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
153 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
155 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
157 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
159 private BrokerFacade broker;
161 private ControllerContext controllerContext;
163 private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class);
165 private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
167 private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION;
169 private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
171 private static final Date EVENT_SUBSCRIPTION_AUGMENT_REVISION;
173 private static final String DATASTORE_PARAM_NAME = "datastore";
175 private static final String SCOPE_PARAM_NAME = "scope";
177 private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
179 private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
181 private static final QName NETCONF_BASE_QNAME;
185 EVENT_SUBSCRIPTION_AUGMENT_REVISION = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
186 NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
187 } catch (final ParseException e) {
188 throw new RestconfDocumentedException(
189 "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
190 ErrorTag.OPERATION_FAILED);
191 } catch (final URISyntaxException e) {
192 throw new RestconfDocumentedException(
193 "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION,
194 ErrorTag.OPERATION_FAILED);
198 public void setBroker(final BrokerFacade broker) {
199 this.broker = broker;
202 public void setControllerContext(final ControllerContext controllerContext) {
203 this.controllerContext = controllerContext;
206 private RestconfImpl() {
209 public static RestconfImpl getInstance() {
214 public NormalizedNodeContext getModules(final UriInfo uriInfo) {
215 final Set<Module> allModules = controllerContext.getAllModules();
216 final MapNode allModuleMap = makeModuleMapNode(allModules);
218 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
220 final Module restconfModule = getRestconfModule();
221 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
222 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
223 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
225 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
226 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
227 moduleContainerBuilder.withChild(allModuleMap);
229 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
230 null, schemaContext), moduleContainerBuilder.build());
234 * Valid only for mount point
237 public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
238 Preconditions.checkNotNull(identifier);
239 if ( ! identifier.contains(ControllerContext.MOUNT)) {
240 final String errMsg = "URI has bad format. If modules behind mount point should be showed,"
241 + " URI has to end with " + ControllerContext.MOUNT;
242 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
245 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
246 final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
247 final Set<Module> modules = controllerContext.getAllModules(mountPoint);
248 final SchemaContext schemaContext = mountPoint.getSchemaContext();
249 final MapNode mountPointModulesMap = makeModuleMapNode(modules);
251 final Module restconfModule = getRestconfModule();
252 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
253 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
254 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
256 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
257 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
258 moduleContainerBuilder.withChild(mountPointModulesMap);
260 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
261 mountPoint, schemaContext), moduleContainerBuilder.build());
265 public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
266 Preconditions.checkNotNull(identifier);
267 final QName moduleNameAndRevision = getModuleNameAndRevision(identifier);
268 Module module = null;
269 DOMMountPoint mountPoint = null;
270 final SchemaContext schemaContext;
271 if (identifier.contains(ControllerContext.MOUNT)) {
272 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
273 mountPoint = mountPointIdentifier.getMountPoint();
274 module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
275 schemaContext = mountPoint.getSchemaContext();
277 module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
278 schemaContext = controllerContext.getGlobalSchema();
281 if (module == null) {
282 final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName()
283 + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.";
284 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
287 final Module restconfModule = getRestconfModule();
288 final Set<Module> modules = Collections.singleton(module);
289 final MapNode moduleMap = makeModuleMapNode(modules);
291 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
292 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
293 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
295 return new NormalizedNodeContext(new InstanceIdentifierContext(null, moduleSchemaNode, mountPoint,
296 schemaContext), moduleMap);
300 public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
301 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
302 final Set<String> availableStreams = Notificator.getStreamNames();
303 final Module restconfModule = getRestconfModule();
304 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
305 Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
306 Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
308 final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
309 .mapBuilder((ListSchemaNode) streamSchemaNode);
311 for (final String streamName : availableStreams) {
312 listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
315 final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
316 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
317 Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
319 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
320 Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
321 streamsContainerBuilder.withChild(listStreamsBuilder.build());
324 return new NormalizedNodeContext(new InstanceIdentifierContext(null, streamsContainerSchemaNode, null,
325 schemaContext), streamsContainerBuilder.build());
329 public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
330 final Set<Module> allModules = controllerContext.getAllModules();
331 return operationsFromModulesToNormalizedContext(allModules, null);
335 public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
336 Set<Module> modules = null;
337 DOMMountPoint mountPoint = null;
338 if (identifier.contains(ControllerContext.MOUNT)) {
339 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
340 mountPoint = mountPointIdentifier.getMountPoint();
341 modules = controllerContext.getAllModules(mountPoint);
344 final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
345 throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
348 return operationsFromModulesToNormalizedContext(modules, mountPoint);
351 private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
352 final DOMMountPoint mountPoint) {
354 // FIXME find best way to change restconf-netconf yang schema for provide this functionality
355 final String errMsg = "We are not able support view operations functionality yet.";
356 throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
360 * @deprecated method will be removed in Lithium release
363 private StructuredData operationsFromModulesToStructuredData(final Set<Module> modules,
364 final DOMMountPoint mountPoint, final boolean prettyPrint) {
365 final List<Node<?>> operationsAsData = new ArrayList<Node<?>>();
366 final Module restconfModule = getRestconfModule();
367 final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
368 restconfModule, Draft02.RestConfModule.OPERATIONS_CONTAINER_SCHEMA_NODE);
369 final QName qName = operationsSchemaNode.getQName();
370 final SchemaPath path = operationsSchemaNode.getPath();
371 final ContainerSchemaNodeBuilder containerSchemaNodeBuilder = new ContainerSchemaNodeBuilder(
372 Draft02.RestConfModule.NAME, 0, qName, path);
373 final ContainerSchemaNodeBuilder fakeOperationsSchemaNode = containerSchemaNodeBuilder;
374 for (final Module module : modules) {
375 final Set<RpcDefinition> rpcs = module.getRpcs();
376 for (final RpcDefinition rpc : rpcs) {
377 final QName rpcQName = rpc.getQName();
378 final SimpleNode<Object> immutableSimpleNode = NodeFactory.<Object> createImmutableSimpleNode(rpcQName, null,
380 operationsAsData.add(immutableSimpleNode);
382 final String name = module.getName();
383 final LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
384 SchemaPath.create(true, QName.create("dummy")));
385 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
386 fakeRpcSchemaNode.setAugmenting(true);
388 final EmptyType instance = EmptyType.getInstance();
389 fakeRpcSchemaNode.setType(instance);
390 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build());
394 final CompositeNode operationsNode = NodeFactory.createImmutableCompositeNode(qName, null, operationsAsData);
395 final ContainerSchemaNode schemaNode = fakeOperationsSchemaNode.build();
396 return new StructuredData(operationsNode, schemaNode, mountPoint, prettyPrint);
399 private Module getRestconfModule() {
400 final Module restconfModule = controllerContext.getRestconfModule();
401 if (restconfModule == null) {
402 throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION,
403 ErrorTag.OPERATION_NOT_SUPPORTED);
406 return restconfModule;
409 private QName getModuleNameAndRevision(final String identifier) {
410 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
411 String moduleNameAndRevision = "";
412 if (mountIndex >= 0) {
413 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
415 moduleNameAndRevision = identifier;
418 final Splitter splitter = Splitter.on("/").omitEmptyStrings();
419 final Iterable<String> split = splitter.split(moduleNameAndRevision);
420 final List<String> pathArgs = Lists.<String> newArrayList(split);
421 if (pathArgs.size() < 2) {
422 throw new RestconfDocumentedException(
423 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
424 ErrorTag.INVALID_VALUE);
428 final String moduleName = pathArgs.get(0);
429 final String revision = pathArgs.get(1);
430 final Date moduleRevision = REVISION_FORMAT.parse(revision);
431 return QName.create(null, moduleRevision, moduleName);
432 } catch (final ParseException e) {
433 throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
434 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
439 * @deprecated method will be removed for Lithium release
440 * so, please use toStreamEntryNode method
443 * @param streamSchemaNode
447 private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) {
448 final List<Node<?>> streamNodeValues = new ArrayList<Node<?>>();
449 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
450 ((DataNodeContainer) streamSchemaNode), "name");
451 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
453 .add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName));
455 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
456 ((DataNodeContainer) streamSchemaNode), "description");
457 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
458 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(descriptionSchemaNode.getQName(), null,
459 "DESCRIPTION_PLACEHOLDER"));
461 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
462 ((DataNodeContainer) streamSchemaNode), "replay-support");
463 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
464 streamNodeValues.add(NodeFactory.<Boolean> createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null,
465 Boolean.valueOf(true)));
467 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
468 ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time");
469 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
470 streamNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(),
473 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
474 ((DataNodeContainer) streamSchemaNode), "events");
475 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
476 streamNodeValues.add(NodeFactory.<String>createImmutableSimpleNode(eventsSchemaNode.getQName(), null, ""));
478 return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues);
482 * @deprecated method will be removed for Lithium release
483 * so, please use toModuleEntryNode method
486 * @param moduleSchemaNode
490 private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) {
491 final List<Node<?>> moduleNodeValues = new ArrayList<Node<?>>();
492 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
493 ((DataNodeContainer) moduleSchemaNode), "name");
494 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
495 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(nameSchemaNode.getQName(), null,
498 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
499 ((DataNodeContainer) moduleSchemaNode), "revision");
500 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
501 final Date _revision = module.getRevision();
502 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(revisionSchemaNode.getQName(), null,
503 REVISION_FORMAT.format(_revision)));
505 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
506 ((DataNodeContainer) moduleSchemaNode), "namespace");
507 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
508 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(namespaceSchemaNode.getQName(), null,
509 module.getNamespace().toString()));
511 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
512 ((DataNodeContainer) moduleSchemaNode), "feature");
513 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
514 for (final FeatureDefinition feature : module.getFeatures()) {
515 moduleNodeValues.add(NodeFactory.<String> createImmutableSimpleNode(featureSchemaNode.getQName(), null,
516 feature.getQName().getLocalName()));
519 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.getQName(), null, moduleNodeValues);
523 public Object getRoot() {
528 public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
529 final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
530 final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
531 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
532 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
533 final SchemaContext schemaContext;
534 if (identifier.contains(MOUNT_POINT_MODULE_NAME) && mountPoint != null) {
535 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
536 if ( ! mountRpcServices.isPresent()) {
537 throw new RestconfDocumentedException("Rpc service is missing.");
539 schemaContext = mountPoint.getSchemaContext();
540 response = mountRpcServices.get().invokeRpc(type, payload.getData());
542 if (namespace.toString().equals(SAL_REMOTE_NAMESPACE)) {
543 response = invokeSalRemoteRpcSubscribeRPC(payload);
545 response = broker.invokeRpc(type, payload.getData());
547 schemaContext = controllerContext.getGlobalSchema();
550 final DOMRpcResult result = checkRpcResponse(response);
552 RpcDefinition resultNodeSchema = null;
553 final NormalizedNode<?, ?> resultData = result.getResult();
554 if (result != null && result.getResult() != null) {
555 resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
558 return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null,
559 resultNodeSchema, mountPoint, schemaContext), resultData);
562 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
563 if (response == null) {
567 final DOMRpcResult retValue = response.get();
568 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
571 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
573 catch (final InterruptedException e) {
574 throw new RestconfDocumentedException(
575 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
576 ErrorTag.PARTIAL_OPERATION);
578 catch (final ExecutionException e) {
579 Throwable cause = e.getCause();
580 if (cause instanceof CancellationException) {
581 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
582 ErrorTag.PARTIAL_OPERATION);
583 } else if (cause != null) {
584 while (cause.getCause() != null) {
585 cause = cause.getCause();
588 if (cause instanceof IllegalArgumentException) {
589 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
590 ErrorTag.INVALID_VALUE);
593 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
596 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
602 private void validateInput(final SchemaNode inputSchema, final NormalizedNodeContext payload) {
603 if (inputSchema != null && payload.getData() == null) {
604 // expected a non null payload
605 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
606 } else if (inputSchema == null && payload.getData() != null) {
607 // did not expect any input
608 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
612 // TODO: Validate "mandatory" and "config" values here??? Or should those be
614 // validate in a more central location inside MD-SAL core.
619 * @deprecated method will be removed for Lithium release
625 private void validateInput(final DataSchemaNode inputSchema, final Node<?> payload) {
626 if (inputSchema != null && payload == null) {
627 // expected a non null payload
628 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
629 } else if (inputSchema == null && payload != null) {
630 // did not expect any input
631 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
635 // TODO: Validate "mandatory" and "config" values here??? Or should those be
637 // validate in a more central location inside MD-SAL core.
641 private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
642 final ContainerNode value = (ContainerNode) payload.getData();
643 final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
644 final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
645 QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), "path")));
646 final Object pathValue = path.isPresent() ? path.get().getValue() : null;
648 if (!(pathValue instanceof YangInstanceIdentifier)) {
649 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
650 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
653 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
654 String streamName = null;
655 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
656 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
658 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
659 DATASTORE_PARAM_NAME);
660 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
662 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
663 scope = scope == null ? DEFAULT_SCOPE : scope;
665 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
666 + "/scope=" + scope);
669 if (Strings.isNullOrEmpty(streamName)) {
670 throw new RestconfDocumentedException(
671 "Path is empty or contains value node which is not Container or List build-in type.",
672 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
675 final QName outputQname = QName.create(rpcQName, "output");
676 final QName streamNameQname = QName.create(rpcQName, "stream-name");
678 final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
679 .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
681 if (!Notificator.existListenerFor(streamName)) {
682 final YangInstanceIdentifier normalizedPathIdentifier = controllerContext.toNormalized(pathIdentifier);
683 Notificator.createListener(normalizedPathIdentifier, streamName);
686 final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
688 return Futures.immediateCheckedFuture(defaultDOMRpcResult);
692 public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
693 if (StringUtils.isNotBlank(noPayload)) {
694 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
697 String identifierEncoded = null;
698 DOMMountPoint mountPoint = null;
699 final SchemaContext schemaContext;
700 if (identifier.contains(ControllerContext.MOUNT)) {
701 // mounted RPC call - look up mount instance.
702 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
703 mountPoint = mountPointId.getMountPoint();
704 schemaContext = mountPoint.getSchemaContext();
705 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
706 + ControllerContext.MOUNT.length() + 1;
707 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
708 identifierEncoded = remoteRpcName;
710 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
711 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
712 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
713 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
715 identifierEncoded = identifier;
716 schemaContext = controllerContext.getGlobalSchema();
719 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
721 RpcDefinition rpc = null;
722 if (mountPoint == null) {
723 rpc = controllerContext.getRpcDefinition(identifierDecoded);
725 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
729 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
732 if (rpc.getInput() != null) {
733 // FIXME : find a correct Error from specification
734 throw new IllegalStateException("RPC " + rpc + " does'n need input value!");
737 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
738 if (mountPoint != null) {
739 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
740 if ( ! mountRpcServices.isPresent()) {
741 throw new RestconfDocumentedException("Rpc service is missing.");
743 response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
745 response = broker.invokeRpc(rpc.getPath(), null);
748 final DOMRpcResult result = checkRpcResponse(response);
750 DataSchemaNode resultNodeSchema = null;
751 NormalizedNode<?, ?> resultData = null;
752 if (result != null && result.getResult() != null) {
753 resultData = result.getResult();
754 final ContainerSchemaNode rpcDataSchemaNode =
755 SchemaContextUtil.getRpcDataSchema(schemaContext, rpc.getOutput().getPath());
756 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
759 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
760 schemaContext), resultData);
763 private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
764 String identifierEncoded = null;
765 DOMMountPoint mountPoint = null;
766 if (identifier.contains(ControllerContext.MOUNT)) {
767 // mounted RPC call - look up mount instance.
768 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
769 mountPoint = mountPointId.getMountPoint();
771 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
772 + ControllerContext.MOUNT.length() + 1;
773 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
774 identifierEncoded = remoteRpcName;
776 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
777 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
778 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
779 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
781 identifierEncoded = identifier;
784 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
786 RpcDefinition rpc = null;
787 if (mountPoint == null) {
788 rpc = controllerContext.getRpcDefinition(identifierDecoded);
790 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
794 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
797 if (mountPoint == null) {
798 return new BrokerRpcExecutor(rpc, broker);
800 return new MountPointRpcExecutor(rpc, mountPoint);
805 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
806 final String[] splittedIdentifier = identifierDecoded.split(":");
807 if (splittedIdentifier.length != 2) {
808 throw new RestconfDocumentedException(identifierDecoded
809 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
810 ErrorTag.INVALID_VALUE);
812 for (final Module module : schemaContext.getModules()) {
813 if (module.getName().equals(splittedIdentifier[0])) {
814 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
815 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
816 return rpcDefinition;
825 * @deprecated method will be removed for Lithium release
828 private StructuredData callRpc(final RpcExecutor rpcExecutor, final CompositeNode payload, final boolean prettyPrint) {
829 if (rpcExecutor == null) {
830 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
833 CompositeNode rpcRequest = null;
834 final RpcDefinition rpc = rpcExecutor.getRpcDefinition();
835 final QName rpcName = rpc.getQName();
837 if (payload == null) {
838 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, null, null, null);
840 final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
841 final List<Node<?>> input = Collections.<Node<?>> singletonList(value);
842 rpcRequest = NodeFactory.createMutableCompositeNode(rpcName, null, input, null, null);
845 final RpcResult<CompositeNode> rpcResult = rpcExecutor.invokeRpc(rpcRequest);
847 checkRpcSuccessAndThrowException(rpcResult);
849 if (rpcResult.getResult() == null) {
853 if (rpc.getOutput() == null) {
854 return null; // no output, nothing to send back.
857 return new StructuredData(rpcResult.getResult(), rpc.getOutput(), null, prettyPrint);
860 private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
861 if (rpcResult.isSuccessful() == false) {
863 throw new RestconfDocumentedException("The operation was not successful", null,
864 rpcResult.getErrors());
869 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
870 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
871 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
872 NormalizedNode<?, ?> data = null;
873 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
874 if (mountPoint != null) {
875 data = broker.readConfigurationData(mountPoint, normalizedII);
877 data = broker.readConfigurationData(normalizedII);
880 throw new RestconfDocumentedException(
881 "Request could not be completed because the relevant data model content does not exist.",
882 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
884 return new NormalizedNodeContext(iiWithData, data);
887 @SuppressWarnings("unchecked")
888 private <T extends Node<?>> T pruneDataAtDepth(final T node, final Integer depth) {
893 if (node instanceof CompositeNode) {
894 final ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
896 for (final Node<?> childNode : ((CompositeNode) node).getValue()) {
897 newChildNodes.add(pruneDataAtDepth(childNode, depth - 1));
901 return (T) ImmutableCompositeNode.create(node.getNodeType(), newChildNodes.build());
902 } else { // SimpleNode
907 private Integer parseDepthParameter(final UriInfo info) {
908 final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
909 if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
914 final Integer depth = Integer.valueOf(param);
916 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
917 "Invalid depth parameter: " + depth, null,
918 "The depth parameter must be an integer > 1 or \"unbounded\""));
922 } catch (final NumberFormatException e) {
923 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
924 "Invalid depth parameter: " + e.getMessage(), null,
925 "The depth parameter must be an integer > 1 or \"unbounded\""));
930 public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) {
931 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
932 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
933 NormalizedNode<?, ?> data = null;
934 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
935 if (mountPoint != null) {
936 data = broker.readOperationalData(mountPoint, normalizedII);
938 data = broker.readOperationalData(normalizedII);
941 throw new RestconfDocumentedException(
942 "Request could not be completed because the relevant data model content does not exist.",
943 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
945 return new NormalizedNodeContext(iiWithData, data);
948 private boolean parsePrettyPrintParameter(final UriInfo info) {
949 final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
950 return Boolean.parseBoolean(param);
954 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
955 Preconditions.checkNotNull(identifier);
956 final InstanceIdentifierContext<DataSchemaNode> iiWithData =
957 (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
959 validateInput(iiWithData.getSchemaNode(), payload);
960 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
961 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
963 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
964 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
967 * There is a small window where another write transaction could be updating the same data
968 * simultaneously and we get an OptimisticLockFailedException. This error is likely
969 * transient and The WriteTransaction#submit API docs state that a retry will likely
970 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
971 * probably will never succeed so we'll fail in that case.
973 * By retrying we're attempting to hide the internal implementation of the data store and
974 * how it handles concurrent updates from the restconf client. The client has instructed us
975 * to put the data and we should make every effort to do so without pushing optimistic lock
976 * failures back to the client and forcing them to handle it via retry (and having to
977 * document the behavior).
982 if (mountPoint != null) {
983 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
985 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
989 } catch (final TransactionCommitFailedException e) {
990 if(e instanceof OptimisticLockFailedException) {
992 LOG.debug("Got OptimisticLockFailedException on last try - failing");
993 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
996 LOG.debug("Got OptimisticLockFailedException - trying again");
998 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
1003 return Response.status(Status.OK).build();
1006 private void validateTopLevelNodeName(final NormalizedNodeContext node,
1007 final YangInstanceIdentifier identifier) {
1009 final String payloadName = node.getData().getNodeType().getLocalName();
1010 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1013 if ( ! pathArguments.hasNext()) {
1015 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
1016 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1017 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1021 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1022 if ( ! payloadName.equals(identifierName)) {
1023 throw new RestconfDocumentedException("Payload name (" + payloadName
1024 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1025 ErrorTag.MALFORMED_MESSAGE);
1031 * @deprecated method will be removed for Lithium release
1037 private void validateTopLevelNodeName(final Node<?> node,
1038 final YangInstanceIdentifier identifier) {
1039 final String payloadName = getName(node);
1040 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
1043 if (!pathArguments.hasNext()) {
1045 if (!node.getNodeType().equals(NETCONF_BASE_QNAME)) {
1046 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
1047 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1051 final String identifierName = pathArguments.next().getNodeType().getLocalName();
1052 if (!payloadName.equals(identifierName)) {
1053 throw new RestconfDocumentedException("Payload name (" + payloadName
1054 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
1055 ErrorTag.MALFORMED_MESSAGE);
1061 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1063 * @throws RestconfDocumentedException
1064 * if key values or key count in payload and URI isn't equal
1067 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext<DataSchemaNode> iiWithData,
1068 final NormalizedNode<?, ?> payload) {
1069 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1070 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1071 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1072 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1073 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1075 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1081 * @deprecated method will be removed for Lithium release
1083 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
1085 * @throws RestconfDocumentedException
1086 * if key values or key count in payload and URI isn't equal
1090 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData,
1091 final CompositeNode payload) {
1092 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
1093 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
1094 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
1095 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
1096 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
1098 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
1103 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
1104 final List<QName> keyDefinitions) {
1105 for (final QName keyDefinition : keyDefinitions) {
1106 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1107 // should be caught during parsing URI to InstanceIdentifier
1108 if (uriKeyValue == null) {
1109 final String errMsg = "Missing key " + keyDefinition + " in URI.";
1110 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1112 // TODO thing about the possibility to fix
1113 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1114 // if (payloadKeyValues.isEmpty()) {
1115 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
1116 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1119 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1120 // if (!uriKeyValue.equals(payloadKeyValue)) {
1121 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
1122 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
1123 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1129 * @deprecated method will be removed for Lithium release
1131 * @param uriKeyValues
1133 * @param keyDefinitions
1136 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final CompositeNode payload,
1137 final List<QName> keyDefinitions) {
1138 for (final QName keyDefinition : keyDefinitions) {
1139 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
1140 // should be caught during parsing URI to InstanceIdentifier
1141 if (uriKeyValue == null) {
1142 throw new RestconfDocumentedException("Missing key " + keyDefinition + " in URI.", ErrorType.PROTOCOL,
1143 ErrorTag.DATA_MISSING);
1145 final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
1146 if (payloadKeyValues.isEmpty()) {
1147 throw new RestconfDocumentedException("Missing key " + keyDefinition.getLocalName()
1148 + " in the message body.", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
1151 final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
1152 if (!uriKeyValue.equals(payloadKeyValue)) {
1153 throw new RestconfDocumentedException("The value '" + uriKeyValue + "' for key '"
1154 + keyDefinition.getLocalName() + "' specified in the URI doesn't match the value '"
1155 + payloadKeyValue + "' specified in the message body. ", ErrorType.PROTOCOL,
1156 ErrorTag.INVALID_VALUE);
1162 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
1163 return createConfigurationData(payload, uriInfo);
1166 // FIXME create RestconfIdetifierHelper and move this method there
1167 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
1168 Preconditions.checkArgument(payload != null);
1169 Preconditions.checkArgument(payload.getData() != null);
1170 Preconditions.checkArgument(payload.getData().getNodeType() != null);
1171 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
1172 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
1174 final QName payloadNodeQname = payload.getData().getNodeType();
1175 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
1176 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
1179 final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext();
1180 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
1181 if(parentSchemaNode instanceof DataNodeContainer) {
1182 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
1183 for (final DataSchemaNode child : cast.getChildNodes()) {
1184 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
1185 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
1189 if (parentSchemaNode instanceof RpcDefinition) {
1192 final String errMsg = "Error parsing input: DataSchemaNode has not children";
1193 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1197 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
1198 if (payload == null) {
1199 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
1202 final URI payloadNS = payload.getData().getNodeType().getNamespace();
1203 if (payloadNS == null) {
1204 throw new RestconfDocumentedException(
1205 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
1206 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1209 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
1210 final InstanceIdentifierContext<DataSchemaNode> iiWithData = (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
1211 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1212 final YangInstanceIdentifier resultII;
1214 if (mountPoint != null) {
1215 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
1218 broker.commitConfigurationDataPost(normalizedII, payload.getData());
1220 } catch(final RestconfDocumentedException e) {
1222 } catch (final Exception e) {
1223 throw new RestconfDocumentedException("Error creating data", e);
1226 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
1227 // FIXME: Provide path to result.
1228 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
1229 if (location != null) {
1230 responseBuilder.location(location);
1232 return responseBuilder.build();
1235 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
1236 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
1237 uriBuilder.path("config");
1239 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
1240 } catch (final Exception e) {
1241 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
1244 return uriBuilder.build();
1248 public Response deleteConfigurationData(final String identifier) {
1249 final InstanceIdentifierContext<DataSchemaNode> iiWithData = controllerContext.toInstanceIdentifier(identifier);
1250 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
1251 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
1254 if (mountPoint != null) {
1255 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
1257 broker.commitConfigurationDataDelete(normalizedII).get();
1259 } catch (final Exception e) {
1260 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
1261 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
1262 if (searchedException.isPresent()) {
1263 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
1265 throw new RestconfDocumentedException("Error while deleting data", e);
1267 return Response.status(Status.OK).build();
1271 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
1273 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
1275 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
1276 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
1280 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
1281 final String streamName = Notificator.createStreamNameFromUri(identifier);
1282 if (Strings.isNullOrEmpty(streamName)) {
1283 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1286 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
1287 if (listener == null) {
1288 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
1291 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
1292 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
1293 paramToValues.get(DATASTORE_PARAM_NAME));
1294 if (datastore == null) {
1295 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
1296 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1298 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
1299 if (scope == null) {
1300 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
1301 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
1304 broker.registerToListenDataChanges(datastore, scope, listener);
1306 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
1307 int notificationPort = NOTIFICATION_PORT;
1309 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
1310 notificationPort = webSocketServerInstance.getPort();
1311 } catch (final NullPointerException e) {
1312 WebSocketServer.createInstance(NOTIFICATION_PORT);
1314 final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
1315 final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
1317 return Response.status(Status.OK).location(uriToWebsocketServer).build();
1321 * Load parameter for subscribing to stream from input composite node
1325 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
1327 private <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor,
1328 final String paramName) {
1329 final QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
1330 EVENT_SUBSCRIPTION_AUGMENT_REVISION);
1331 final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode = value.getChild(new NodeIdentifier(
1332 QName.create(salRemoteAugment, paramName)));
1333 if (!enumNode.isPresent()) {
1336 final Object rawValue = enumNode.get().getValue();
1337 if (!(rawValue instanceof String)) {
1341 return resolveAsEnum(classDescriptor, (String) rawValue);
1345 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
1347 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
1350 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
1351 if (Strings.isNullOrEmpty(value)) {
1354 return resolveAsEnum(classDescriptor, value);
1357 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
1358 final T[] enumConstants = classDescriptor.getEnumConstants();
1359 if (enumConstants != null) {
1360 for (final T enm : classDescriptor.getEnumConstants()) {
1361 if (((Enum<?>) enm).name().equals(value)) {
1369 private Map<String, String> resolveValuesFromUri(final String uri) {
1370 final Map<String, String> result = new HashMap<>();
1371 final String[] tokens = uri.split("/");
1372 for (int i = 1; i < tokens.length; i++) {
1373 final String[] parameterTokens = tokens[i].split("=");
1374 if (parameterTokens.length == 2) {
1375 result.put(parameterTokens[0], parameterTokens[1]);
1381 private Module findModule(final DOMMountPoint mountPoint, final Node<?> data) {
1382 Module module = null;
1383 if (data instanceof NodeWrapper) {
1384 module = findModule(mountPoint, (NodeWrapper<?>) data);
1385 } else if (data != null) {
1386 final URI namespace = data.getNodeType().getNamespace();
1387 if (mountPoint != null) {
1388 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1390 module = controllerContext.findModuleByNamespace(namespace);
1393 if (module != null) {
1396 throw new RestconfDocumentedException(
1397 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)",
1398 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
1401 private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper<?> data) {
1402 final URI namespace = data.getNamespace();
1403 Preconditions.<URI> checkNotNull(namespace);
1405 Module module = null;
1406 if (mountPoint != null) {
1407 module = controllerContext.findModuleByNamespace(mountPoint, namespace);
1408 if (module == null) {
1409 module = controllerContext.findModuleByName(mountPoint, namespace.toString());
1412 module = controllerContext.findModuleByNamespace(namespace);
1413 if (module == null) {
1414 module = controllerContext.findModuleByName(namespace.toString());
1421 private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode,
1422 final CompositeNode data, final DataSchemaNode schemaOfData, final SchemaContext schemaContext) {
1423 YangInstanceIdentifier instanceIdentifier = null;
1424 if (identifierWithSchemaNode != null) {
1425 instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier();
1428 final YangInstanceIdentifier iiOriginal = instanceIdentifier;
1429 InstanceIdentifierBuilder iiBuilder = null;
1430 if (iiOriginal == null) {
1431 iiBuilder = YangInstanceIdentifier.builder();
1433 iiBuilder = YangInstanceIdentifier.builder(iiOriginal);
1436 if ((schemaOfData instanceof ListSchemaNode)) {
1437 final HashMap<QName, Object> keys = resolveKeysFromData(((ListSchemaNode) schemaOfData), data);
1438 iiBuilder.nodeWithKey(schemaOfData.getQName(), keys);
1440 iiBuilder.node(schemaOfData.getQName());
1443 final YangInstanceIdentifier instance = iiBuilder.toInstance();
1444 DOMMountPoint mountPoint = null;
1445 final SchemaContext schemaCtx = null;
1446 if (identifierWithSchemaNode != null) {
1447 mountPoint = identifierWithSchemaNode.getMountPoint();
1450 return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext);
1453 private HashMap<QName, Object> resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) {
1454 final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
1455 final List<QName> _keyDefinition = listNode.getKeyDefinition();
1456 for (final QName key : _keyDefinition) {
1457 SimpleNode<? extends Object> head = null;
1458 final String localName = key.getLocalName();
1459 final List<SimpleNode<? extends Object>> simpleNodesByName = dataNode.getSimpleNodesByName(localName);
1460 if (simpleNodesByName != null) {
1461 head = Iterables.getFirst(simpleNodesByName, null);
1464 Object dataNodeKeyValueObject = null;
1466 dataNodeKeyValueObject = head.getValue();
1469 if (dataNodeKeyValueObject == null) {
1470 throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName()
1471 + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL,
1472 ErrorTag.INVALID_VALUE);
1475 keyValues.put(key, dataNodeKeyValueObject);
1481 private boolean endsWithMountPoint(final String identifier) {
1482 return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
1485 private boolean representsMountPointRootData(final Node<?> data) {
1486 final URI namespace = namespace(data);
1487 return (SchemaContext.NAME.getNamespace().equals(namespace) /*
1488 * || MOUNT_POINT_MODULE_NAME .equals( namespace .
1491 && SchemaContext.NAME.getLocalName().equals(localName(data));
1494 private String addMountPointIdentifier(final String identifier) {
1495 final boolean endsWith = identifier.endsWith("/");
1497 return (identifier + ControllerContext.MOUNT);
1500 return identifier + "/" + ControllerContext.MOUNT;
1504 * @deprecated method will be removed in Lithium release
1505 * we don't wish to use Node and CompositeNode anywhere
1508 public CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
1509 if (schema == null) {
1510 final String localName = node == null ? null :
1511 node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
1512 node.getNodeType().getLocalName();
1514 throw new RestconfDocumentedException("Data schema node was not found for " + localName,
1515 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1518 if (!(schema instanceof DataNodeContainer)) {
1519 throw new RestconfDocumentedException("Root element has to be container or list yang datatype.",
1520 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1523 if ((node instanceof NodeWrapper<?>)) {
1524 NodeWrapper<?> nodeWrap = (NodeWrapper<?>) node;
1525 final boolean isChangeAllowed = ((NodeWrapper<?>) node).isChangeAllowed();
1526 if (isChangeAllowed) {
1527 nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper<?>) node, schema);
1529 this.normalizeNode(nodeWrap, schema, null, mountPoint);
1530 } catch (final IllegalArgumentException e) {
1531 final RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1532 restconfDocumentedException.addSuppressed(e);
1533 throw restconfDocumentedException;
1535 if (nodeWrap instanceof CompositeNodeWrapper) {
1536 return ((CompositeNodeWrapper) nodeWrap).unwrap();
1541 if (node instanceof CompositeNode) {
1542 return (CompositeNode) node;
1545 throw new RestconfDocumentedException("Top level element is not interpreted as composite node.",
1546 ErrorType.APPLICATION, ErrorTag.INVALID_VALUE);
1549 private void normalizeNode(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1550 final QName previousAugment, final DOMMountPoint mountPoint) {
1551 if (schema == null) {
1552 throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName()
1553 + "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1556 QName currentAugment = null;
1557 if (nodeBuilder.getQname() != null) {
1558 currentAugment = previousAugment;
1560 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint);
1561 if (nodeBuilder.getQname() == null) {
1562 throw new RestconfDocumentedException(
1563 "Data has bad format.\nIf data is in XML format then namespace for \""
1564 + nodeBuilder.getLocalName() + "\" should be \"" + schema.getQName().getNamespace()
1565 + "\".\n" + "If data is in JSON format then module name for \""
1566 + nodeBuilder.getLocalName() + "\" should be corresponding to namespace \""
1567 + schema.getQName().getNamespace() + "\".", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1571 if (nodeBuilder instanceof CompositeNodeWrapper) {
1572 if (schema instanceof DataNodeContainer) {
1573 normalizeCompositeNode((CompositeNodeWrapper) nodeBuilder, (DataNodeContainer) schema, mountPoint,
1575 } else if (schema instanceof AnyXmlSchemaNode) {
1576 normalizeAnyXmlNode((CompositeNodeWrapper) nodeBuilder, (AnyXmlSchemaNode) schema);
1578 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
1579 normalizeSimpleNode((SimpleNodeWrapper) nodeBuilder, schema, mountPoint);
1580 } else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
1581 normalizeEmptyNode((EmptyNodeWrapper) nodeBuilder, schema);
1585 private void normalizeAnyXmlNode(final CompositeNodeWrapper compositeNode, final AnyXmlSchemaNode schema) {
1586 final List<NodeWrapper<?>> children = compositeNode.getValues();
1587 for (final NodeWrapper<? extends Object> child : children) {
1588 child.setNamespace(schema.getQName().getNamespace());
1589 if (child instanceof CompositeNodeWrapper) {
1590 normalizeAnyXmlNode((CompositeNodeWrapper) child, schema);
1595 private void normalizeEmptyNode(final EmptyNodeWrapper emptyNodeBuilder, final DataSchemaNode schema) {
1596 if ((schema instanceof LeafSchemaNode)) {
1597 emptyNodeBuilder.setComposite(false);
1599 if ((schema instanceof ContainerSchemaNode)) {
1600 // FIXME: Add presence check
1601 emptyNodeBuilder.setComposite(true);
1606 private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema,
1607 final DOMMountPoint mountPoint) {
1608 final Object value = simpleNode.getValue();
1609 Object inputValue = value;
1610 final TypeDef typeDef = this.typeDefinition(schema);
1611 TypeDefinition<? extends Object> typeDefinition = typeDef != null ? typeDef.typedef : null;
1613 // For leafrefs, extract the type it is pointing to
1614 if(typeDefinition instanceof LeafrefTypeDefinition) {
1615 if (schema.getQName().equals(typeDef.qName)) {
1616 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema);
1618 typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName);
1622 if (typeDefinition instanceof IdentityrefTypeDefinition) {
1623 inputValue = parseToIdentityValuesDTO(simpleNode, value, inputValue);
1626 Object outputValue = inputValue;
1628 if (typeDefinition != null) {
1629 final Codec<Object, Object> codec = RestCodec.from(typeDefinition, mountPoint);
1630 outputValue = codec == null ? null : codec.deserialize(inputValue);
1633 simpleNode.setValue(outputValue);
1636 private Object parseToIdentityValuesDTO(final SimpleNodeWrapper simpleNode, final Object value, Object inputValue) {
1637 if ((value instanceof String)) {
1638 inputValue = new IdentityValuesDTO(simpleNode.getNamespace().toString(), (String) value, null,
1640 } // else value is already instance of IdentityValuesDTO
1644 private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
1645 final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
1646 final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
1647 checkNodeMultiplicityAccordingToSchema(schema, children);
1648 for (final NodeWrapper<? extends Object> child : children) {
1649 final List<DataSchemaNode> potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName(
1650 schema, child.getLocalName());
1652 if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
1653 final StringBuilder builder = new StringBuilder();
1654 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1655 builder.append(" ").append(potentialSchemaNode.getQName().getNamespace().toString()).append("\n");
1658 throw new RestconfDocumentedException("Node \"" + child.getLocalName()
1659 + "\" is added as augment from more than one module. "
1660 + "Therefore node must have namespace (XML format) or module name (JSON format)."
1661 + "\nThe node is added as augment from modules with namespaces:\n" + builder,
1662 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
1665 boolean rightNodeSchemaFound = false;
1666 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
1667 if (!rightNodeSchemaFound) {
1668 final QName potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode,
1669 currentAugment, mountPoint);
1670 if (child.getQname() != null) {
1671 this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
1672 rightNodeSchemaFound = true;
1677 if (!rightNodeSchemaFound) {
1678 throw new RestconfDocumentedException("Schema node \"" + child.getLocalName()
1679 + "\" was not found in module.", ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT);
1683 if ((schema instanceof ListSchemaNode)) {
1684 final ListSchemaNode listSchemaNode = (ListSchemaNode) schema;
1685 final List<QName> listKeys = listSchemaNode.getKeyDefinition();
1686 for (final QName listKey : listKeys) {
1687 boolean foundKey = false;
1688 for (final NodeWrapper<? extends Object> child : children) {
1689 if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
1695 throw new RestconfDocumentedException("Missing key in URI \"" + listKey.getLocalName()
1696 + "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"", ErrorType.PROTOCOL,
1697 ErrorTag.DATA_MISSING);
1703 private void checkNodeMultiplicityAccordingToSchema(final DataNodeContainer dataNodeContainer,
1704 final List<NodeWrapper<?>> nodes) {
1705 final Map<String, Integer> equalNodeNamesToCounts = new HashMap<String, Integer>();
1706 for (final NodeWrapper<?> child : nodes) {
1707 Integer count = equalNodeNamesToCounts.get(child.getLocalName());
1708 equalNodeNamesToCounts.put(child.getLocalName(), count == null ? 1 : ++count);
1711 for (final DataSchemaNode childSchemaNode : dataNodeContainer.getChildNodes()) {
1712 if (childSchemaNode instanceof ContainerSchemaNode || childSchemaNode instanceof LeafSchemaNode) {
1713 final String localName = childSchemaNode.getQName().getLocalName();
1714 final Integer count = equalNodeNamesToCounts.get(localName);
1715 if (count != null && count > 1) {
1716 throw new RestconfDocumentedException("Multiple input data elements were specified for '"
1717 + childSchemaNode.getQName().getLocalName()
1718 + "'. The data for this element type can only be specified once.", ErrorType.APPLICATION,
1719 ErrorTag.BAD_ELEMENT);
1725 private QName normalizeNodeName(final NodeWrapper<? extends Object> nodeBuilder, final DataSchemaNode schema,
1726 final QName previousAugment, final DOMMountPoint mountPoint) {
1727 QName validQName = schema.getQName();
1728 QName currentAugment = previousAugment;
1729 if (schema.isAugmenting()) {
1730 currentAugment = schema.getQName();
1731 } else if (previousAugment != null
1732 && !Objects.equal(schema.getQName().getNamespace(), previousAugment.getNamespace())) {
1733 validQName = QName.create(currentAugment, schema.getQName().getLocalName());
1736 String moduleName = null;
1737 if (mountPoint == null) {
1738 moduleName = controllerContext.findModuleNameByNamespace(validQName.getNamespace());
1740 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.getNamespace());
1743 if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace())
1744 || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) {
1746 * || Note : this check is wrong -
1747 * can never be true as it compares
1748 * a URI with a String not sure what
1749 * the intention is so commented out
1750 * ... Objects . equal ( nodeBuilder
1751 * . getNamespace ( ) ,
1752 * MOUNT_POINT_MODULE_NAME )
1755 nodeBuilder.setQname(validQName);
1758 return currentAugment;
1761 private URI namespace(final Node<?> data) {
1762 if (data instanceof NodeWrapper) {
1763 return ((NodeWrapper<?>) data).getNamespace();
1764 } else if (data != null) {
1765 return data.getNodeType().getNamespace();
1767 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1771 private String localName(final Node<?> data) {
1772 if (data instanceof NodeWrapper) {
1773 return ((NodeWrapper<?>) data).getLocalName();
1774 } else if (data != null) {
1775 return data.getNodeType().getLocalName();
1777 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1782 * @deprecated method will be removed for Lithium release
1788 private String getName(final Node<?> data) {
1789 if (data instanceof NodeWrapper) {
1790 return ((NodeWrapper<?>) data).getLocalName();
1791 } else if (data != null) {
1792 return data.getNodeType().getLocalName();
1794 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(data).toString());
1798 private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
1799 TypeDefinition<?> baseType = type;
1800 QName qName = nodeQName;
1801 while (baseType.getBaseType() != null) {
1802 if (baseType instanceof ExtendedType) {
1803 qName = baseType.getQName();
1805 baseType = baseType.getBaseType();
1808 return new TypeDef(baseType, qName);
1812 private TypeDef typeDefinition(final DataSchemaNode node) {
1813 if (node instanceof LeafListSchemaNode) {
1814 return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
1815 } else if (node instanceof LeafSchemaNode) {
1816 return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
1817 } else if (node instanceof AnyXmlSchemaNode) {
1820 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
1824 private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode<?, ?> dataNode, final DataSchemaNode schema) {
1825 Node<?> nodes = null;
1826 if (dataNode == null) {
1827 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING,
1828 "No data was found."));
1830 nodes = DataNormalizer.toLegacy(dataNode);
1831 if (nodes != null) {
1832 if (nodes instanceof CompositeNode) {
1833 return (CompositeNode) nodes;
1835 LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode.");
1838 LOG.error("Top level node isn't of type Container or List schema node but "
1839 + schema.getClass().getSimpleName());
1842 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1843 "It wasn't possible to correctly interpret data."));
1846 private NormalizedNode<?, ?> compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode,
1847 final DataSchemaNode schema) {
1848 final List<Node<?>> lst = new ArrayList<Node<?>>();
1850 if (schema instanceof ContainerSchemaNode) {
1851 return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser()
1852 .parse(lst, (ContainerSchemaNode) schema);
1853 } else if (schema instanceof ListSchemaNode) {
1854 return CnSnToNormalizedNodeParserFactory.getInstance().getMapEntryNodeParser()
1855 .parse(lst, (ListSchemaNode) schema);
1858 LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName());
1860 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1861 "It wasn't possible to translate specified data to datastore readable form."));
1864 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1865 final InstanceIdentifierContext iiWithSchemaNode) {
1866 return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
1869 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1870 final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) {
1871 return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode(
1872 iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
1873 iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext());
1876 private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(
1877 final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) {
1878 Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
1879 final List<PathArgument> result = new ArrayList<PathArgument>();
1880 final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
1881 while (iter.hasNext()) {
1882 final PathArgument pathArgument = iter.next();
1883 if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
1884 result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
1886 result.add(pathArgument);
1888 return YangInstanceIdentifier.create(result);
1891 private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper<?> node,
1892 final DataSchemaNode schemaNode) {
1893 if (node instanceof CompositeNodeWrapper) {
1894 return (CompositeNodeWrapper) node;
1895 } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) {
1896 final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node;
1897 return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper));
1900 throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE,
1901 "Top level element has to be composite node or has to represent data container node."));
1904 private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
1905 if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
1911 public BigInteger getOperationalReceived() {
1912 // TODO Auto-generated method stub
1916 private MapNode makeModuleMapNode(final Set<Module> modules) {
1917 Preconditions.checkNotNull(modules);
1918 final Module restconfModule = getRestconfModule();
1919 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1920 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1921 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1923 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1924 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1926 for (final Module module : modules) {
1927 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1929 return listModuleBuilder.build();
1932 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1933 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1934 "moduleSchemaNode has to be of type ListSchemaNode");
1935 final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
1936 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
1937 .mapEntryBuilder(listModuleSchemaNode);
1939 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1940 (listModuleSchemaNode), "name");
1941 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1942 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1943 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
1946 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1947 (listModuleSchemaNode), "revision");
1948 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1949 Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
1950 final String revision = REVISION_FORMAT.format(module.getRevision());
1951 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
1954 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1955 (listModuleSchemaNode), "namespace");
1956 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1957 Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
1958 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
1959 .withValue(module.getNamespace().toString()).build());
1961 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1962 (listModuleSchemaNode), "feature");
1963 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1964 Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
1965 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
1966 .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
1967 for (final FeatureDefinition feature : module.getFeatures()) {
1968 featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
1969 .withValue(feature.getQName().getLocalName()).build());
1971 moduleNodeValues.withChild(featuresBuilder.build());
1973 return moduleNodeValues.build();
1976 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
1977 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
1978 "streamSchemaNode has to be of type ListSchemaNode");
1979 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
1980 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
1981 .mapEntryBuilder(listStreamSchemaNode);
1983 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1984 (listStreamSchemaNode), "name");
1985 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1986 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1987 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
1990 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1991 (listStreamSchemaNode), "description");
1992 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1993 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
1994 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
1995 .withValue("DESCRIPTION_PLACEHOLDER").build());
1997 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1998 (listStreamSchemaNode), "replay-support");
1999 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2000 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
2001 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
2002 .withValue(Boolean.valueOf(true)).build());
2004 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2005 (listStreamSchemaNode), "replay-log-creation-time");
2006 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2007 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
2008 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
2009 .withValue("").build());
2011 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
2012 (listStreamSchemaNode), "events");
2013 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
2014 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
2015 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
2016 .withValue("").build());
2018 return streamNodeValues.build();