1 package org.opendaylight.controller.sal.restconf.impl
3 import com.google.common.base.Preconditions
5 import java.util.ArrayList
6 import java.util.HashMap
9 import javax.ws.rs.core.Response
10 import org.opendaylight.controller.md.sal.common.api.TransactionStatus
11 import org.opendaylight.controller.sal.core.api.mount.MountInstance
12 import org.opendaylight.controller.sal.rest.api.RestconfService
13 import org.opendaylight.yangtools.yang.common.QName
14 import org.opendaylight.yangtools.yang.common.RpcResult
15 import org.opendaylight.yangtools.yang.data.api.CompositeNode
16 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
17 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder
18 import org.opendaylight.yangtools.yang.data.api.Node
19 import org.opendaylight.yangtools.yang.data.impl.NodeFactory
20 import org.opendaylight.yangtools.yang.model.api.ChoiceNode
21 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode
22 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer
23 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode
24 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
25 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode
26 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode
27 import org.opendaylight.yangtools.yang.model.api.Module
28 import org.opendaylight.yangtools.yang.model.api.RpcDefinition
29 import org.opendaylight.yangtools.yang.model.api.TypeDefinition
30 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
32 import static javax.ws.rs.core.Response.Status.*
34 class RestconfImpl implements RestconfService {
36 val static RestconfImpl INSTANCE = new RestconfImpl
42 extension ControllerContext controllerContext
45 if (INSTANCE !== null) {
46 throw new IllegalStateException("Already instantiated");
50 static def getInstance() {
54 override readAllData() {
55 // return broker.readOperationalData("".toInstanceIdentifier.getInstanceIdentifier);
56 throw new UnsupportedOperationException("Reading all data is currently not supported.")
59 override getModules() {
60 throw new UnsupportedOperationException("TODO: auto-generated method stub")
67 override invokeRpc(String identifier, CompositeNode payload) {
68 return callRpc(identifier.rpcDefinition, payload)
71 override invokeRpc(String identifier) {
72 return callRpc(identifier.rpcDefinition, null)
75 private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
77 throw new ResponseException(NOT_FOUND, "RPC does not exist.");
79 var CompositeNode rpcRequest;
80 if (payload === null) {
81 rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
83 val value = normalizeNode(payload, rpc.input, null)
84 val List<Node<?>> input = new ArrayList
86 rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
88 val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
89 if (!rpcResult.successful) {
90 throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
92 if (rpcResult.result === null) {
95 return new StructuredData(rpcResult.result, rpc.output)
98 override readData(String identifier) {
99 val iiWithData = identifier.toInstanceIdentifier
100 var CompositeNode data = null;
101 if (iiWithData.mountPoint !== null) {
102 data = broker.readOperationalDataBehindMountPoint(iiWithData.mountPoint, iiWithData.instanceIdentifier)
104 data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
106 return new StructuredData(data, iiWithData.schemaNode)
109 override readConfigurationData(String identifier) {
110 val iiWithData = identifier.toInstanceIdentifier
111 var CompositeNode data = null;
112 if (iiWithData.mountPoint !== null) {
113 data = broker.readConfigurationDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
115 data = broker.readConfigurationData(iiWithData.getInstanceIdentifier);
117 return new StructuredData(data, iiWithData.schemaNode)
120 override readOperationalData(String identifier) {
121 val iiWithData = identifier.toInstanceIdentifier
122 var CompositeNode data = null;
123 if (iiWithData.mountPoint !== null) {
124 data = broker.readOperationalDataBehindMountPoint(iiWithData.mountPoint, iiWithData.getInstanceIdentifier)
126 data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
128 return new StructuredData(data, iiWithData.schemaNode)
131 override updateConfigurationDataLegacy(String identifier, CompositeNode payload) {
132 updateConfigurationData(identifier, payload);
135 override updateConfigurationData(String identifier, CompositeNode payload) {
136 val iiWithData = identifier.toInstanceIdentifier
137 val value = normalizeNode(payload, iiWithData.schemaNode, iiWithData.mountPoint)
138 var RpcResult<TransactionStatus> status = null
139 if (iiWithData.mountPoint !== null) {
140 status = broker.commitConfigurationDataPutBehindMountPoint(iiWithData.mountPoint,
141 iiWithData.instanceIdentifier, value).get()
143 status = broker.commitConfigurationDataPut(iiWithData.instanceIdentifier, value).get();
145 switch status.result {
146 case TransactionStatus.COMMITED: Response.status(OK).build
147 default: Response.status(INTERNAL_SERVER_ERROR).build
151 override createConfigurationDataLegacy(String identifier, CompositeNode payload) {
152 createConfigurationData(identifier, payload);
155 override createConfigurationData(String identifier, CompositeNode payload) {
156 if (payload.namespace === null) {
157 throw new ResponseException(BAD_REQUEST,
158 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
160 val uncompleteInstIdWithData = identifier.toInstanceIdentifier
161 val schemaNode = uncompleteInstIdWithData.mountPoint.findModule(payload)?.getSchemaChildNode(payload)
162 val value = normalizeNode(payload, schemaNode, uncompleteInstIdWithData.mountPoint)
163 val completeInstIdWithData = uncompleteInstIdWithData.addLastIdentifierFromData(value, schemaNode)
164 var RpcResult<TransactionStatus> status = null
165 if (completeInstIdWithData.mountPoint !== null) {
166 status = broker.commitConfigurationDataPostBehindMountPoint(completeInstIdWithData.mountPoint,
167 completeInstIdWithData.instanceIdentifier, value)?.get();
169 status = broker.commitConfigurationDataPost(completeInstIdWithData.instanceIdentifier, value)?.get();
171 if (status === null) {
172 return Response.status(ACCEPTED).build
174 switch status.result {
175 case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
176 default: Response.status(INTERNAL_SERVER_ERROR).build
180 override createConfigurationData(CompositeNode payload) {
181 if (payload.namespace === null) {
182 throw new ResponseException(BAD_REQUEST,
183 "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)");
185 val schemaNode = findModule(null, payload)?.getSchemaChildNode(payload)
186 val value = normalizeNode(payload, schemaNode, null)
187 val iiWithData = addLastIdentifierFromData(null, value, schemaNode)
188 var RpcResult<TransactionStatus> status = null
189 if (iiWithData.mountPoint !== null) {
190 status = broker.commitConfigurationDataPostBehindMountPoint(iiWithData.mountPoint,
191 iiWithData.instanceIdentifier, value)?.get();
193 status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
195 if (status === null) {
196 return Response.status(ACCEPTED).build
198 switch status.result {
199 case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
200 default: Response.status(INTERNAL_SERVER_ERROR).build
204 override deleteConfigurationData(String identifier) {
205 val iiWithData = identifier.toInstanceIdentifier
206 var RpcResult<TransactionStatus> status = null
207 if (iiWithData.mountPoint !== null) {
208 status = broker.commitConfigurationDataDeleteBehindMountPoint(iiWithData.mountPoint,
209 iiWithData.getInstanceIdentifier).get;
211 status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier).get;
213 switch status.result {
214 case TransactionStatus.COMMITED: Response.status(OK).build
215 default: Response.status(INTERNAL_SERVER_ERROR).build
219 private def dispatch URI namespace(CompositeNode data) {
220 return data.nodeType.namespace
223 private def dispatch URI namespace(CompositeNodeWrapper data) {
224 return data.namespace
227 private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
228 if (mountPoint !== null) {
229 return mountPoint.findModuleByNamespace(data.nodeType.namespace)
231 return findModuleByNamespace(data.nodeType.namespace)
235 private def dispatch Module findModule(MountInstance mountPoint, CompositeNodeWrapper data) {
236 Preconditions.checkNotNull(data.namespace)
237 var Module module = null;
238 if (mountPoint !== null) {
239 module = mountPoint.findModuleByNamespace(data.namespace) // namespace from XML
240 if (module === null) {
241 module = mountPoint.findModuleByName(data.namespace.toString) // namespace (module name) from JSON
244 module = data.namespace.findModuleByNamespace // namespace from XML
245 if (module === null) {
246 module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
252 private def dispatch DataSchemaNode getSchemaChildNode(DataNodeContainer parentSchemaNode, CompositeNode data) {
253 return parentSchemaNode?.getDataChildByName(data.nodeType.localName)
256 private def dispatch DataSchemaNode getSchemaChildNode(DataNodeContainer parentSchemaNode, CompositeNodeWrapper data) {
257 return parentSchemaNode?.getDataChildByName(data.localName)
260 private def InstanceIdWithSchemaNode addLastIdentifierFromData(InstanceIdWithSchemaNode identifierWithSchemaNode,
261 CompositeNode data, DataSchemaNode schemaOfData) {
262 val iiOriginal = identifierWithSchemaNode?.instanceIdentifier
263 var InstanceIdentifierBuilder iiBuilder = null
264 if (iiOriginal === null) {
265 iiBuilder = InstanceIdentifier.builder
267 iiBuilder = InstanceIdentifier.builder(iiOriginal)
270 if (schemaOfData instanceof ListSchemaNode) {
271 iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
273 iiBuilder.node(schemaOfData.QName)
275 return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
278 private def resolveKeysFromData(ListSchemaNode listNode, CompositeNode dataNode) {
279 val keyValues = new HashMap<QName, Object>();
280 for (key : listNode.keyDefinition) {
281 val dataNodeKeyValueObject = dataNode.getSimpleNodesByName(key.localName)?.head?.value
282 if (dataNodeKeyValueObject === null) {
283 throw new ResponseException(BAD_REQUEST,
284 "Data contains list \"" + dataNode.nodeType.localName + "\" which does not contain key: \"" +
285 key.localName + "\"")
287 keyValues.put(key, dataNodeKeyValueObject);
292 private def CompositeNode normalizeNode(CompositeNode node, DataSchemaNode schema, MountInstance mountPoint) {
293 if (schema === null) {
294 throw new ResponseException(INTERNAL_SERVER_ERROR, "Data schema node was not found for " + node?.nodeType?.localName)
296 if (!(schema instanceof DataNodeContainer)) {
297 throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
299 if (node instanceof CompositeNodeWrapper) {
300 if ((node as CompositeNodeWrapper).changeAllowed) {
301 normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
303 return (node as CompositeNodeWrapper).unwrap()
308 private def void normalizeNode(NodeWrapper<?> nodeBuilder, DataSchemaNode schema, QName previousAugment,
309 MountInstance mountPoint) {
310 if (schema === null) {
311 throw new ResponseException(BAD_REQUEST,
312 "Data has bad format.\n\"" + nodeBuilder.localName + "\" does not exist in yang schema.");
314 var validQName = schema.QName
315 var currentAugment = previousAugment;
316 if (schema.augmenting) {
317 currentAugment = schema.QName
318 } else if (previousAugment !== null && schema.QName.namespace !== previousAugment.namespace) {
319 validQName = QName.create(currentAugment, schema.QName.localName);
321 var String moduleName = null;
322 if (mountPoint === null) {
323 moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
325 moduleName = mountPoint.findModuleByNamespace(validQName.namespace)?.name
327 if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
328 nodeBuilder.namespace.toString == moduleName) {
329 nodeBuilder.qname = validQName
331 throw new ResponseException(BAD_REQUEST,
332 "Data has bad format.\nIf data is in XML format then namespace for \"" + nodeBuilder.localName +
333 "\" should be \"" + schema.QName.namespace + "\".\nIf data is in Json format then module name for \"" +
334 nodeBuilder.localName + "\" should be \"" + moduleName + "\".");
337 if (nodeBuilder instanceof CompositeNodeWrapper) {
338 val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
339 for (child : children) {
341 findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes),
342 currentAugment, mountPoint)
344 if(schema instanceof ListSchemaNode) {
345 val listKeys = (schema as ListSchemaNode).keyDefinition
346 for (listKey : listKeys) {
348 for (child : children) {
349 if (child.unwrap.nodeType.localName == listKey.localName) {
354 throw new ResponseException(BAD_REQUEST,
355 "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName + "\"")
359 } else if (nodeBuilder instanceof SimpleNodeWrapper) {
360 val simpleNode = (nodeBuilder as SimpleNodeWrapper)
361 val value = simpleNode.value
362 var inputValue = value;
364 if (schema.typeDefinition instanceof IdentityrefTypeDefinition) {
365 if (value instanceof String) {
366 inputValue = new IdentityValuesDTO(validQName.namespace.toString, value as String, null)
367 } // else value is instance of ValuesDTO
370 val outputValue = RestCodec.from(schema.typeDefinition)?.deserialize(inputValue);
371 simpleNode.setValue(outputValue)
372 } else if (nodeBuilder instanceof EmptyNodeWrapper) {
373 val emptyNodeBuilder = nodeBuilder as EmptyNodeWrapper
374 if (schema instanceof LeafSchemaNode) {
375 emptyNodeBuilder.setComposite(false);
376 } else if (schema instanceof ContainerSchemaNode) {
378 // FIXME: Add presence check
379 emptyNodeBuilder.setComposite(true);
384 private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
385 var baseType = node.type
386 while (baseType.baseType !== null) {
387 baseType = baseType.baseType;
392 private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
393 var TypeDefinition<?> baseType = node.type
394 while (baseType.baseType !== null) {
395 baseType = baseType.baseType;
400 private def DataSchemaNode findFirstSchemaByLocalName(String localName, Set<DataSchemaNode> schemas) {
401 for (schema : schemas) {
402 if (schema instanceof ChoiceNode) {
403 for (caze : (schema as ChoiceNode).cases) {
404 val result = findFirstSchemaByLocalName(localName, caze.childNodes)
405 if (result !== null) {
410 val result = schemas.findFirst[n|n.QName.localName.equals(localName)]
411 if (result !== null) {