7bdfa277a00418d39b5724267947fcf7e4446a81
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / operations / runtimerpc / RuntimeRpc.java
1 /*
2  * Copyright (c) 2013 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
9 package org.opendaylight.controller.netconf.confignetconfconnector.operations.runtimerpc;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.Maps;
14 import org.opendaylight.controller.config.util.ConfigRegistryClient;
15 import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
16 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
17 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
18 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
19 import org.opendaylight.controller.config.yangjmxgenerator.attribute.SimpleTypeResolver;
20 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
21 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.fromxml.AttributeConfigElement;
22 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping.SimpleAttributeMappingStrategy;
23 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.InstanceRuntimeRpc;
24 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.ModuleRpcs;
25 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.rpc.Rpcs;
26 import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
27 import org.opendaylight.controller.netconf.confignetconfconnector.operations.Commit;
28 import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
29 import org.opendaylight.controller.netconf.util.xml.XmlElement;
30 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33 import org.w3c.dom.Document;
34 import org.w3c.dom.Element;
35
36 import javax.management.ObjectName;
37 import javax.management.openmbean.SimpleType;
38 import java.util.Map;
39
40 public class RuntimeRpc extends AbstractConfigNetconfOperation {
41
42     private static final Logger logger = LoggerFactory.getLogger(Commit.class);
43     public static final String CONTEXT_INSTANCE = "context-instance";
44
45     private final YangStoreSnapshot yangStoreSnapshot;
46
47     public RuntimeRpc(final YangStoreSnapshot yangStoreSnapshot, ConfigRegistryClient configRegistryClient,
48             String netconfSessionIdForReporting) {
49         super(configRegistryClient, netconfSessionIdForReporting);
50         this.yangStoreSnapshot = yangStoreSnapshot;
51     }
52
53     private String getStringRepresentation(final Object result) {
54         final SimpleType<?> simpleType = SimpleTypeResolver.getSimpleType(result.getClass().getName());
55         final Optional<String> mappedAttributeOpt = new SimpleAttributeMappingStrategy(simpleType).mapAttribute(result);
56         return mappedAttributeOpt.isPresent() ? mappedAttributeOpt.get() : "";
57     }
58
59     private Object executeOperation(final ConfigRegistryClient configRegistryClient, final ObjectName on,
60             final String name, final Map<String, AttributeConfigElement> attributes) {
61         final Object[] params = new Object[attributes.size()];
62         final String[] signature = new String[attributes.size()];
63
64         int i = 0;
65         for (final String attrName : attributes.keySet()) {
66             final AttributeConfigElement attribute = attributes.get(attrName);
67             final Optional<?> resolvedValueOpt = attribute.getResolvedValue();
68
69             params[i] = resolvedValueOpt.isPresent() ? resolvedValueOpt.get() : attribute.getResolvedDefaultValue();
70             signature[i] = resolvedValueOpt.isPresent() ? resolvedValueOpt.get().getClass().getName() : attribute
71                     .getResolvedDefaultValue().getClass().getName();
72             i++;
73         }
74
75         return configRegistryClient.invokeMethod(on, name, params, signature);
76     }
77
78     public NetconfOperationExecution fromXml(final XmlElement xml) throws NetconfDocumentedException {
79         final String namespace = xml.getNamespace();
80         final XmlElement contextInstanceElement = xml.getOnlyChildElement(CONTEXT_INSTANCE);
81         final String operationName = xml.getName();
82
83         final RuntimeRpcElementResolved id = RuntimeRpcElementResolved.fromXpath(
84                 contextInstanceElement.getTextContent(), operationName, namespace);
85
86         final Rpcs rpcs = mapRpcs(yangStoreSnapshot.getModuleMXBeanEntryMap());
87
88         final ModuleRpcs rpcMapping = rpcs.getRpcMapping(id);
89         final InstanceRuntimeRpc instanceRuntimeRpc = rpcMapping.getRpc(id.getRuntimeBeanName(), operationName);
90
91         // TODO move to Rpcs after xpath attribute is redesigned
92
93         final ObjectName on = id.getObjectName(rpcMapping);
94         Map<String, AttributeConfigElement> attributes = instanceRuntimeRpc.fromXml(xml);
95         attributes = sortAttributes(attributes, xml);
96
97         return new NetconfOperationExecution(on, instanceRuntimeRpc.getName(), attributes,
98                 instanceRuntimeRpc.getReturnType(), namespace);
99     }
100
101     @Override
102     public HandlingPriority canHandle(Document message) {
103         XmlElement requestElement = getRequestElementWithCheck(message);
104
105         XmlElement operationElement = requestElement.getOnlyChildElement();
106         final String netconfOperationName = operationElement.getName();
107         final String netconfOperationNamespace = operationElement.getNamespace();
108
109         final Optional<XmlElement> contextInstanceElement = operationElement
110                 .getOnlyChildElementOptionally(CONTEXT_INSTANCE);
111
112         if (contextInstanceElement.isPresent() == false)
113             return HandlingPriority.CANNOT_HANDLE;
114
115         final RuntimeRpcElementResolved id = RuntimeRpcElementResolved.fromXpath(contextInstanceElement.get()
116                 .getTextContent(), netconfOperationName, netconfOperationNamespace);
117
118         // TODO reuse rpcs instance in fromXml method
119         final Rpcs rpcs = mapRpcs(yangStoreSnapshot.getModuleMXBeanEntryMap());
120
121         try {
122
123             final ModuleRpcs rpcMapping = rpcs.getRpcMapping(id);
124             final InstanceRuntimeRpc instanceRuntimeRpc = rpcMapping.getRpc(id.getRuntimeBeanName(),
125                     netconfOperationName);
126             Preconditions.checkState(instanceRuntimeRpc != null, "No rpc found for %s:%s", netconfOperationNamespace,
127                     netconfOperationName);
128
129         } catch (IllegalStateException e) {
130             logger.debug("Cannot handle runtime operation {}:{}", netconfOperationNamespace, netconfOperationName, e);
131             return HandlingPriority.CANNOT_HANDLE;
132         }
133
134         return HandlingPriority.HANDLE_WITH_DEFAULT_PRIORITY;
135     }
136
137     @Override
138     protected HandlingPriority canHandle(String netconfOperationName, String namespace) {
139         throw new UnsupportedOperationException(
140                 "This should not be used since it is not possible to provide check with these attributes");
141     }
142
143     @Override
144     protected String getOperationName() {
145         throw new UnsupportedOperationException("Runtime rpc does not have a stable name");
146     }
147
148     @Override
149     protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
150
151         // TODO exception handling
152         // TODO check for namespaces and unknown elements
153
154         final NetconfOperationExecution execution = fromXml(xml);
155
156         logger.debug("Invoking operation {} on {} with arguments {}", execution.operationName, execution.on,
157                 execution.attributes);
158         final Object result = executeOperation(configRegistryClient, execution.on, execution.operationName,
159                 execution.attributes);
160
161         logger.info("Operation {} called successfully on {} with arguments {} with result {}", execution.operationName,
162                 execution.on, execution.attributes, result);
163
164         if (execution.returnType.equals("void")) {
165             return document.createElement("ok");
166         } else {
167             final Element output = XmlUtil.createTextElement(document, "result", getStringRepresentation(result));
168             XmlUtil.addNamespaceAttr(output, execution.namespace);
169             return output;
170         }
171     }
172
173     private static class NetconfOperationExecution {
174
175         private final ObjectName on;
176         private final String operationName;
177         private final Map<String, AttributeConfigElement> attributes;
178         private final String returnType;
179         private final String namespace;
180
181         public NetconfOperationExecution(final ObjectName on, final String name,
182                 final Map<String, AttributeConfigElement> attributes, final String returnType, final String namespace) {
183             this.on = on;
184             this.operationName = name;
185             this.attributes = attributes;
186             this.returnType = returnType;
187             this.namespace = namespace;
188         }
189
190     }
191
192     private static Map<String, AttributeConfigElement> sortAttributes(
193             final Map<String, AttributeConfigElement> attributes, final XmlElement xml) {
194         final Map<String, AttributeConfigElement> sorted = Maps.newLinkedHashMap();
195
196         for (XmlElement xmlElement : xml.getChildElements()) {
197             final String name = xmlElement.getName();
198             if (CONTEXT_INSTANCE.equals(name) == false) { // skip context
199                                                           // instance child node
200                                                           // because it
201                                                           // specifies
202                 // ObjectName
203                 final AttributeConfigElement value = attributes.get(name);
204                 if (value == null) {
205                     throw new IllegalArgumentException("Cannot find yang mapping for node " + xmlElement);
206                 }
207                 sorted.put(name, value);
208             }
209         }
210
211         return sorted;
212     }
213
214     private static Rpcs mapRpcs(final Map<String, Map<String, ModuleMXBeanEntry>> mBeanEntries) {
215
216         final Map<String, Map<String, ModuleRpcs>> map = Maps.newHashMap();
217
218         for (final String namespace : mBeanEntries.keySet()) {
219
220             Map<String, ModuleRpcs> namespaceToModules = map.get(namespace);
221             if (namespaceToModules == null) {
222                 namespaceToModules = Maps.newHashMap();
223                 map.put(namespace, namespaceToModules);
224             }
225
226             for (final String moduleName : mBeanEntries.get(namespace).keySet()) {
227
228                 ModuleRpcs rpcMapping = namespaceToModules.get(moduleName);
229                 if (rpcMapping == null) {
230                     rpcMapping = new ModuleRpcs();
231                     namespaceToModules.put(moduleName, rpcMapping);
232                 }
233
234                 final ModuleMXBeanEntry entry = mBeanEntries.get(namespace).get(moduleName);
235
236                 for (final RuntimeBeanEntry runtimeEntry : entry.getRuntimeBeans()) {
237                     rpcMapping.addNameMapping(runtimeEntry);
238                     for (final Rpc rpc : runtimeEntry.getRpcs()) {
239                         rpcMapping.addRpc(runtimeEntry, rpc);
240                     }
241                 }
242             }
243         }
244
245         return new Rpcs(map);
246     }
247
248 }