Bug fixes for netconf southbound plugin.
[controller.git] / opendaylight / md-sal / sal-netconf-connector / src / main / java / org / opendaylight / controller / sal / connect / netconf / NetconfMapping.xtend
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.sal.connect.netconf
9
10 import com.google.common.base.Optional
11 import com.google.common.base.Preconditions
12 import com.google.common.collect.ImmutableList
13 import java.net.URI
14 import java.util.ArrayList
15 import java.util.Collections
16 import java.util.List
17 import java.util.Set
18 import java.util.concurrent.atomic.AtomicInteger
19 import org.opendaylight.controller.sal.common.util.Rpcs
20 import org.opendaylight.yangtools.yang.data.api.CompositeNode
21 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
22 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
23 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument
24 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
25 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode
26 import java.util.Collections
27 import java.util.List
28 import java.util.Set
29 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
30 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates
31 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument
32 import org.opendaylight.yangtools.yang.data.api.Node
33 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
34 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils
35 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext
37 import org.w3c.dom.Document
38 import org.w3c.dom.Element
39 import org.opendaylight.yangtools.yang.common.QName
40 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
41 import org.opendaylight.controller.netconf.api.NetconfMessage
42 import org.opendaylight.yangtools.yang.common.RpcResult
43
44 class NetconfMapping {
45
46     public static val NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0")
47     public static val NETCONF_MONITORING_URI = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
48     public static val NETCONF_NOTIFICATION_URI = URI.create("urn:ietf:params:xml:ns:netconf:notification:1.0")
49     
50     
51     public static val NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
52     public static val NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
53     public static val NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
54     public static val NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
55     public static val NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
56     public static val NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
57     public static val NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
58     public static val NETCONF_DELETE_CONFIG_QNAME = QName.create(NETCONF_QNAME, "delete-config");
59     public static val NETCONF_OPERATION_QNAME = QName.create(NETCONF_QNAME, "operation");
60     public static val NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit");
61     
62     public static val NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config");
63     public static val NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
64     public static val NETCONF_TARGET_QNAME = QName.create(NETCONF_QNAME, "target");
65     
66     public static val NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate");
67     public static val NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
68     
69     
70     public static val NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
71     public static val NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok");
72     public static val NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
73     public static val NETCONF_CREATE_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI,null,"create-subscription");
74     public static val NETCONF_CANCEL_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI,null,"cancel-subscription");
75     public static val IETF_NETCONF_MONITORING_MODULE = QName.create(NETCONF_MONITORING_URI, "2010-10-04","ietf-netconf-monitoring");
76
77     static List<Node<?>> RUNNING = Collections.<Node<?>>singletonList(
78         new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME, null, null));
79     public static val CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME, null, RUNNING);
80
81     static val messageId = new AtomicInteger(0);
82
83     static def Node<?> toFilterStructure(InstanceIdentifier identifier) {
84         var Node<?> previous = null;
85         if(identifier.path.empty) {
86             return null;
87         }
88         
89         for (component : identifier.path.reverseView) {
90             val Node<?> current = component.toNode(previous);
91             previous = current;
92         }
93         return filter("subtree",previous);
94     }
95
96     static def dispatch Node<?> toNode(NodeIdentifierWithPredicates argument, Node<?> node) {
97         val list = new ArrayList<Node<?>>();
98         for (arg : argument.keyValues.entrySet) {
99             list.add = new SimpleNodeTOImpl(arg.key, null, arg.value);
100         }
101         if (node != null) {
102             list.add(node);
103         }
104         return new CompositeNodeTOImpl(argument.nodeType, null, list)
105     }
106
107     static def dispatch Node<?> toNode(PathArgument argument, Node<?> node) {
108         if (node != null) {
109             return new CompositeNodeTOImpl(argument.nodeType, null, Collections.singletonList(node));
110         } else {
111             return new SimpleNodeTOImpl(argument.nodeType, null, null);
112         }
113     }
114
115     static def CompositeNode toCompositeNode(NetconfMessage message,Optional<SchemaContext> ctx) {
116         //TODO: implement general normalization to normalize incoming Netconf Message 
117         // for Schema Context counterpart
118         return null
119     }
120     
121     static def CompositeNode toNotificationNode(NetconfMessage message,Optional<SchemaContext> ctx) {
122         if (ctx.present) {
123             val schemaContext = ctx.get
124             val notifications = schemaContext.notifications
125             val document = message.document
126             return XmlDocumentUtils.notificationToDomNodes(document, Optional.<Set<NotificationDefinition>>fromNullable(notifications))
127         }
128         return null
129     }
130
131     static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node,Optional<SchemaContext> ctx) {
132         val rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node))
133         val w3cPayload = XmlDocumentUtils.toDocument(rpcPayload, XmlDocumentUtils.defaultValueCodecProvider)
134         w3cPayload.documentElement.setAttribute("message-id", "m-" + messageId.andIncrement)
135         return new NetconfMessage(w3cPayload);
136     }
137     
138     def static flattenInput(CompositeNode node) {
139         val inputQName = QName.create(node.nodeType,"input");
140         val input = node.getFirstCompositeByName(inputQName);
141         if(input == null) return node;
142         if(input instanceof CompositeNode) {
143             
144             val nodes = ImmutableList.builder() //
145                 .addAll(input.children) //
146                 .addAll(node.children.filter[nodeType != inputQName]) //
147                 .build()
148             return ImmutableCompositeNode.create(node.nodeType,nodes);
149         } 
150         
151     }
152
153     static def RpcResult<CompositeNode> toRpcResult(NetconfMessage message,QName rpc,Optional<SchemaContext> context) {
154         var CompositeNode rawRpc;
155         if(context.present) {
156             if(isDataRetrievalReply(rpc)) {
157                 
158                 val xmlData = message.document.dataSubtree
159                 val dataNodes = XmlDocumentUtils.toDomNodes(xmlData, Optional.of(context.get.dataDefinitions))
160                 
161                 val it = ImmutableCompositeNode.builder()
162                 setQName(NETCONF_RPC_REPLY_QNAME)
163                 add(ImmutableCompositeNode.create(NETCONF_DATA_QNAME, dataNodes));
164                 
165                 rawRpc = it.toInstance;
166                 //sys(xmlData)
167             } else {
168                 val rpcSchema = context.get.operations.findFirst[QName == rpc]
169                 rawRpc = message.document.toCompositeNode() as CompositeNode;
170             }
171             
172             
173             
174         } else {
175             rawRpc = message.document.toCompositeNode() as CompositeNode;
176         }
177         //rawRpc.
178         return Rpcs.getRpcResult(true, rawRpc, Collections.emptySet());
179     }
180     
181     def static Element getDataSubtree(Document doc) {
182         doc.getElementsByTagNameNS(NETCONF_URI.toString,"data").item(0) as Element
183     }
184     
185     def static boolean isDataRetrievalReply(QName it) {
186         return NETCONF_URI == namespace && ( localName == NETCONF_GET_CONFIG_QNAME.localName || localName == NETCONF_GET_QNAME.localName) 
187     }
188
189     static def wrap(QName name, Node<?> node) {
190         if (node != null) {
191             return new CompositeNodeTOImpl(name, null, Collections.singletonList(node));
192         } else {
193             return new CompositeNodeTOImpl(name, null, Collections.emptyList());
194         }
195     }
196
197     static def wrap(QName name, Node<?> additional, Node<?> node) {
198         if (node != null) {
199             return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node));
200         } else {
201             return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional));
202         }
203     }
204
205     static def filter(String type, Node<?> node) {
206         val it = ImmutableCompositeNode.builder(); //
207         setQName(NETCONF_FILTER_QNAME);
208         setAttribute(NETCONF_TYPE_QNAME,type);
209         if (node != null) {
210             return add(node).toInstance();
211         } else {
212             return toInstance();
213         }
214     }
215
216     public static def Node<?> toCompositeNode(Document document) {
217         return XmlDocumentUtils.toDomNode(document) as Node<?>
218     }
219     
220     public static def checkValidReply(NetconfMessage input, NetconfMessage output) {
221         val inputMsgId = input.document.documentElement.getAttribute("message-id")
222         val outputMsgId = output.document.documentElement.getAttribute("message-id")
223         Preconditions.checkState(inputMsgId == outputMsgId,"Rpc request and reply message IDs must be same.");
224         
225     }
226     
227 }