0f53e56b8493560b2ff82ca773c04b4cce158ebf
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / RestconfImpl.xtend
1 package org.opendaylight.controller.sal.restconf.impl
2
3 import com.google.common.base.Preconditions
4 import java.net.URI
5 import java.util.ArrayList
6 import java.util.HashMap
7 import java.util.List
8 import java.util.Set
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
31
32 import static javax.ws.rs.core.Response.Status.*
33
34 class RestconfImpl implements RestconfService {
35
36     val static RestconfImpl INSTANCE = new RestconfImpl
37
38     @Property
39     BrokerFacade broker
40
41     @Property
42     extension ControllerContext controllerContext
43
44     private new() {
45         if (INSTANCE !== null) {
46             throw new IllegalStateException("Already instantiated");
47         }
48     }
49
50     static def getInstance() {
51         return INSTANCE
52     }
53
54     override readAllData() {
55 //        return broker.readOperationalData("".toInstanceIdentifier.getInstanceIdentifier);
56         throw new UnsupportedOperationException("Reading all data is currently not supported.")
57     }
58
59     override getModules() {
60         throw new UnsupportedOperationException("TODO: auto-generated method stub")
61     }
62
63     override getRoot() {
64         return null;
65     }
66
67     override invokeRpc(String identifier, CompositeNode payload) {
68         return callRpc(identifier.rpcDefinition, payload)
69     }
70     
71     override invokeRpc(String identifier) {
72         return callRpc(identifier.rpcDefinition, null)
73     }
74     
75     private def StructuredData callRpc(RpcDefinition rpc, CompositeNode payload) {
76         if (rpc === null) {
77             throw new ResponseException(NOT_FOUND, "RPC does not exist.");
78         }
79         var CompositeNode rpcRequest;
80         if (payload === null) {
81             rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, null, null, null)
82         } else {
83             val value = normalizeNode(payload, rpc.input, null)
84             val List<Node<?>> input = new ArrayList
85             input.add(value)
86             rpcRequest = NodeFactory.createMutableCompositeNode(rpc.QName, null, input, null, null)
87         }
88         val rpcResult = broker.invokeRpc(rpc.QName, rpcRequest);
89         if (!rpcResult.successful) {
90             throw new ResponseException(INTERNAL_SERVER_ERROR, "Operation failed")
91         }
92         if (rpcResult.result === null) {
93             return null
94         }
95         return new StructuredData(rpcResult.result, rpc.output, null)
96     }
97
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)
103         } else {
104             data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
105         }
106         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
107     }
108
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)
114         } else {
115             data = broker.readConfigurationData(iiWithData.getInstanceIdentifier);
116         }
117         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
118     }
119
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)
125         } else {
126             data = broker.readOperationalData(iiWithData.getInstanceIdentifier);
127         }
128         return new StructuredData(data, iiWithData.schemaNode, iiWithData.mountPoint)
129     }
130
131     override updateConfigurationDataLegacy(String identifier, CompositeNode payload) {
132         updateConfigurationData(identifier, payload);
133     }
134
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()
142         } else {
143             status = broker.commitConfigurationDataPut(iiWithData.instanceIdentifier, value).get();
144         }
145         switch status.result {
146             case TransactionStatus.COMMITED: Response.status(OK).build
147             default: Response.status(INTERNAL_SERVER_ERROR).build
148         }
149     }
150
151     override createConfigurationDataLegacy(String identifier, CompositeNode payload) {
152         createConfigurationData(identifier, payload);
153     }
154
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)");
159         }
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();
168         } else {
169             status = broker.commitConfigurationDataPost(completeInstIdWithData.instanceIdentifier, value)?.get();
170         }
171         if (status === null) {
172             return Response.status(ACCEPTED).build
173         }
174         switch status.result {
175             case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
176             default: Response.status(INTERNAL_SERVER_ERROR).build
177         }
178     }
179     
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)");
184         }
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();
192         } else {
193             status = broker.commitConfigurationDataPost(iiWithData.instanceIdentifier, value)?.get();
194         }
195         if (status === null) {
196             return Response.status(ACCEPTED).build
197         }
198         switch status.result {
199             case TransactionStatus.COMMITED: Response.status(NO_CONTENT).build
200             default: Response.status(INTERNAL_SERVER_ERROR).build
201         }
202     }
203     
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;
210         } else {
211             status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier).get;
212         }
213         switch status.result {
214             case TransactionStatus.COMMITED: Response.status(OK).build
215             default: Response.status(INTERNAL_SERVER_ERROR).build
216         }
217     }
218     
219     private def dispatch URI namespace(CompositeNode data) {
220         return data.nodeType.namespace
221     }
222     
223     private def dispatch URI namespace(CompositeNodeWrapper data) {
224         return data.namespace
225     }
226
227     private def dispatch Module findModule(MountInstance mountPoint, CompositeNode data) {
228         if (mountPoint !== null) {
229             return mountPoint.findModuleByNamespace(data.nodeType.namespace)
230         } else {
231             return findModuleByNamespace(data.nodeType.namespace)
232         }
233     }
234
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
242             }
243         } else {
244             module = data.namespace.findModuleByNamespace // namespace from XML
245             if (module === null) {
246                 module = data.namespace.toString.findModuleByName // namespace (module name) from JSON
247             }
248         }
249         return module
250     }
251     
252     private def dispatch DataSchemaNode getSchemaChildNode(DataNodeContainer parentSchemaNode, CompositeNode data) {
253         return parentSchemaNode?.getDataChildByName(data.nodeType.localName)
254     }
255     
256     private def dispatch DataSchemaNode getSchemaChildNode(DataNodeContainer parentSchemaNode, CompositeNodeWrapper data) {
257         return parentSchemaNode?.getDataChildByName(data.localName)
258     }
259
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
266         } else {
267             iiBuilder = InstanceIdentifier.builder(iiOriginal)
268         }
269
270         if (schemaOfData instanceof ListSchemaNode) {
271             iiBuilder.nodeWithKey(schemaOfData.QName, (schemaOfData as ListSchemaNode).resolveKeysFromData(data))
272         } else {
273             iiBuilder.node(schemaOfData.QName)
274         }
275         return new InstanceIdWithSchemaNode(iiBuilder.toInstance, schemaOfData, identifierWithSchemaNode?.mountPoint)
276     }
277
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 + "\"")
286             }
287             keyValues.put(key, dataNodeKeyValueObject);
288         }
289         return keyValues
290     }
291
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)
295         }
296         if (!(schema instanceof DataNodeContainer)) {
297             throw new ResponseException(BAD_REQUEST, "Root element has to be container or list yang datatype.");
298         }
299         if (node instanceof CompositeNodeWrapper) {
300             if ((node  as CompositeNodeWrapper).changeAllowed) {
301                 normalizeNode(node as CompositeNodeWrapper, schema, null, mountPoint)
302             }
303             return (node as CompositeNodeWrapper).unwrap()
304         }
305         return node
306     }
307
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.");
313         }
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);
320         }
321         var String moduleName = null;
322         if (mountPoint === null) {
323             moduleName = controllerContext.findModuleNameByNamespace(validQName.namespace);
324         } else {
325             moduleName = controllerContext.findModuleNameByNamespace(mountPoint, validQName.namespace)
326         }
327         if (nodeBuilder.namespace === null || nodeBuilder.namespace == validQName.namespace ||
328             nodeBuilder.namespace.toString == moduleName) {
329             nodeBuilder.qname = validQName
330         } else {
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 + "\".");
335         }
336
337         if (nodeBuilder instanceof CompositeNodeWrapper) {
338             val List<NodeWrapper<?>> children = (nodeBuilder as CompositeNodeWrapper).getValues
339             for (child : children) {
340                 normalizeNode(child,
341                     findFirstSchemaByLocalName(child.localName, (schema as DataNodeContainer).childNodes),
342                     currentAugment, mountPoint)
343             }
344             if(schema instanceof ListSchemaNode) {
345                 val listKeys = (schema as ListSchemaNode).keyDefinition
346                 for (listKey : listKeys) {
347                     var foundKey = false
348                     for (child : children) {
349                         if (child.unwrap.nodeType.localName == listKey.localName) {
350                             foundKey = true;
351                         }
352                     }
353                     if (!foundKey) {
354                         throw new ResponseException(BAD_REQUEST,
355                             "Missing key in URI \"" + listKey.localName + "\" of list \"" + schema.QName.localName + "\"")
356                     }
357                 }
358             }
359         } else if (nodeBuilder instanceof SimpleNodeWrapper) {
360             val simpleNode = (nodeBuilder as SimpleNodeWrapper)
361             val value = simpleNode.value
362             var inputValue = value;
363             
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
368             }
369             
370             val outputValue = RestCodec.from(schema.typeDefinition, mountPoint)?.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) {
377
378                 // FIXME: Add presence check
379                 emptyNodeBuilder.setComposite(true);
380             }
381         }
382     }
383
384     private def dispatch TypeDefinition<?> typeDefinition(LeafSchemaNode node) {
385         var baseType = node.type
386         while (baseType.baseType !== null) {
387             baseType = baseType.baseType;
388         }
389         baseType
390     }
391
392     private def dispatch TypeDefinition<?> typeDefinition(LeafListSchemaNode node) {
393         var TypeDefinition<?> baseType = node.type
394         while (baseType.baseType !== null) {
395             baseType = baseType.baseType;
396         }
397         baseType
398     }
399
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) {
406                         return result
407                     }
408                 }
409             } else {
410                 val result = schemas.findFirst[n|n.QName.localName.equals(localName)]
411                 if (result !== null) {
412                     return result;
413
414                 }
415             }
416         }
417         return null
418     }
419
420 }