2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.sal.restconf.impl
10 import com.google.common.base.Preconditions
11 import com.google.common.base.Splitter
12 import com.google.common.collect.Lists
14 import java.text.ParseException
15 import java.text.SimpleDateFormat
16 import java.util.ArrayList
17 import java.util.HashMap
20 import javax.ws.rs.core.Response
21 import javax.ws.rs.core.UriInfo
22 import org.opendaylight.controller.md.sal.common.api.TransactionStatus
23 import org.opendaylight.controller.sal.core.api.mount.MountInstance
24 import org.opendaylight.controller.sal.rest.api.RestconfService
25 import org.opendaylight.controller.sal.streams.listeners.Notificator
26 import org.opendaylight.controller.sal.streams.websockets.WebSocketServer
27 import org.opendaylight.yangtools.yang.common.QName
28 import org.opendaylight.yangtools.yang.common.RpcResult
29 import org.opendaylight.yangtools.yang.data.api.CompositeNode
30 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
32 import org.opendaylight.yangtools.yang.data.api.Node
33 import org.opendaylight.yangtools.yang.data.impl.NodeFactory
34 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
35 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
36 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
37 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
38 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
39 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
40 import org.opendaylight.yangtools.yang.model.api.Module
41 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
42 import org.opendaylight.yangtools.yang.model.api.SchemaContext
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
44 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
45 import org.opendaylight.yangtools.yang.model.util.EmptyType
46 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder
47 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder
49 import static javax.ws.rs.core.Response.Status.*
51 class RestconfImpl implements RestconfService {
53 val static RestconfImpl INSTANCE = new RestconfImpl
54 val static MOUNT_POINT_MODULE_NAME = "ietf-netconf"
55 val static REVISION_FORMAT = new SimpleDateFormat("yyyy-MM-dd")
56 val static RESTCONF_MODULE_DRAFT02_REVISION = "2013-10-19"
57 val static RESTCONF_MODULE_DRAFT02_NAME = "ietf-restconf"
58 val static RESTCONF_MODULE_DRAFT02_NAMESPACE = "urn:ietf:params:xml:ns:yang:ietf-restconf"
59 val static RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE = "restconf"
60 val static RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE = "restconf"
61 val static RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE = "modules"
62 val static RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE = "module"
63 val static RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE = "operations"
64 val static SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote"
65 val static SAL_REMOTE_RPC_SUBSRCIBE = "create-data-change-event-subscription"
71 extension ControllerContext controllerContext
74 if (INSTANCE !== null) {
75 throw new IllegalStateException("Already instantiated");
79 static def getInstance() {
83 override getModules() {
84 val restconfModule = getRestconfModule()
85 val List<Node<?>> modulesAsData = new ArrayList
86 val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
87 for (module : allModules) {
88 modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
90 val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
91 val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
92 return new StructuredData(modulesNode, modulesSchemaNode, null)
95 override getModules(String identifier) {
96 var Set<Module> modules = null
97 var MountInstance mountPoint = null
98 if (identifier.contains(ControllerContext.MOUNT)) {
99 mountPoint = identifier.toMountPointIdentifier.mountPoint
100 modules = mountPoint.allModules
102 throw new ResponseException(BAD_REQUEST, "URI has bad format. If modules behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
104 val List<Node<?>> modulesAsData = new ArrayList
105 val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
106 for (module : modules) {
107 modulesAsData.add(module.toModuleCompositeNode(moduleSchemaNode))
109 val modulesSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE)
110 val modulesNode = NodeFactory.createImmutableCompositeNode(modulesSchemaNode.QName, null, modulesAsData)
111 return new StructuredData(modulesNode, modulesSchemaNode, mountPoint)
114 override getModule(String identifier) {
115 val moduleNameAndRevision = identifier.moduleNameAndRevision
116 var Module module = null
117 var MountInstance mountPoint = null
118 if (identifier.contains(ControllerContext.MOUNT)) {
119 mountPoint = identifier.toMountPointIdentifier.mountPoint
120 module = mountPoint.findModuleByNameAndRevision(moduleNameAndRevision)
122 module = findModuleByNameAndRevision(moduleNameAndRevision)
124 if (module === null) {
125 throw new ResponseException(BAD_REQUEST,
126 "Module with name '" + moduleNameAndRevision.localName + "' and revision '" +
127 moduleNameAndRevision.revision + "' was not found.")
129 val moduleSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE)
130 val moduleNode = module.toModuleCompositeNode(moduleSchemaNode)
131 return new StructuredData(moduleNode, moduleSchemaNode, mountPoint)
134 override getOperations() {
135 return operationsFromModulesToStructuredData(allModules,null)
138 override getOperations(String identifier) {
139 var Set<Module> modules = null
140 var MountInstance mountPoint = null
141 if (identifier.contains(ControllerContext.MOUNT)) {
142 mountPoint = identifier.toMountPointIdentifier.mountPoint
143 modules = mountPoint.allModules
145 throw new ResponseException(BAD_REQUEST, "URI has bad format. If operations behind mount point should be showed, URI has to end with " + ControllerContext.MOUNT)
147 return operationsFromModulesToStructuredData(modules,mountPoint)
150 private def StructuredData operationsFromModulesToStructuredData(Set<Module> modules,MountInstance mountPoint) {
151 val List<Node<?>> operationsAsData = new ArrayList
152 val operationsSchemaNode = restconfModule.getSchemaNode(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE)
153 val fakeOperationsSchemaNode = new ContainerSchemaNodeBuilder(RESTCONF_MODULE_DRAFT02_NAME, 0, operationsSchemaNode.QName, operationsSchemaNode.path)
154 for (module : modules) {
155 for (rpc : module.rpcs) {
156 operationsAsData.add(NodeFactory.createImmutableSimpleNode(rpc.QName, null, null))
157 val fakeRpcSchemaNode = new LeafSchemaNodeBuilder(module.name, 0, rpc.QName, null)
158 fakeRpcSchemaNode.setAugmenting(true)
159 fakeRpcSchemaNode.setType(EmptyType.instance)
160 fakeOperationsSchemaNode.addChildNode(fakeRpcSchemaNode.build)
163 val operationsNode = NodeFactory.createImmutableCompositeNode(operationsSchemaNode.QName, null, operationsAsData)
164 return new StructuredData(operationsNode, fakeOperationsSchemaNode.build, mountPoint)
167 private def Module getRestconfModule() {
168 val restconfModule = findModuleByNameAndRevision(
169 QName.create(RESTCONF_MODULE_DRAFT02_NAMESPACE, RESTCONF_MODULE_DRAFT02_REVISION,
170 RESTCONF_MODULE_DRAFT02_NAME))
171 if (restconfModule === null) {
172 throw new ResponseException(INTERNAL_SERVER_ERROR, "Restconf module was not found.")
174 return restconfModule
177 private def QName getModuleNameAndRevision(String identifier) {
178 val indexOfMountPointFirstLetter = identifier.indexOf(ControllerContext.MOUNT)
179 var moduleNameAndRevision = "";
180 if (indexOfMountPointFirstLetter !== -1) { // module and revision is behind mount point string
181 moduleNameAndRevision = identifier.substring(indexOfMountPointFirstLetter + ControllerContext.MOUNT.length)
183 moduleNameAndRevision = identifier
185 val pathArgs = Lists.newArrayList(Splitter.on("/").omitEmptyStrings.split(moduleNameAndRevision))
186 if (pathArgs.length < 2) {
187 throw new ResponseException(BAD_REQUEST,
188 "URI has bad format. End of URI should be in format 'moduleName/yyyy-MM-dd'")
191 val moduleName = pathArgs.head
192 val moduleRevision = REVISION_FORMAT.parse(pathArgs.get(1))
193 return QName.create(null, moduleRevision, moduleName)
194 } catch(ParseException e) {
195 throw new ResponseException(BAD_REQUEST, "URI has bad format. It should be 'moduleName/yyyy-MM-dd'")
199 private def CompositeNode toModuleCompositeNode(Module module, DataSchemaNode moduleSchemaNode) {
200 val List<Node<?>> moduleNodeValues = new ArrayList
201 val nameSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("name").head
202 moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(nameSchemaNode.QName, null, module.name))
203 val revisionSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("revision").head
204 moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(revisionSchemaNode.QName, null, REVISION_FORMAT.format(module.revision)))
205 val namespaceSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("namespace").head
206 moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(namespaceSchemaNode.QName, null, module.namespace.toString))
207 val featureSchemaNode = (moduleSchemaNode as DataNodeContainer).findInstanceDataChildrenByName("feature").head
208 for (feature : module.features) {
209 moduleNodeValues.add(NodeFactory.createImmutableSimpleNode(featureSchemaNode.QName, null, feature.QName.localName))
211 return NodeFactory.createImmutableCompositeNode(moduleSchemaNode.QName, null, moduleNodeValues)
214 private def DataSchemaNode getSchemaNode(Module restconfModule, String schemaNodeName) {
215 val restconfGrouping = restconfModule.groupings.filter[g|g.QName.localName == RESTCONF_MODULE_DRAFT02_RESTCONF_GROUPING_SCHEMA_NODE].head
216 val restconfContainer = restconfGrouping.findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_RESTCONF_CONTAINER_SCHEMA_NODE).head
217 if (schemaNodeName == RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE) {
218 return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_OPERATIONS_CONTAINER_SCHEMA_NODE).head
219 } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE) {
220 return (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
221 } else if (schemaNodeName == RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE) {
222 val modules = (restconfContainer as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULES_CONTAINER_SCHEMA_NODE).head
223 return (modules as DataNodeContainer).findInstanceDataChildrenByName(RESTCONF_MODULE_DRAFT02_MODULE_LIST_SCHEMA_NODE).head
232 override invokeRpc(String identifier, CompositeNode payload) {
233 val rpc = identifier.rpcDefinition
235 throw new ResponseException(NOT_FOUND, "RPC does not exist.");
237 if (rpc.QName.namespace.toString == SAL_REMOTE_NAMESPACE && rpc.QName.localName == SAL_REMOTE_RPC_SUBSRCIBE) {
238 val value = normalizeNode(payload, rpc.input, null)
239 val pathNode = value?.getFirstSimpleByName(QName.create(rpc.QName, "path"))
240 val pathValue = pathNode?.value
241 if (pathValue === null && !(pathValue instanceof InstanceIdentifier)) {
242 throw new ResponseException(INTERNAL_SERVER_ERROR, "Instance identifier was not normalized correctly.");
244 val pathIdentifier = (pathValue as InstanceIdentifier)
245 var String streamName = null
246 if (!pathIdentifier.path.nullOrEmpty) {
247 streamName = Notificator.createStreamNameFromUri(pathIdentifier.toFullRestconfIdentifier)
249 if (streamName.nullOrEmpty) {
250 throw new ResponseException(BAD_REQUEST, "Path is empty or contains data node which is not Container or List build-in type.");
252 val streamNameNode = NodeFactory.createImmutableSimpleNode(QName.create(rpc.output.QName, "stream-name"), null, streamName)
253 val List<Node<?>> output = new ArrayList
254 output.add(streamNameNode)
255 val responseData = NodeFactory.createMutableCompositeNode(rpc.output.QName, null, output, null, null)
257 if (!Notificator.existListenerFor(pathIdentifier)) {
258 Notificator.createListener(pathIdentifier, streamName)
261 return new StructuredData(responseData, rpc.output, null)
263 return callRpc(identifier.rpcDefinition, payload)
266 override invokeRpc(String identifier, String noPayload) {
267 if (!noPayload.nullOrEmpty) {
268 throw new ResponseException(UNSUPPORTED_MEDIA_TYPE, "Content-Type contains unsupported Media Type.");
270 return callRpc(identifier.rpcDefinition, null)
273 private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
275 throw new ResponseException(NOT_FOUND, "RPC does not exist.");
277 var CompositeNode rpcRequest;
278 if (payload === null) {
279 rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
281 val value = normalizeNode(payload, rpc.input, null)
282 val List<Node<?>> input = new ArrayList
284 rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
286 val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
287 if (!rpcResult.successful) {
288 throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
290 if (rpcResult.result === null) {
293 return new StructuredData(rpcResult.result, rpc.output, null)
296 override readConfigurationData(String identifier) {
297 val iiWithData = identifier.toInstanceIdentifier
298 var CompositeNode data = null;
299 if (iiWithData.mountPoint !== null) {
300 data = broker.readConfigurationDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
302 data = broker.readConfigurationData(iiWithData.getInstanceIdentifier);
304 return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
307 override readOperationalData(String identifier) {
308 val iiWithData = identifier.toInstanceIdentifier
309 var CompositeNode data = null;
310 if (iiWithData.mountPoint !== null) {
311 data = broker.readOperationalDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
313 data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
315 return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
318 override updateConfigurationData(String identifier, CompositeNode payload) {
319 val iiWithData = identifier.toInstanceIdentifier
320 val value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
321 var RpcResult<TransactionStatus> status = null
322 if (iiWithData.mountPoint !== null) {
323 status = broker.commitConfigurationDataPutBehindMountPoint(iiWithData.mountPoint,
324 iiWithData.instanceIdentifier, value).get()
326 status = broker.commitConfigurationDataPut(iiWithData.instanceIdentifier, value).get();
328 switch status.result {
329 case TransactionStatus.COMMITED: Response.status(OK).build
330 default: Response.status(INTERNAL_SERVER_ERROR).build
334 override createConfigurationData(String identifier, CompositeNode payload) {
335 if (payload.namespace === null) {
336 throw new ResponseException(BAD_REQUEST,
337 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
339 var InstanceIdWithSchemaNode iiWithData;
340 var CompositeNode value;
341 if (payload.representsMountPointRootData) { // payload represents mount point data and URI represents path to the mount point
342 if (identifier.endsWithMountPoint) {
343 throw new ResponseException(BAD_REQUEST,
344 "URI has bad format. URI should be without \"" + ControllerContext.MOUNT + "\" for POST operation.");
346 val completIdentifier = identifier.addMountPointIdentifier
347 iiWithData = completIdentifier.toInstanceIdentifier
348 value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
350 val uncompleteInstIdWithData = identifier.toInstanceIdentifier
351 val parentSchema = uncompleteInstIdWithData.schemaNode as DataNodeContainer
352 val module = uncompleteInstIdWithData.mountPoint.findModule(payload)
353 if (module === null) {
354 throw new ResponseException(BAD_REQUEST, "Module was not found for \"" + payload.namespace + "\"")
356 val schemaNode = parentSchema.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
357 value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
358 iiWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
360 var RpcResult<TransactionStatus> status = null
361 if (iiWithData.mountPoint !== null) {
362 status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
363 iiWithData.instanceIdentifier, value)?.get();
365 status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
367 if (status === null) {
368 return Response.status(ACCEPTED).build
370 switch status.result {
371 case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
372 default: Response.status(INTERNAL_SERVER_ERROR).build
376 override createConfigurationData(CompositeNode payload) {
377 if (payload.namespace === null) {
378 throw new ResponseException(BAD_REQUEST,
379 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
381 val module = findModule(null, payload)
382 if (module === null) {
383 throw new ResponseException(BAD_REQUEST,
384 "Data has bad format. Root element node has incorrect namespace (XML format) or module name(JSON format)");
386 val schemaNode = module.findInstanceDataChildByNameAndNamespace(payload.name, module.namespace)
387 val value = normalizeNode(payload, schemaNode, null)
388 val iiWithData = addLastIdentifierFromData(null, value, schemaNode)
389 var RpcResult<TransactionStatus> status = null
390 if (iiWithData.mountPoint !== null) {
391 status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
392 iiWithData.instanceIdentifier, value)?.get();
394 status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
396 if (status === null) {
397 return Response.status(ACCEPTED).build
399 switch status.result {
400 case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
401 default: Response.status(INTERNAL_SERVER_ERROR).build
405 override deleteConfigurationData(String identifier) {
406 val iiWithData = identifier.toInstanceIdentifier
407 var RpcResult<TransactionStatus> status = null
408 if (iiWithData.mountPoint !== null) {
409 status = broker.commitConfigurationDataDeleteBehindMountPoint(iiWithData.mountPoint,
410 iiWithData.getInstanceIdentifier).get;
412 status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier).get;
414 switch status.result {
415 case TransactionStatus.COMMITED: Response.status(OK).build
416 default: Response.status(INTERNAL_SERVER_ERROR).build
420 override subscribeToStream(String identifier, UriInfo uriInfo) {
421 val streamName = Notificator.createStreamNameFromUri(identifier)
422 if (streamName.nullOrEmpty) {
423 throw new ResponseException(BAD_REQUEST, "Stream name is empty.")
425 val listener = Notificator.getListenerFor(streamName);
426 if (listener === null) {
427 throw new ResponseException(BAD_REQUEST, "Stream was not found.")
429 broker.registerToListenDataChanges(listener)
430 val uriBuilder = uriInfo.getAbsolutePathBuilder()
431 val uriToWebsocketServer = uriBuilder.port(WebSocketServer.PORT).replacePath(streamName).build()
432 return Response.status(OK).location(uriToWebsocketServer).build
435 private def dispatch URI namespace(CompositeNode data) {
436 return data.nodeType.namespace
439 private def dispatch URI namespace(CompositeNodeWrapper data) {
440 return data.namespace
443 private def dispatch String localName(CompositeNode data) {
444 return data.nodeType.localName
447 private def dispatch String localName(CompositeNodeWrapper data) {
448 return data.localName
451 private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
452 if (mountPoint !== null) {
453 return mountPoint.findModuleByNamespace(data.nodeType.namespace)
455 return findModuleByNamespace(data.nodeType.namespace)
459 private def dispatch Module findModule(MountInstance mountPoint, CompositeNodeWrapper data) {
460 Preconditions.checkNotNull(data.namespace)
461 var Module module = null;
462 if (mountPoint !== null) {
463 module = mountPoint.findModuleByNamespace(data.namespace) // namespace from XML
464 if (module === null) {
465 module = mountPoint.findModuleByName(data.namespace.toString) // namespace (module name) from JSON
468 module = data.namespace.findModuleByNamespace // namespace from XML
469 if (module === null) {
470 module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
476 private def dispatch getName(CompositeNode data) {
477 return data.nodeType.localName
480 private def dispatch getName(CompositeNodeWrapper data) {
481 return data.localName
484 private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode,
485 CompositeNode data, DataSchemaNode schemaOfData) {
486 val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
487 var InstanceIdentifierBuilder iiBuilder = null
488 if (iiOriginal === null) {
489 iiBuilder = InstanceIdentifier.builder
491 iiBuilder = InstanceIdentifier.builder(iiOriginal)
494 if (schemaOfData instanceof ListSchemaNode) {
495 iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
497 iiBuilder.node(schemaOfData.QName)
499 return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
502 private def resolveKeysFromData(ListSchemaNode listNode, CompositeNode dataNode) {
503 val keyValues = new HashMap<QName, Object>();
504 for (key : listNode.keyDefinition) {
505 val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
506 if (dataNodeKeyValueObject === null) {
507 throw new ResponseException(BAD_REQUEST,
508 "Data contains list \"" + dataNode.nodeType.localName + "\" which does not contain key: \"" +
509 key.localName + "\"")
511 keyValues.put(key, dataNodeKeyValueObject);
516 private def endsWithMountPoint(String identifier) {
517 return (identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"))
520 private def representsMountPointRootData(CompositeNode data) {
521 return ((data.namespace == SchemaContext.NAME.namespace || data.namespace == MOUNT_POINT_MODULE_NAME) &&
522 data.localName == SchemaContext.NAME.localName)
525 private def addMountPointIdentifier(String identifier) {
526 if (identifier.endsWith("/")) {
527 return identifier + ControllerContext.MOUNT
529 return identifier + "/" + ControllerContext.MOUNT
532 private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, MountInstance mountPoint) {
533 if (schema === null) {
534 throw new ResponseException(INTERNAL_SERVER_ERROR,
535 "Data schema node was not found for " + node?.nodeType?.localName)
537 if (!(schema instanceof DataNodeContainer)) {
538 throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
540 if (node instanceof CompositeNodeWrapper) {
541 if ((node as CompositeNodeWrapper).changeAllowed) {
542 normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
544 return (node as CompositeNodeWrapper).unwrap()
549 private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
550 MountInstance mountPoint) {
551 if (schema === null) {
552 throw new ResponseException(BAD_REQUEST,
553 "Data has bad format.\n\"" + nodeBuilder.localName + "\" does not exist in yang schema.");
556 var QName currentAugment;
557 if (nodeBuilder.qname !== null) {
558 currentAugment = previousAugment
560 currentAugment = normalizeNodeName(nodeBuilder, schema, previousAugment, mountPoint)
561 if (nodeBuilder.qname === null) {
562 throw new ResponseException(BAD_REQUEST,
563 "Data has bad format.\nIf data is in XML format then namespace for \"" + nodeBuilder.localName +
564 "\" should be \"" + schema.QName.namespace + "\".\n" +
565 "If data is in JSON format then module name for \"" + nodeBuilder.localName +
566 "\" should be corresponding to namespace \"" + schema.QName.namespace + "\".");
570 if (nodeBuilder instanceof CompositeNodeWrapper) {
571 val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
572 for (child : children) {
573 val potentialSchemaNodes = (schema as DataNodeContainer).findInstanceDataChildrenByName(child.localName)
574 if (potentialSchemaNodes.size > 1 && child.namespace === null) {
575 val StringBuilder namespacesOfPotentialModules = new StringBuilder;
576 for (potentialSchemaNode : potentialSchemaNodes) {
577 namespacesOfPotentialModules.append(" ").append(potentialSchemaNode.QName.namespace.toString).append("\n")
579 throw new ResponseException(BAD_REQUEST,
580 "Node \"" + child.localName + "\" is added as augment from more than one module. "
581 + "Therefore node must have namespace (XML format) or module name (JSON format)."
582 + "\nThe node is added as augment from modules with namespaces:\n" + namespacesOfPotentialModules)
584 var rightNodeSchemaFound = false
585 for (potentialSchemaNode : potentialSchemaNodes) {
586 if (!rightNodeSchemaFound) {
587 val potentialCurrentAugment = normalizeNodeName(child, potentialSchemaNode, currentAugment,
589 if (child.qname !== null) {
590 normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint)
591 rightNodeSchemaFound = true
595 if (!rightNodeSchemaFound) {
596 throw new ResponseException(BAD_REQUEST,
597 "Schema node \"" + child.localName + "\" was not found in module.")
600 if (schema instanceof ListSchemaNode) {
601 val listKeys = (schema as ListSchemaNode).keyDefinition
602 for (listKey : listKeys) {
604 for (child : children) {
605 if (child.unwrap.nodeType.localName == listKey.localName) {
610 throw new ResponseException(BAD_REQUEST,
611 "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName +
616 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
617 val simpleNode = (nodeBuilder as SimpleNodeWrapper)
618 val value = simpleNode.value
619 var inputValue = value;
621 if (schema.typeDefinition instanceof IdentityrefTypeDefinition) {
622 if (value instanceof String) {
623 inputValue = new IdentityValuesDTO(nodeBuilder.namespace.toString, value as String, null)
624 } // else value is already instance of IdentityValuesDTO
627 val outputValue = RestCodec.from(schema.typeDefinition, mountPoint)?.deserialize(inputValue);
628 simpleNode.setValue(outputValue)
629 } else if (nodeBuilder instanceof EmptyNodeWrapper) {
630 val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
631 if (schema instanceof LeafSchemaNode) {
632 emptyNodeBuilder.setComposite(false);
633 } else if (schema instanceof ContainerSchemaNode) {
635 // FIXME: Add presence check
636 emptyNodeBuilder.setComposite(true);
641 private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
642 var baseType = node.type
643 while (baseType.baseType !== null) {
644 baseType = baseType.baseType;
649 private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
650 var TypeDefinition<?> baseType = node.type
651 while (baseType.baseType !== null) {
652 baseType = baseType.baseType;
657 private def QName normalizeNodeName(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
658 MountInstance mountPoint) {
659 var validQName = schema.QName
660 var currentAugment = previousAugment;
661 if (schema.augmenting) {
662 currentAugment = schema.QName
663 } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
664 validQName = QName.create(currentAugment, schema.QName.localName);
666 var String moduleName = null;
667 if (mountPoint === null) {
668 moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
670 moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.namespace)
672 if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
673 nodeBuilder.namespace.toString == moduleName || nodeBuilder.namespace == MOUNT_POINT_MODULE_NAME) {
674 nodeBuilder.qname = validQName
676 return currentAugment