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.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Predicates;
14 import com.google.common.base.Splitter;
15 import com.google.common.base.Strings;
16 import com.google.common.base.Throwables;
17 import com.google.common.collect.Iterables;
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Sets;
20 import com.google.common.util.concurrent.CheckedFuture;
21 import com.google.common.util.concurrent.Futures;
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.dom.api.DOMMountPoint;
49 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
50 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
51 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
52 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
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.streams.listeners.ListenerAdapter;
58 import org.opendaylight.controller.sal.streams.listeners.Notificator;
59 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
60 import org.opendaylight.yangtools.yang.common.QName;
61 import org.opendaylight.yangtools.yang.common.QNameModule;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
64 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
66 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
69 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
72 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
73 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
74 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
75 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
76 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
77 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
78 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
79 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
80 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
81 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
82 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
83 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
84 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
85 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
86 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
87 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
88 import org.opendaylight.yangtools.yang.model.api.Module;
89 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
90 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
91 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
92 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
93 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
94 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
95 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
96 import org.slf4j.Logger;
97 import org.slf4j.LoggerFactory;
99 public class RestconfImpl implements RestconfService {
101 private enum UriParameters {
102 PRETTY_PRINT("prettyPrint"),
105 private String uriParameterName;
107 UriParameters(final String uriParameterName) {
108 this.uriParameterName = uriParameterName;
112 public String toString() {
113 return uriParameterName;
117 private static class TypeDef {
118 public final TypeDefinition<? extends Object> typedef;
119 public final QName qName;
121 TypeDef(final TypeDefinition<? extends Object> typedef, final QName qName) {
122 this.typedef = typedef;
127 private final static RestconfImpl INSTANCE = new RestconfImpl();
129 private static final int NOTIFICATION_PORT = 8181;
131 private static final int CHAR_NOT_FOUND = -1;
133 private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
135 private final static SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
137 private final static String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
139 private final static String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
141 private BrokerFacade broker;
143 private ControllerContext controllerContext;
145 private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class);
147 private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
149 private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION;
151 private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
153 private static final String DATASTORE_PARAM_NAME = "datastore";
155 private static final String SCOPE_PARAM_NAME = "scope";
157 private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
159 private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
161 private static final QName NETCONF_BASE_QNAME;
163 private static final QNameModule SAL_REMOTE_AUGMENT;
165 private static final YangInstanceIdentifier.AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER;
169 final Date eventSubscriptionAugRevision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
170 NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
171 SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
172 eventSubscriptionAugRevision);
173 SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"),
174 QName.create(SAL_REMOTE_AUGMENT, "datastore")));
175 } catch (final ParseException e) {
176 throw new RestconfDocumentedException(
177 "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
178 ErrorTag.OPERATION_FAILED);
179 } catch (final URISyntaxException e) {
180 throw new RestconfDocumentedException(
181 "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION,
182 ErrorTag.OPERATION_FAILED);
186 public void setBroker(final BrokerFacade broker) {
187 this.broker = broker;
190 public void setControllerContext(final ControllerContext controllerContext) {
191 this.controllerContext = controllerContext;
194 private RestconfImpl() {
197 public static RestconfImpl getInstance() {
202 public NormalizedNodeContext getModules(final UriInfo uriInfo) {
203 final Set<Module> allModules = controllerContext.getAllModules();
204 final MapNode allModuleMap = makeModuleMapNode(allModules);
206 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
208 final Module restconfModule = getRestconfModule();
209 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
210 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
211 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
213 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
214 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
215 moduleContainerBuilder.withChild(allModuleMap);
217 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
218 null, schemaContext), moduleContainerBuilder.build());
222 * Valid only for mount point
225 public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
226 Preconditions.checkNotNull(identifier);
227 if ( ! identifier.contains(ControllerContext.MOUNT)) {
228 final String errMsg = "URI has bad format. If modules behind mount point should be showed,"
229 + " URI has to end with " + ControllerContext.MOUNT;
230 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
233 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
234 final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
235 final Set<Module> modules = controllerContext.getAllModules(mountPoint);
236 final SchemaContext schemaContext = mountPoint.getSchemaContext();
237 final MapNode mountPointModulesMap = makeModuleMapNode(modules);
239 final Module restconfModule = getRestconfModule();
240 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
241 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
242 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
244 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
245 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
246 moduleContainerBuilder.withChild(mountPointModulesMap);
248 return new NormalizedNodeContext(new InstanceIdentifierContext(null, modulesSchemaNode,
249 mountPoint, schemaContext), moduleContainerBuilder.build());
253 public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
254 Preconditions.checkNotNull(identifier);
255 final QName moduleNameAndRevision = getModuleNameAndRevision(identifier);
256 Module module = null;
257 DOMMountPoint mountPoint = null;
258 final SchemaContext schemaContext;
259 if (identifier.contains(ControllerContext.MOUNT)) {
260 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
261 mountPoint = mountPointIdentifier.getMountPoint();
262 module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
263 schemaContext = mountPoint.getSchemaContext();
265 module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
266 schemaContext = controllerContext.getGlobalSchema();
269 if (module == null) {
270 final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName()
271 + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.";
272 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
275 final Module restconfModule = getRestconfModule();
276 final Set<Module> modules = Collections.singleton(module);
277 final MapNode moduleMap = makeModuleMapNode(modules);
279 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
280 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
281 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
283 return new NormalizedNodeContext(new InstanceIdentifierContext(null, moduleSchemaNode, mountPoint,
284 schemaContext), moduleMap);
288 public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
289 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
290 final Set<String> availableStreams = Notificator.getStreamNames();
291 final Module restconfModule = getRestconfModule();
292 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
293 Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
294 Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
296 final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
297 .mapBuilder((ListSchemaNode) streamSchemaNode);
299 for (final String streamName : availableStreams) {
300 listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
303 final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
304 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
305 Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
307 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
308 Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
309 streamsContainerBuilder.withChild(listStreamsBuilder.build());
312 return new NormalizedNodeContext(new InstanceIdentifierContext(null, streamsContainerSchemaNode, null,
313 schemaContext), streamsContainerBuilder.build());
317 public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
318 final Set<Module> allModules = controllerContext.getAllModules();
319 return operationsFromModulesToNormalizedContext(allModules, null);
323 public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
324 Set<Module> modules = null;
325 DOMMountPoint mountPoint = null;
326 if (identifier.contains(ControllerContext.MOUNT)) {
327 final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
328 mountPoint = mountPointIdentifier.getMountPoint();
329 modules = controllerContext.getAllModules(mountPoint);
332 final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
333 throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
336 return operationsFromModulesToNormalizedContext(modules, mountPoint);
339 private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
340 final DOMMountPoint mountPoint) {
342 // FIXME find best way to change restconf-netconf yang schema for provide this functionality
343 final String errMsg = "We are not able support view operations functionality yet.";
344 throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
347 private Module getRestconfModule() {
348 final Module restconfModule = controllerContext.getRestconfModule();
349 if (restconfModule == null) {
350 throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION,
351 ErrorTag.OPERATION_NOT_SUPPORTED);
354 return restconfModule;
357 private QName getModuleNameAndRevision(final String identifier) {
358 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
359 String moduleNameAndRevision = "";
360 if (mountIndex >= 0) {
361 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
363 moduleNameAndRevision = identifier;
366 final Splitter splitter = Splitter.on("/").omitEmptyStrings();
367 final Iterable<String> split = splitter.split(moduleNameAndRevision);
368 final List<String> pathArgs = Lists.<String> newArrayList(split);
369 if (pathArgs.size() < 2) {
370 throw new RestconfDocumentedException(
371 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
372 ErrorTag.INVALID_VALUE);
376 final String moduleName = pathArgs.get(0);
377 final String revision = pathArgs.get(1);
378 final Date moduleRevision = REVISION_FORMAT.parse(revision);
379 return QName.create(null, moduleRevision, moduleName);
380 } catch (final ParseException e) {
381 throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
382 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
387 public Object getRoot() {
392 public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
393 final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
394 final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
395 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
396 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
397 final SchemaContext schemaContext;
398 if (identifier.contains(MOUNT_POINT_MODULE_NAME) && mountPoint != null) {
399 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
400 if ( ! mountRpcServices.isPresent()) {
401 throw new RestconfDocumentedException("Rpc service is missing.");
403 schemaContext = mountPoint.getSchemaContext();
404 response = mountRpcServices.get().invokeRpc(type, payload.getData());
406 if (namespace.toString().equals(SAL_REMOTE_NAMESPACE)) {
407 response = invokeSalRemoteRpcSubscribeRPC(payload);
409 response = broker.invokeRpc(type, payload.getData());
411 schemaContext = controllerContext.getGlobalSchema();
414 final DOMRpcResult result = checkRpcResponse(response);
416 RpcDefinition resultNodeSchema = null;
417 final NormalizedNode<?, ?> resultData = result.getResult();
418 if (result != null && result.getResult() != null) {
419 resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
422 return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null,
423 resultNodeSchema, mountPoint, schemaContext), resultData);
426 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
427 if (response == null) {
431 final DOMRpcResult retValue = response.get();
432 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
435 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
437 catch (final InterruptedException e) {
438 throw new RestconfDocumentedException(
439 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
440 ErrorTag.PARTIAL_OPERATION);
442 catch (final ExecutionException e) {
443 Throwable cause = e.getCause();
444 if (cause instanceof CancellationException) {
445 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
446 ErrorTag.PARTIAL_OPERATION);
447 } else if (cause != null) {
448 while (cause.getCause() != null) {
449 cause = cause.getCause();
452 if (cause instanceof IllegalArgumentException) {
453 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
454 ErrorTag.INVALID_VALUE);
457 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
460 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
466 private void validateInput(final SchemaNode inputSchema, final NormalizedNodeContext payload) {
467 if (inputSchema != null && payload.getData() == null) {
468 // expected a non null payload
469 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
470 } else if (inputSchema == null && payload.getData() != null) {
471 // did not expect any input
472 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
476 // TODO: Validate "mandatory" and "config" values here??? Or should those be
478 // validate in a more central location inside MD-SAL core.
482 private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
483 final ContainerNode value = (ContainerNode) payload.getData();
484 final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
485 final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
486 QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), "path")));
487 final Object pathValue = path.isPresent() ? path.get().getValue() : null;
489 if (!(pathValue instanceof YangInstanceIdentifier)) {
490 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
491 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
494 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
495 String streamName = null;
496 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
497 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
499 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
500 DATASTORE_PARAM_NAME);
501 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
503 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
504 scope = scope == null ? DEFAULT_SCOPE : scope;
506 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
507 + "/scope=" + scope);
510 if (Strings.isNullOrEmpty(streamName)) {
511 throw new RestconfDocumentedException(
512 "Path is empty or contains value node which is not Container or List build-in type.",
513 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
516 final QName outputQname = QName.create(rpcQName, "output");
517 final QName streamNameQname = QName.create(rpcQName, "stream-name");
519 final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
520 .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
522 if (!Notificator.existListenerFor(streamName)) {
523 final YangInstanceIdentifier normalizedPathIdentifier = controllerContext.toNormalized(pathIdentifier);
524 Notificator.createListener(normalizedPathIdentifier, streamName);
527 final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
529 return Futures.immediateCheckedFuture(defaultDOMRpcResult);
533 public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
534 if (StringUtils.isNotBlank(noPayload)) {
535 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
538 String identifierEncoded = null;
539 DOMMountPoint mountPoint = null;
540 final SchemaContext schemaContext;
541 if (identifier.contains(ControllerContext.MOUNT)) {
542 // mounted RPC call - look up mount instance.
543 final InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier);
544 mountPoint = mountPointId.getMountPoint();
545 schemaContext = mountPoint.getSchemaContext();
546 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
547 + ControllerContext.MOUNT.length() + 1;
548 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
549 identifierEncoded = remoteRpcName;
551 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
552 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
553 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
554 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
556 identifierEncoded = identifier;
557 schemaContext = controllerContext.getGlobalSchema();
560 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
562 RpcDefinition rpc = null;
563 if (mountPoint == null) {
564 rpc = controllerContext.getRpcDefinition(identifierDecoded);
566 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
570 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
573 if (rpc.getInput() != null) {
574 // FIXME : find a correct Error from specification
575 throw new IllegalStateException("RPC " + rpc + " does'n need input value!");
578 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
579 if (mountPoint != null) {
580 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
581 if ( ! mountRpcServices.isPresent()) {
582 throw new RestconfDocumentedException("Rpc service is missing.");
584 response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
586 response = broker.invokeRpc(rpc.getPath(), null);
589 final DOMRpcResult result = checkRpcResponse(response);
591 DataSchemaNode resultNodeSchema = null;
592 NormalizedNode<?, ?> resultData = null;
593 if (result != null && result.getResult() != null) {
594 resultData = result.getResult();
595 final ContainerSchemaNode rpcDataSchemaNode =
596 SchemaContextUtil.getRpcDataSchema(schemaContext, rpc.getOutput().getPath());
597 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
600 return new NormalizedNodeContext(new InstanceIdentifierContext(null, resultNodeSchema, mountPoint,
601 schemaContext), resultData);
604 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
605 final String[] splittedIdentifier = identifierDecoded.split(":");
606 if (splittedIdentifier.length != 2) {
607 throw new RestconfDocumentedException(identifierDecoded
608 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
609 ErrorTag.INVALID_VALUE);
611 for (final Module module : schemaContext.getModules()) {
612 if (module.getName().equals(splittedIdentifier[0])) {
613 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
614 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
615 return rpcDefinition;
624 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
625 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
626 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
627 NormalizedNode<?, ?> data = null;
628 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
629 if (mountPoint != null) {
630 data = broker.readConfigurationData(mountPoint, normalizedII);
632 data = broker.readConfigurationData(normalizedII);
635 throw new RestconfDocumentedException(
636 "Request could not be completed because the relevant data model content does not exist.",
637 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
639 return new NormalizedNodeContext(iiWithData, data);
642 private Integer parseDepthParameter(final UriInfo info) {
643 final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
644 if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
649 final Integer depth = Integer.valueOf(param);
651 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
652 "Invalid depth parameter: " + depth, null,
653 "The depth parameter must be an integer > 1 or \"unbounded\""));
657 } catch (final NumberFormatException e) {
658 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
659 "Invalid depth parameter: " + e.getMessage(), null,
660 "The depth parameter must be an integer > 1 or \"unbounded\""));
665 public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) {
666 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
667 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
668 NormalizedNode<?, ?> data = null;
669 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
670 if (mountPoint != null) {
671 data = broker.readOperationalData(mountPoint, normalizedII);
673 data = broker.readOperationalData(normalizedII);
676 throw new RestconfDocumentedException(
677 "Request could not be completed because the relevant data model content does not exist.",
678 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
680 return new NormalizedNodeContext(iiWithData, data);
683 private boolean parsePrettyPrintParameter(final UriInfo info) {
684 final String param = info.getQueryParameters(false).getFirst(UriParameters.PRETTY_PRINT.toString());
685 return Boolean.parseBoolean(param);
689 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
690 Preconditions.checkNotNull(identifier);
691 final InstanceIdentifierContext<DataSchemaNode> iiWithData =
692 (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
694 validateInput(iiWithData.getSchemaNode(), payload);
695 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
696 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
698 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
699 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
702 * There is a small window where another write transaction could be updating the same data
703 * simultaneously and we get an OptimisticLockFailedException. This error is likely
704 * transient and The WriteTransaction#submit API docs state that a retry will likely
705 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
706 * probably will never succeed so we'll fail in that case.
708 * By retrying we're attempting to hide the internal implementation of the data store and
709 * how it handles concurrent updates from the restconf client. The client has instructed us
710 * to put the data and we should make every effort to do so without pushing optimistic lock
711 * failures back to the client and forcing them to handle it via retry (and having to
712 * document the behavior).
717 if (mountPoint != null) {
718 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
720 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
724 } catch (final TransactionCommitFailedException e) {
725 if(e instanceof OptimisticLockFailedException) {
727 LOG.debug("Got OptimisticLockFailedException on last try - failing");
728 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
731 LOG.debug("Got OptimisticLockFailedException - trying again");
733 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
738 return Response.status(Status.OK).build();
741 private void validateTopLevelNodeName(final NormalizedNodeContext node,
742 final YangInstanceIdentifier identifier) {
744 final String payloadName = node.getData().getNodeType().getLocalName();
745 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
748 if ( ! pathArguments.hasNext()) {
750 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
751 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
752 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
756 final String identifierName = pathArguments.next().getNodeType().getLocalName();
757 if ( ! payloadName.equals(identifierName)) {
758 throw new RestconfDocumentedException("Payload name (" + payloadName
759 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
760 ErrorTag.MALFORMED_MESSAGE);
766 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
768 * @throws RestconfDocumentedException
769 * if key values or key count in payload and URI isn't equal
772 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext<DataSchemaNode> iiWithData,
773 final NormalizedNode<?, ?> payload) {
774 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
775 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
776 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
777 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
778 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
780 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
785 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
786 final List<QName> keyDefinitions) {
787 for (final QName keyDefinition : keyDefinitions) {
788 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
789 // should be caught during parsing URI to InstanceIdentifier
790 if (uriKeyValue == null) {
791 final String errMsg = "Missing key " + keyDefinition + " in URI.";
792 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
794 // TODO thing about the possibility to fix
795 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
796 // if (payloadKeyValues.isEmpty()) {
797 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
798 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
801 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
802 // if (!uriKeyValue.equals(payloadKeyValue)) {
803 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
804 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
805 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
811 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
812 return createConfigurationData(payload, uriInfo);
815 // FIXME create RestconfIdetifierHelper and move this method there
816 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
817 Preconditions.checkArgument(payload != null);
818 Preconditions.checkArgument(payload.getData() != null);
819 Preconditions.checkArgument(payload.getData().getNodeType() != null);
820 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
821 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
823 final QName payloadNodeQname = payload.getData().getNodeType();
824 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
825 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
828 final InstanceIdentifierContext parentContext = payload.getInstanceIdentifierContext();
829 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
830 if(parentSchemaNode instanceof DataNodeContainer) {
831 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
832 for (final DataSchemaNode child : cast.getChildNodes()) {
833 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
834 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
838 if (parentSchemaNode instanceof RpcDefinition) {
841 final String errMsg = "Error parsing input: DataSchemaNode has not children";
842 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
846 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
847 if (payload == null) {
848 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
851 final URI payloadNS = payload.getData().getNodeType().getNamespace();
852 if (payloadNS == null) {
853 throw new RestconfDocumentedException(
854 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
855 ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
858 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
859 final InstanceIdentifierContext<DataSchemaNode> iiWithData = (InstanceIdentifierContext<DataSchemaNode>) payload.getInstanceIdentifierContext();
860 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
861 final YangInstanceIdentifier resultII;
863 if (mountPoint != null) {
864 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()).checkedGet();
866 broker.commitConfigurationDataPost(normalizedII, payload.getData()).checkedGet();
868 } catch(final RestconfDocumentedException e) {
870 } catch (final Exception e) {
871 throw new RestconfDocumentedException("Error creating data", e);
874 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
875 // FIXME: Provide path to result.
876 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
877 if (location != null) {
878 responseBuilder.location(location);
880 return responseBuilder.build();
883 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
884 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
885 uriBuilder.path("config");
887 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
888 } catch (final Exception e) {
889 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
892 return uriBuilder.build();
896 public Response deleteConfigurationData(final String identifier) {
897 final InstanceIdentifierContext<DataSchemaNode> iiWithData = controllerContext.toInstanceIdentifier(identifier);
898 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
899 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
902 if (mountPoint != null) {
903 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
905 broker.commitConfigurationDataDelete(normalizedII).get();
907 } catch (final Exception e) {
908 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
909 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
910 if (searchedException.isPresent()) {
911 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
913 throw new RestconfDocumentedException("Error while deleting data", e);
915 return Response.status(Status.OK).build();
919 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
921 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
923 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
924 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
928 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
929 final String streamName = Notificator.createStreamNameFromUri(identifier);
930 if (Strings.isNullOrEmpty(streamName)) {
931 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
934 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
935 if (listener == null) {
936 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
939 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
940 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
941 paramToValues.get(DATASTORE_PARAM_NAME));
942 if (datastore == null) {
943 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
944 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
946 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
948 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
949 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
952 broker.registerToListenDataChanges(datastore, scope, listener);
954 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
955 int notificationPort = NOTIFICATION_PORT;
957 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
958 notificationPort = webSocketServerInstance.getPort();
959 } catch (final NullPointerException e) {
960 WebSocketServer.createInstance(NOTIFICATION_PORT);
962 final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
963 final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
965 return Response.status(Status.OK).location(uriToWebsocketServer).build();
969 * Load parameter for subscribing to stream from input composite node
973 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
975 private <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor,
976 final String paramName) {
977 final Optional<DataContainerChild<? extends PathArgument, ?>> augNode = value.getChild(SAL_REMOTE_AUG_IDENTIFIER);
978 if (!augNode.isPresent() && !(augNode instanceof AugmentationNode)) {
981 final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode =
982 ((AugmentationNode) augNode.get()).getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
983 if (!enumNode.isPresent()) {
986 final Object rawValue = enumNode.get().getValue();
987 if (!(rawValue instanceof String)) {
991 return resolveAsEnum(classDescriptor, (String) rawValue);
995 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
997 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
1000 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
1001 if (Strings.isNullOrEmpty(value)) {
1004 return resolveAsEnum(classDescriptor, value);
1007 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
1008 final T[] enumConstants = classDescriptor.getEnumConstants();
1009 if (enumConstants != null) {
1010 for (final T enm : classDescriptor.getEnumConstants()) {
1011 if (((Enum<?>) enm).name().equals(value)) {
1019 private Map<String, String> resolveValuesFromUri(final String uri) {
1020 final Map<String, String> result = new HashMap<>();
1021 final String[] tokens = uri.split("/");
1022 for (int i = 1; i < tokens.length; i++) {
1023 final String[] parameterTokens = tokens[i].split("=");
1024 if (parameterTokens.length == 2) {
1025 result.put(parameterTokens[0], parameterTokens[1]);
1031 private boolean endsWithMountPoint(final String identifier) {
1032 return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/");
1035 private String addMountPointIdentifier(final String identifier) {
1036 final boolean endsWith = identifier.endsWith("/");
1038 return (identifier + ControllerContext.MOUNT);
1041 return identifier + "/" + ControllerContext.MOUNT;
1044 private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
1045 TypeDefinition<?> baseType = type;
1046 QName qName = nodeQName;
1047 while (baseType.getBaseType() != null) {
1048 if (baseType instanceof ExtendedType) {
1049 qName = baseType.getQName();
1051 baseType = baseType.getBaseType();
1054 return new TypeDef(baseType, qName);
1058 private TypeDef typeDefinition(final DataSchemaNode node) {
1059 if (node instanceof LeafListSchemaNode) {
1060 return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
1061 } else if (node instanceof LeafSchemaNode) {
1062 return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
1063 } else if (node instanceof AnyXmlSchemaNode) {
1066 throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
1070 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1071 final InstanceIdentifierContext iiWithSchemaNode) {
1072 return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false);
1075 private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode(
1076 final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) {
1077 return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode(
1078 iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(),
1079 iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext());
1082 private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(
1083 final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) {
1084 Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null");
1085 final List<PathArgument> result = new ArrayList<PathArgument>();
1086 final Iterator<PathArgument> iter = instIdentifier.getPathArguments().iterator();
1087 while (iter.hasNext()) {
1088 final PathArgument pathArgument = iter.next();
1089 if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) {
1090 result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType()));
1092 result.add(pathArgument);
1094 return YangInstanceIdentifier.create(result);
1097 private boolean isDataContainerNode(final DataSchemaNode schemaNode) {
1098 if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) {
1104 public BigInteger getOperationalReceived() {
1105 // TODO Auto-generated method stub
1109 private MapNode makeModuleMapNode(final Set<Module> modules) {
1110 Preconditions.checkNotNull(modules);
1111 final Module restconfModule = getRestconfModule();
1112 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1113 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1114 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1116 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1117 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1119 for (final Module module : modules) {
1120 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1122 return listModuleBuilder.build();
1125 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1126 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1127 "moduleSchemaNode has to be of type ListSchemaNode");
1128 final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
1129 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
1130 .mapEntryBuilder(listModuleSchemaNode);
1132 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1133 (listModuleSchemaNode), "name");
1134 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1135 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1136 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
1139 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1140 (listModuleSchemaNode), "revision");
1141 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1142 Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
1143 final String revision = REVISION_FORMAT.format(module.getRevision());
1144 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
1147 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1148 (listModuleSchemaNode), "namespace");
1149 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1150 Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
1151 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
1152 .withValue(module.getNamespace().toString()).build());
1154 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1155 (listModuleSchemaNode), "feature");
1156 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1157 Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
1158 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
1159 .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
1160 for (final FeatureDefinition feature : module.getFeatures()) {
1161 featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
1162 .withValue(feature.getQName().getLocalName()).build());
1164 moduleNodeValues.withChild(featuresBuilder.build());
1166 return moduleNodeValues.build();
1169 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
1170 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
1171 "streamSchemaNode has to be of type ListSchemaNode");
1172 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
1173 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
1174 .mapEntryBuilder(listStreamSchemaNode);
1176 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1177 (listStreamSchemaNode), "name");
1178 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1179 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1180 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
1183 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1184 (listStreamSchemaNode), "description");
1185 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1186 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
1187 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
1188 .withValue("DESCRIPTION_PLACEHOLDER").build());
1190 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1191 (listStreamSchemaNode), "replay-support");
1192 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1193 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
1194 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
1195 .withValue(Boolean.valueOf(true)).build());
1197 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1198 (listStreamSchemaNode), "replay-log-creation-time");
1199 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1200 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
1201 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
1202 .withValue("").build());
1204 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1205 (listStreamSchemaNode), "events");
1206 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1207 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
1208 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
1209 .withValue("").build());
1211 return streamNodeValues.build();