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.Collections;
28 import java.util.Date;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
34 import java.util.concurrent.CancellationException;
35 import java.util.concurrent.ExecutionException;
36 import javax.ws.rs.core.Response;
37 import javax.ws.rs.core.Response.ResponseBuilder;
38 import javax.ws.rs.core.Response.Status;
39 import javax.ws.rs.core.UriBuilder;
40 import javax.ws.rs.core.UriInfo;
41 import org.apache.commons.lang3.StringUtils;
42 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
43 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
44 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
45 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
46 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
47 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
48 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
49 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
50 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
51 import org.opendaylight.controller.sal.rest.api.Draft02;
52 import org.opendaylight.controller.sal.rest.api.RestconfService;
53 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
54 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
55 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
56 import org.opendaylight.controller.sal.streams.listeners.Notificator;
57 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
58 import org.opendaylight.yangtools.yang.common.QName;
59 import org.opendaylight.yangtools.yang.common.QNameModule;
60 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
64 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
65 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
66 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
67 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
69 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
71 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
72 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
73 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
74 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
75 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
76 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
77 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
78 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
79 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
80 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
81 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
82 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
83 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
84 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
85 import org.opendaylight.yangtools.yang.model.api.Module;
86 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
87 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
88 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
89 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
90 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
91 import org.slf4j.Logger;
92 import org.slf4j.LoggerFactory;
94 public class RestconfImpl implements RestconfService {
96 private enum UriParameters {
97 PRETTY_PRINT("prettyPrint"),
100 private String uriParameterName;
102 UriParameters(final String uriParameterName) {
103 this.uriParameterName = uriParameterName;
107 public String toString() {
108 return uriParameterName;
112 private static final RestconfImpl INSTANCE = new RestconfImpl();
114 private static final int NOTIFICATION_PORT = 8181;
116 private static final int CHAR_NOT_FOUND = -1;
118 private static final String MOUNT_POINT_MODULE_NAME = "ietf-netconf";
120 private static final SimpleDateFormat REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
122 private static final String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
124 private static final String SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription";
126 private BrokerFacade broker;
128 private ControllerContext controllerContext;
130 private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class);
132 private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
134 private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION;
136 private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
138 private static final String DATASTORE_PARAM_NAME = "datastore";
140 private static final String SCOPE_PARAM_NAME = "scope";
142 private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
144 private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
146 private static final QName NETCONF_BASE_QNAME;
148 private static final QNameModule SAL_REMOTE_AUGMENT;
150 private static final YangInstanceIdentifier.AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER;
154 final Date eventSubscriptionAugRevision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
155 NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
156 SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT,
157 eventSubscriptionAugRevision);
158 SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"),
159 QName.create(SAL_REMOTE_AUGMENT, "datastore")));
160 } catch (final ParseException e) {
161 throw new RestconfDocumentedException(
162 "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
163 ErrorTag.OPERATION_FAILED);
164 } catch (final URISyntaxException e) {
165 throw new RestconfDocumentedException(
166 "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION,
167 ErrorTag.OPERATION_FAILED);
171 public void setBroker(final BrokerFacade broker) {
172 this.broker = broker;
175 public void setControllerContext(final ControllerContext controllerContext) {
176 this.controllerContext = controllerContext;
179 private RestconfImpl() {
182 public static RestconfImpl getInstance() {
187 public NormalizedNodeContext getModules(final UriInfo uriInfo) {
188 final Set<Module> allModules = controllerContext.getAllModules();
189 final MapNode allModuleMap = makeModuleMapNode(allModules);
191 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
193 final Module restconfModule = getRestconfModule();
194 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
195 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
196 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
198 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
199 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
200 moduleContainerBuilder.withChild(allModuleMap);
202 return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, modulesSchemaNode,
203 null, schemaContext), moduleContainerBuilder.build());
207 * Valid only for mount point
210 public NormalizedNodeContext getModules(final String identifier, final UriInfo uriInfo) {
211 Preconditions.checkNotNull(identifier);
212 if ( ! identifier.contains(ControllerContext.MOUNT)) {
213 final String errMsg = "URI has bad format. If modules behind mount point should be showed,"
214 + " URI has to end with " + ControllerContext.MOUNT;
215 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
218 final InstanceIdentifierContext<?> mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
219 final DOMMountPoint mountPoint = mountPointIdentifier.getMountPoint();
220 final Set<Module> modules = controllerContext.getAllModules(mountPoint);
221 final MapNode mountPointModulesMap = makeModuleMapNode(modules);
223 final Module restconfModule = getRestconfModule();
224 final DataSchemaNode modulesSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
225 restconfModule, Draft02.RestConfModule.MODULES_CONTAINER_SCHEMA_NODE);
226 Preconditions.checkState(modulesSchemaNode instanceof ContainerSchemaNode);
228 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> moduleContainerBuilder =
229 Builders.containerBuilder((ContainerSchemaNode) modulesSchemaNode);
230 moduleContainerBuilder.withChild(mountPointModulesMap);
232 return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, modulesSchemaNode,
233 mountPoint, controllerContext.getGlobalSchema()), moduleContainerBuilder.build());
237 public NormalizedNodeContext getModule(final String identifier, final UriInfo uriInfo) {
238 Preconditions.checkNotNull(identifier);
239 final QName moduleNameAndRevision = getModuleNameAndRevision(identifier);
240 Module module = null;
241 DOMMountPoint mountPoint = null;
242 final SchemaContext schemaContext;
243 if (identifier.contains(ControllerContext.MOUNT)) {
244 final InstanceIdentifierContext<?> mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
245 mountPoint = mountPointIdentifier.getMountPoint();
246 module = controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision);
247 schemaContext = mountPoint.getSchemaContext();
249 module = controllerContext.findModuleByNameAndRevision(moduleNameAndRevision);
250 schemaContext = controllerContext.getGlobalSchema();
253 if (module == null) {
254 final String errMsg = "Module with name '" + moduleNameAndRevision.getLocalName()
255 + "' and revision '" + moduleNameAndRevision.getRevision() + "' was not found.";
256 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
259 final Module restconfModule = getRestconfModule();
260 final Set<Module> modules = Collections.singleton(module);
261 final MapNode moduleMap = makeModuleMapNode(modules);
263 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
264 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
265 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
267 return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, moduleSchemaNode, mountPoint,
268 schemaContext), moduleMap);
272 public NormalizedNodeContext getAvailableStreams(final UriInfo uriInfo) {
273 final SchemaContext schemaContext = controllerContext.getGlobalSchema();
274 final Set<String> availableStreams = Notificator.getStreamNames();
275 final Module restconfModule = getRestconfModule();
276 final DataSchemaNode streamSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(restconfModule,
277 Draft02.RestConfModule.STREAM_LIST_SCHEMA_NODE);
278 Preconditions.checkState(streamSchemaNode instanceof ListSchemaNode);
280 final CollectionNodeBuilder<MapEntryNode, MapNode> listStreamsBuilder = Builders
281 .mapBuilder((ListSchemaNode) streamSchemaNode);
283 for (final String streamName : availableStreams) {
284 listStreamsBuilder.withChild(toStreamEntryNode(streamName, streamSchemaNode));
287 final DataSchemaNode streamsContainerSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
288 restconfModule, Draft02.RestConfModule.STREAMS_CONTAINER_SCHEMA_NODE);
289 Preconditions.checkState(streamsContainerSchemaNode instanceof ContainerSchemaNode);
291 final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> streamsContainerBuilder =
292 Builders.containerBuilder((ContainerSchemaNode) streamsContainerSchemaNode);
293 streamsContainerBuilder.withChild(listStreamsBuilder.build());
296 return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, streamsContainerSchemaNode, null,
297 schemaContext), streamsContainerBuilder.build());
301 public NormalizedNodeContext getOperations(final UriInfo uriInfo) {
302 final Set<Module> allModules = controllerContext.getAllModules();
303 return operationsFromModulesToNormalizedContext(allModules, null);
307 public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) {
308 Set<Module> modules = null;
309 DOMMountPoint mountPoint = null;
310 if (identifier.contains(ControllerContext.MOUNT)) {
311 final InstanceIdentifierContext<?> mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier);
312 mountPoint = mountPointIdentifier.getMountPoint();
313 modules = controllerContext.getAllModules(mountPoint);
316 final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with ";
317 throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
320 return operationsFromModulesToNormalizedContext(modules, mountPoint);
323 private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
324 final DOMMountPoint mountPoint) {
326 // FIXME find best way to change restconf-netconf yang schema for provide this functionality
327 final String errMsg = "We are not able support view operations functionality yet.";
328 throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED);
331 private Module getRestconfModule() {
332 final Module restconfModule = controllerContext.getRestconfModule();
333 if (restconfModule == null) {
334 throw new RestconfDocumentedException("ietf-restconf module was not found.", ErrorType.APPLICATION,
335 ErrorTag.OPERATION_NOT_SUPPORTED);
338 return restconfModule;
341 private QName getModuleNameAndRevision(final String identifier) {
342 final int mountIndex = identifier.indexOf(ControllerContext.MOUNT);
343 String moduleNameAndRevision = "";
344 if (mountIndex >= 0) {
345 moduleNameAndRevision = identifier.substring(mountIndex + ControllerContext.MOUNT.length());
347 moduleNameAndRevision = identifier;
350 final Splitter splitter = Splitter.on("/").omitEmptyStrings();
351 final Iterable<String> split = splitter.split(moduleNameAndRevision);
352 final List<String> pathArgs = Lists.<String> newArrayList(split);
353 if (pathArgs.size() < 2) {
354 throw new RestconfDocumentedException(
355 "URI has bad format. End of URI should be in format \'moduleName/yyyy-MM-dd\'", ErrorType.PROTOCOL,
356 ErrorTag.INVALID_VALUE);
360 final String moduleName = pathArgs.get(0);
361 final String revision = pathArgs.get(1);
362 final Date moduleRevision = REVISION_FORMAT.parse(revision);
363 return QName.create(null, moduleRevision, moduleName);
364 } catch (final ParseException e) {
365 throw new RestconfDocumentedException("URI has bad format. It should be \'moduleName/yyyy-MM-dd\'",
366 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
371 public Object getRoot() {
376 public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
377 final SchemaPath type = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
378 final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
379 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
380 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
381 final SchemaContext schemaContext;
382 if (identifier.contains(MOUNT_POINT_MODULE_NAME) && mountPoint != null) {
383 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
384 if ( ! mountRpcServices.isPresent()) {
385 throw new RestconfDocumentedException("Rpc service is missing.");
387 schemaContext = mountPoint.getSchemaContext();
388 response = mountRpcServices.get().invokeRpc(type, payload.getData());
390 if (namespace.toString().equals(SAL_REMOTE_NAMESPACE)) {
391 response = invokeSalRemoteRpcSubscribeRPC(payload);
393 response = broker.invokeRpc(type, payload.getData());
395 schemaContext = controllerContext.getGlobalSchema();
398 final DOMRpcResult result = checkRpcResponse(response);
400 RpcDefinition resultNodeSchema = null;
401 final NormalizedNode<?, ?> resultData = result.getResult();
402 if (result != null && result.getResult() != null) {
403 resultNodeSchema = (RpcDefinition) payload.getInstanceIdentifierContext().getSchemaNode();
406 return new NormalizedNodeContext(new InstanceIdentifierContext<RpcDefinition>(null,
407 resultNodeSchema, mountPoint, schemaContext), resultData);
410 private DOMRpcResult checkRpcResponse(final CheckedFuture<DOMRpcResult, DOMRpcException> response) {
411 if (response == null) {
415 final DOMRpcResult retValue = response.get();
416 if (retValue.getErrors() == null || retValue.getErrors().isEmpty()) {
419 throw new RestconfDocumentedException("RpcError message", null, retValue.getErrors());
421 catch (final InterruptedException e) {
422 throw new RestconfDocumentedException(
423 "The operation was interrupted while executing and did not complete.", ErrorType.RPC,
424 ErrorTag.PARTIAL_OPERATION);
426 catch (final ExecutionException e) {
427 Throwable cause = e.getCause();
428 if (cause instanceof CancellationException) {
429 throw new RestconfDocumentedException("The operation was cancelled while executing.", ErrorType.RPC,
430 ErrorTag.PARTIAL_OPERATION);
431 } else if (cause != null) {
432 while (cause.getCause() != null) {
433 cause = cause.getCause();
436 if (cause instanceof IllegalArgumentException) {
437 throw new RestconfDocumentedException(cause.getMessage(), ErrorType.PROTOCOL,
438 ErrorTag.INVALID_VALUE);
441 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
444 throw new RestconfDocumentedException("The operation encountered an unexpected error while executing.",
450 private void validateInput(final SchemaNode inputSchema, final NormalizedNodeContext payload) {
451 if (inputSchema != null && payload.getData() == null) {
452 // expected a non null payload
453 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
454 } else if (inputSchema == null && payload.getData() != null) {
455 // did not expect any input
456 throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
460 // TODO: Validate "mandatory" and "config" values here??? Or should those be
462 // validate in a more central location inside MD-SAL core.
466 private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
467 final ContainerNode value = (ContainerNode) payload.getData();
468 final QName rpcQName = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
469 final Optional<DataContainerChild<? extends PathArgument, ?>> path = value.getChild(new NodeIdentifier(
470 QName.create(payload.getInstanceIdentifierContext().getSchemaNode().getQName(), "path")));
471 final Object pathValue = path.isPresent() ? path.get().getValue() : null;
473 if (!(pathValue instanceof YangInstanceIdentifier)) {
474 throw new RestconfDocumentedException("Instance identifier was not normalized correctly.",
475 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
478 final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue);
479 String streamName = null;
480 if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) {
481 final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null);
483 LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class,
484 DATASTORE_PARAM_NAME);
485 datastore = datastore == null ? DEFAULT_DATASTORE : datastore;
487 DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME);
488 scope = scope == null ? DEFAULT_SCOPE : scope;
490 streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore
491 + "/scope=" + scope);
494 if (Strings.isNullOrEmpty(streamName)) {
495 throw new RestconfDocumentedException(
496 "Path is empty or contains value node which is not Container or List build-in type.",
497 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
500 final QName outputQname = QName.create(rpcQName, "output");
501 final QName streamNameQname = QName.create(rpcQName, "stream-name");
503 final ContainerNode output = ImmutableContainerNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(outputQname))
504 .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
506 if (!Notificator.existListenerFor(streamName)) {
507 final YangInstanceIdentifier normalizedPathIdentifier = controllerContext.toNormalized(pathIdentifier);
508 Notificator.createListener(normalizedPathIdentifier, streamName);
511 final DOMRpcResult defaultDOMRpcResult = new DefaultDOMRpcResult(output);
513 return Futures.immediateCheckedFuture(defaultDOMRpcResult);
517 public NormalizedNodeContext invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
518 if (StringUtils.isNotBlank(noPayload)) {
519 throw new RestconfDocumentedException("Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
522 String identifierEncoded = null;
523 DOMMountPoint mountPoint = null;
524 final SchemaContext schemaContext;
525 if (identifier.contains(ControllerContext.MOUNT)) {
526 // mounted RPC call - look up mount instance.
527 final InstanceIdentifierContext<?> mountPointId = controllerContext.toMountPointIdentifier(identifier);
528 mountPoint = mountPointId.getMountPoint();
529 schemaContext = mountPoint.getSchemaContext();
530 final int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT)
531 + ControllerContext.MOUNT.length() + 1;
532 final String remoteRpcName = identifier.substring(startOfRemoteRpcName);
533 identifierEncoded = remoteRpcName;
535 } else if (identifier.indexOf("/") != CHAR_NOT_FOUND) {
536 final String slashErrorMsg = String.format("Identifier %n%s%ncan\'t contain slash "
537 + "character (/).%nIf slash is part of identifier name then use %%2F placeholder.", identifier);
538 throw new RestconfDocumentedException(slashErrorMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
540 identifierEncoded = identifier;
541 schemaContext = controllerContext.getGlobalSchema();
544 final String identifierDecoded = controllerContext.urlPathArgDecode(identifierEncoded);
546 RpcDefinition rpc = null;
547 if (mountPoint == null) {
548 rpc = controllerContext.getRpcDefinition(identifierDecoded);
550 rpc = findRpc(mountPoint.getSchemaContext(), identifierDecoded);
554 throw new RestconfDocumentedException("RPC does not exist.", ErrorType.RPC, ErrorTag.UNKNOWN_ELEMENT);
557 if (rpc.getInput() != null) {
558 // FIXME : find a correct Error from specification
559 throw new IllegalStateException("RPC " + rpc + " does'n need input value!");
562 final CheckedFuture<DOMRpcResult, DOMRpcException> response;
563 if (mountPoint != null) {
564 final Optional<DOMRpcService> mountRpcServices = mountPoint.getService(DOMRpcService.class);
565 if ( ! mountRpcServices.isPresent()) {
566 throw new RestconfDocumentedException("Rpc service is missing.");
568 response = mountRpcServices.get().invokeRpc(rpc.getPath(), null);
570 response = broker.invokeRpc(rpc.getPath(), null);
573 final DOMRpcResult result = checkRpcResponse(response);
575 DataSchemaNode resultNodeSchema = null;
576 NormalizedNode<?, ?> resultData = null;
577 if (result != null && result.getResult() != null) {
578 resultData = result.getResult();
579 final ContainerSchemaNode rpcDataSchemaNode =
580 SchemaContextUtil.getRpcDataSchema(schemaContext, rpc.getOutput().getPath());
581 resultNodeSchema = rpcDataSchemaNode.getDataChildByName(result.getResult().getNodeType());
584 return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, resultNodeSchema, mountPoint,
585 schemaContext), resultData);
588 private RpcDefinition findRpc(final SchemaContext schemaContext, final String identifierDecoded) {
589 final String[] splittedIdentifier = identifierDecoded.split(":");
590 if (splittedIdentifier.length != 2) {
591 throw new RestconfDocumentedException(identifierDecoded
592 + " couldn't be splitted to 2 parts (module:rpc name)", ErrorType.APPLICATION,
593 ErrorTag.INVALID_VALUE);
595 for (final Module module : schemaContext.getModules()) {
596 if (module.getName().equals(splittedIdentifier[0])) {
597 for (final RpcDefinition rpcDefinition : module.getRpcs()) {
598 if (rpcDefinition.getQName().getLocalName().equals(splittedIdentifier[1])) {
599 return rpcDefinition;
608 public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
609 final InstanceIdentifierContext<?> iiWithData = controllerContext.toInstanceIdentifier(identifier);
610 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
611 NormalizedNode<?, ?> data = null;
612 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
613 if (mountPoint != null) {
614 data = broker.readConfigurationData(mountPoint, normalizedII);
616 data = broker.readConfigurationData(normalizedII);
619 throw new RestconfDocumentedException(
620 "Request could not be completed because the relevant data model content does not exist.",
621 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
623 return new NormalizedNodeContext(iiWithData, data);
626 // FIXME: Move this to proper place
627 @SuppressWarnings("unused")
628 private Integer parseDepthParameter(final UriInfo info) {
629 final String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
630 if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
635 final Integer depth = Integer.valueOf(param);
637 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
638 "Invalid depth parameter: " + depth, null,
639 "The depth parameter must be an integer > 1 or \"unbounded\""));
643 } catch (final NumberFormatException e) {
644 throw new RestconfDocumentedException(new RestconfError(ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
645 "Invalid depth parameter: " + e.getMessage(), null,
646 "The depth parameter must be an integer > 1 or \"unbounded\""));
651 public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) {
652 final InstanceIdentifierContext<?> iiWithData = controllerContext.toInstanceIdentifier(identifier);
653 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
654 NormalizedNode<?, ?> data = null;
655 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
656 if (mountPoint != null) {
657 data = broker.readOperationalData(mountPoint, normalizedII);
659 data = broker.readOperationalData(normalizedII);
662 throw new RestconfDocumentedException(
663 "Request could not be completed because the relevant data model content does not exist.",
664 ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
666 return new NormalizedNodeContext(iiWithData, data);
670 public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) {
671 Preconditions.checkNotNull(identifier);
672 final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
674 validateInput(iiWithData.getSchemaNode(), payload);
675 validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
676 validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData());
678 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
679 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
682 * There is a small window where another write transaction could be updating the same data
683 * simultaneously and we get an OptimisticLockFailedException. This error is likely
684 * transient and The WriteTransaction#submit API docs state that a retry will likely
685 * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
686 * probably will never succeed so we'll fail in that case.
688 * By retrying we're attempting to hide the internal implementation of the data store and
689 * how it handles concurrent updates from the restconf client. The client has instructed us
690 * to put the data and we should make every effort to do so without pushing optimistic lock
691 * failures back to the client and forcing them to handle it via retry (and having to
692 * document the behavior).
697 if (mountPoint != null) {
698 broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
700 broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet();
704 } catch (final TransactionCommitFailedException e) {
705 if(e instanceof OptimisticLockFailedException) {
707 LOG.debug("Got OptimisticLockFailedException on last try - failing");
708 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
711 LOG.debug("Got OptimisticLockFailedException - trying again");
713 throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
718 return Response.status(Status.OK).build();
721 private void validateTopLevelNodeName(final NormalizedNodeContext node,
722 final YangInstanceIdentifier identifier) {
724 final String payloadName = node.getData().getNodeType().getLocalName();
725 final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
728 if ( ! pathArguments.hasNext()) {
730 if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) {
731 throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
732 ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
736 final String identifierName = pathArguments.next().getNodeType().getLocalName();
737 if ( ! payloadName.equals(identifierName)) {
738 throw new RestconfDocumentedException("Payload name (" + payloadName
739 + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
740 ErrorTag.MALFORMED_MESSAGE);
746 * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
748 * @throws RestconfDocumentedException
749 * if key values or key count in payload and URI isn't equal
752 private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext<?> iiWithData,
753 final NormalizedNode<?, ?> payload) {
754 if (iiWithData.getSchemaNode() instanceof ListSchemaNode) {
755 final List<QName> keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition();
756 final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument();
757 if (lastPathArgument instanceof NodeIdentifierWithPredicates) {
758 final Map<QName, Object> uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument)
760 isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions);
765 private void isEqualUriAndPayloadKeyValues(final Map<QName, Object> uriKeyValues, final NormalizedNode<?, ?> payload,
766 final List<QName> keyDefinitions) {
767 for (final QName keyDefinition : keyDefinitions) {
768 final Object uriKeyValue = uriKeyValues.get(keyDefinition);
769 // should be caught during parsing URI to InstanceIdentifier
770 if (uriKeyValue == null) {
771 final String errMsg = "Missing key " + keyDefinition + " in URI.";
772 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
774 // TODO thing about the possibility to fix
775 // final List<SimpleNode<?>> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName());
776 // if (payloadKeyValues.isEmpty()) {
777 // final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body.";
778 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING);
781 // final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue();
782 // if (!uriKeyValue.equals(payloadKeyValue)) {
783 // final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() +
784 // "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. ";
785 // throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
791 public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
792 return createConfigurationData(payload, uriInfo);
795 // FIXME create RestconfIdetifierHelper and move this method there
796 private YangInstanceIdentifier checkConsistencyOfNormalizedNodeContext(final NormalizedNodeContext payload) {
797 Preconditions.checkArgument(payload != null);
798 Preconditions.checkArgument(payload.getData() != null);
799 Preconditions.checkArgument(payload.getData().getNodeType() != null);
800 Preconditions.checkArgument(payload.getInstanceIdentifierContext() != null);
801 Preconditions.checkArgument(payload.getInstanceIdentifierContext().getInstanceIdentifier() != null);
803 final QName payloadNodeQname = payload.getData().getNodeType();
804 final YangInstanceIdentifier yangIdent = payload.getInstanceIdentifierContext().getInstanceIdentifier();
805 if (payloadNodeQname.compareTo(yangIdent.getLastPathArgument().getNodeType()) > 0) {
808 final InstanceIdentifierContext<?> parentContext = payload.getInstanceIdentifierContext();
809 final SchemaNode parentSchemaNode = parentContext.getSchemaNode();
810 if(parentSchemaNode instanceof DataNodeContainer) {
811 final DataNodeContainer cast = (DataNodeContainer) parentSchemaNode;
812 for (final DataSchemaNode child : cast.getChildNodes()) {
813 if (payloadNodeQname.compareTo(child.getQName()) == 0) {
814 return YangInstanceIdentifier.builder(yangIdent).node(child.getQName()).build();
818 if (parentSchemaNode instanceof RpcDefinition) {
821 final String errMsg = "Error parsing input: DataSchemaNode has not children";
822 throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
826 public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
827 if (payload == null) {
828 throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
831 // FIXME: move this to parsing stage (we can have augmentation nodes here which do not have namespace)
832 // final URI payloadNS = payload.getData().getNodeType().getNamespace();
833 // if (payloadNS == null) {
834 // throw new RestconfDocumentedException(
835 // "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)",
836 // ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE);
839 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
840 final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
841 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
843 if (mountPoint != null) {
844 broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()).checkedGet();
846 broker.commitConfigurationDataPost(normalizedII, payload.getData()).checkedGet();
848 } catch(final RestconfDocumentedException e) {
850 } catch (final Exception e) {
851 throw new RestconfDocumentedException("Error creating data", e);
854 final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
855 // FIXME: Provide path to result.
856 final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII);
857 if (location != null) {
858 responseBuilder.location(location);
860 return responseBuilder.build();
863 private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
864 final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
865 uriBuilder.path("config");
867 uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint));
868 } catch (final Exception e) {
869 LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e);
872 return uriBuilder.build();
876 public Response deleteConfigurationData(final String identifier) {
877 final InstanceIdentifierContext<?> iiWithData = controllerContext.toInstanceIdentifier(identifier);
878 final DOMMountPoint mountPoint = iiWithData.getMountPoint();
879 final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
882 if (mountPoint != null) {
883 broker.commitConfigurationDataDelete(mountPoint, normalizedII);
885 broker.commitConfigurationDataDelete(normalizedII).get();
887 } catch (final Exception e) {
888 final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
889 Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
890 if (searchedException.isPresent()) {
891 throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
893 throw new RestconfDocumentedException("Error while deleting data", e);
895 return Response.status(Status.OK).build();
899 * Subscribes to some path in schema context (stream) to listen on changes on this stream.
901 * Additional parameters for subscribing to stream are loaded via rpc input parameters:
903 * <li>datastore</li> - default CONFIGURATION (other values of {@link LogicalDatastoreType} enum type)
904 * <li>scope</li> - default BASE (other values of {@link DataChangeScope})
908 public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
909 final String streamName = Notificator.createStreamNameFromUri(identifier);
910 if (Strings.isNullOrEmpty(streamName)) {
911 throw new RestconfDocumentedException("Stream name is empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
914 final ListenerAdapter listener = Notificator.getListenerFor(streamName);
915 if (listener == null) {
916 throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT);
919 final Map<String, String> paramToValues = resolveValuesFromUri(identifier);
920 final LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class,
921 paramToValues.get(DATASTORE_PARAM_NAME));
922 if (datastore == null) {
923 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)",
924 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
926 final DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME));
928 throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)",
929 ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE);
932 broker.registerToListenDataChanges(datastore, scope, listener);
934 final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder();
935 int notificationPort = NOTIFICATION_PORT;
937 final WebSocketServer webSocketServerInstance = WebSocketServer.getInstance();
938 notificationPort = webSocketServerInstance.getPort();
939 } catch (final NullPointerException e) {
940 WebSocketServer.createInstance(NOTIFICATION_PORT);
942 final UriBuilder uriToWebsocketServerBuilder = uriBuilder.port(notificationPort).scheme("ws");
943 final URI uriToWebsocketServer = uriToWebsocketServerBuilder.replacePath(streamName).build();
945 return Response.status(Status.OK).location(uriToWebsocketServer).build();
949 * Load parameter for subscribing to stream from input composite node
953 * @return enum object if its string value is equal to {@code paramName}. In other cases null.
955 private <T> T parseEnumTypeParameter(final ContainerNode value, final Class<T> classDescriptor,
956 final String paramName) {
957 final Optional<DataContainerChild<? extends PathArgument, ?>> augNode = value.getChild(SAL_REMOTE_AUG_IDENTIFIER);
958 if (!augNode.isPresent() && !(augNode instanceof AugmentationNode)) {
961 final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode =
962 ((AugmentationNode) augNode.get()).getChild(new NodeIdentifier(QName.create(SAL_REMOTE_AUGMENT, paramName)));
963 if (!enumNode.isPresent()) {
966 final Object rawValue = enumNode.get().getValue();
967 if (!(rawValue instanceof String)) {
971 return resolveAsEnum(classDescriptor, (String) rawValue);
975 * Checks whether {@code value} is one of the string representation of enumeration {@code classDescriptor}
977 * @return enum object if string value of {@code classDescriptor} enumeration is equal to {@code value}. Other cases
980 private <T> T parserURIEnumParameter(final Class<T> classDescriptor, final String value) {
981 if (Strings.isNullOrEmpty(value)) {
984 return resolveAsEnum(classDescriptor, value);
987 private <T> T resolveAsEnum(final Class<T> classDescriptor, final String value) {
988 final T[] enumConstants = classDescriptor.getEnumConstants();
989 if (enumConstants != null) {
990 for (final T enm : classDescriptor.getEnumConstants()) {
991 if (((Enum<?>) enm).name().equals(value)) {
999 private Map<String, String> resolveValuesFromUri(final String uri) {
1000 final Map<String, String> result = new HashMap<>();
1001 final String[] tokens = uri.split("/");
1002 for (int i = 1; i < tokens.length; i++) {
1003 final String[] parameterTokens = tokens[i].split("=");
1004 if (parameterTokens.length == 2) {
1005 result.put(parameterTokens[0], parameterTokens[1]);
1011 public BigInteger getOperationalReceived() {
1012 // TODO Auto-generated method stub
1016 private MapNode makeModuleMapNode(final Set<Module> modules) {
1017 Preconditions.checkNotNull(modules);
1018 final Module restconfModule = getRestconfModule();
1019 final DataSchemaNode moduleSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode(
1020 restconfModule, Draft02.RestConfModule.MODULE_LIST_SCHEMA_NODE);
1021 Preconditions.checkState(moduleSchemaNode instanceof ListSchemaNode);
1023 final CollectionNodeBuilder<MapEntryNode, MapNode> listModuleBuilder = Builders
1024 .mapBuilder((ListSchemaNode) moduleSchemaNode);
1026 for (final Module module : modules) {
1027 listModuleBuilder.withChild(toModuleEntryNode(module, moduleSchemaNode));
1029 return listModuleBuilder.build();
1032 protected MapEntryNode toModuleEntryNode(final Module module, final DataSchemaNode moduleSchemaNode) {
1033 Preconditions.checkArgument(moduleSchemaNode instanceof ListSchemaNode,
1034 "moduleSchemaNode has to be of type ListSchemaNode");
1035 final ListSchemaNode listModuleSchemaNode = (ListSchemaNode) moduleSchemaNode;
1036 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> moduleNodeValues = Builders
1037 .mapEntryBuilder(listModuleSchemaNode);
1039 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1040 (listModuleSchemaNode), "name");
1041 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1042 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1043 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(module.getName())
1046 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1047 (listModuleSchemaNode), "revision");
1048 final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1049 Preconditions.checkState(revisionSchemaNode instanceof LeafSchemaNode);
1050 final String revision = REVISION_FORMAT.format(module.getRevision());
1051 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) revisionSchemaNode).withValue(revision)
1054 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1055 (listModuleSchemaNode), "namespace");
1056 final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1057 Preconditions.checkState(namespaceSchemaNode instanceof LeafSchemaNode);
1058 moduleNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) namespaceSchemaNode)
1059 .withValue(module.getNamespace().toString()).build());
1061 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1062 (listModuleSchemaNode), "feature");
1063 final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1064 Preconditions.checkState(featureSchemaNode instanceof LeafListSchemaNode);
1065 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> featuresBuilder = Builders
1066 .leafSetBuilder((LeafListSchemaNode) featureSchemaNode);
1067 for (final FeatureDefinition feature : module.getFeatures()) {
1068 featuresBuilder.withChild(Builders.leafSetEntryBuilder(((LeafListSchemaNode) featureSchemaNode))
1069 .withValue(feature.getQName().getLocalName()).build());
1071 moduleNodeValues.withChild(featuresBuilder.build());
1073 return moduleNodeValues.build();
1076 protected MapEntryNode toStreamEntryNode(final String streamName, final DataSchemaNode streamSchemaNode) {
1077 Preconditions.checkArgument(streamSchemaNode instanceof ListSchemaNode,
1078 "streamSchemaNode has to be of type ListSchemaNode");
1079 final ListSchemaNode listStreamSchemaNode = (ListSchemaNode) streamSchemaNode;
1080 final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamNodeValues = Builders
1081 .mapEntryBuilder(listStreamSchemaNode);
1083 List<DataSchemaNode> instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1084 (listStreamSchemaNode), "name");
1085 final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1086 Preconditions.checkState(nameSchemaNode instanceof LeafSchemaNode);
1087 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode).withValue(streamName)
1090 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1091 (listStreamSchemaNode), "description");
1092 final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1093 Preconditions.checkState(descriptionSchemaNode instanceof LeafSchemaNode);
1094 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) nameSchemaNode)
1095 .withValue("DESCRIPTION_PLACEHOLDER").build());
1097 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1098 (listStreamSchemaNode), "replay-support");
1099 final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1100 Preconditions.checkState(replaySupportSchemaNode instanceof LeafSchemaNode);
1101 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replaySupportSchemaNode)
1102 .withValue(Boolean.valueOf(true)).build());
1104 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1105 (listStreamSchemaNode), "replay-log-creation-time");
1106 final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1107 Preconditions.checkState(replayLogCreationTimeSchemaNode instanceof LeafSchemaNode);
1108 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) replayLogCreationTimeSchemaNode)
1109 .withValue("").build());
1111 instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName(
1112 (listStreamSchemaNode), "events");
1113 final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null);
1114 Preconditions.checkState(eventsSchemaNode instanceof LeafSchemaNode);
1115 streamNodeValues.withChild(Builders.leafBuilder((LeafSchemaNode) eventsSchemaNode)
1116 .withValue("").build());
1118 return streamNodeValues.build();