Merge "On openflow plugin restart, NPE in tx poller"
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / operations / editconfig / EditConfig.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.editconfig;
10
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.collect.Maps;
13 import com.google.common.collect.Multimap;
14 import org.opendaylight.controller.config.api.JmxAttributeValidationException;
15 import org.opendaylight.controller.config.api.ValidationException;
16 import org.opendaylight.controller.config.util.ConfigRegistryClient;
17 import org.opendaylight.controller.config.util.ConfigTransactionClient;
18 import org.opendaylight.controller.config.yang.store.api.YangStoreSnapshot;
19 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
20 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
21 import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity;
22 import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag;
23 import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType;
24 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Config;
25 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfig;
26 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.InstanceConfigElementResolved;
27 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleConfig;
28 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.ModuleElementResolved;
29 import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services;
30 import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
31 import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfigXmlParser.EditConfigExecution;
32 import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider;
33 import org.opendaylight.controller.netconf.util.xml.XmlElement;
34 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.w3c.dom.Document;
38 import org.w3c.dom.Element;
39
40 import javax.management.InstanceNotFoundException;
41 import javax.management.ObjectName;
42 import java.util.HashMap;
43 import java.util.Map;
44 import java.util.Map.Entry;
45
46 public class EditConfig extends AbstractConfigNetconfOperation {
47
48     private static final Logger logger = LoggerFactory.getLogger(EditConfig.class);
49
50     private final YangStoreSnapshot yangStoreSnapshot;
51
52     private final TransactionProvider transactionProvider;
53     private EditConfigXmlParser editConfigXmlParser;
54
55     public EditConfig(YangStoreSnapshot yangStoreSnapshot, TransactionProvider transactionProvider,
56             ConfigRegistryClient configRegistryClient, String netconfSessionIdForReporting) {
57         super(configRegistryClient, netconfSessionIdForReporting);
58         this.yangStoreSnapshot = yangStoreSnapshot;
59         this.transactionProvider = transactionProvider;
60         this.editConfigXmlParser = new EditConfigXmlParser();
61     }
62
63     @VisibleForTesting
64     Element getResponseInternal(final Document document,
65             final EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
66
67         if (editConfigExecution.shouldTest()) {
68             executeTests(configRegistryClient, editConfigExecution);
69         }
70
71         if (editConfigExecution.shouldSet()) {
72             executeSet(configRegistryClient, editConfigExecution);
73         }
74
75         logger.info("Operation {} successful", EditConfigXmlParser.EDIT_CONFIG);
76
77         return document.createElement(XmlNetconfConstants.OK);
78     }
79
80     private void executeSet(ConfigRegistryClient configRegistryClient,
81             EditConfigXmlParser.EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
82         try {
83             set(configRegistryClient, editConfigExecution);
84         } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) {
85             logger.warn("Set phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e);
86             final Map<String, String> errorInfo = new HashMap<>();
87             errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage());
88             throw new NetconfDocumentedException("Test phase: " + e.getMessage(), e, ErrorType.application,
89                     ErrorTag.operation_failed, ErrorSeverity.error, errorInfo);
90         }
91         logger.debug("Set phase for {} operation successful", EditConfigXmlParser.EDIT_CONFIG);
92     }
93
94     private void executeTests(ConfigRegistryClient configRegistryClient,
95             EditConfigExecution editConfigExecution) throws NetconfDocumentedException {
96         try {
97             test(configRegistryClient, editConfigExecution, editConfigExecution.getDefaultStrategy());
98         } catch (IllegalStateException | JmxAttributeValidationException | ValidationException e) {
99             logger.warn("Test phase for {} failed", EditConfigXmlParser.EDIT_CONFIG, e);
100             final Map<String, String> errorInfo = new HashMap<>();
101             errorInfo.put(ErrorTag.operation_failed.name(), e.getMessage());
102             throw new NetconfDocumentedException("Test phase: " + e.getMessage(), e, ErrorType.application,
103                     ErrorTag.operation_failed, ErrorSeverity.error, errorInfo);
104         }
105         logger.debug("Test phase for {} operation successful", EditConfigXmlParser.EDIT_CONFIG);
106     }
107
108     private void test(ConfigRegistryClient configRegistryClient,
109                       EditConfigExecution execution, EditStrategyType editStrategyType) {
110         ObjectName taON = transactionProvider.getTestTransaction();
111         try {
112
113             // default strategy = replace wipes config
114             if (editStrategyType == EditStrategyType.replace) {
115                 transactionProvider.wipeTestTransaction(taON);
116             }
117
118             setOnTransaction(configRegistryClient, execution.getResolvedXmlElements(), execution.getServices(), taON);
119             // TODO add service reference persistance testing here
120             transactionProvider.validateTestTransaction(taON);
121         } finally {
122             transactionProvider.abortTestTransaction(taON);
123         }
124     }
125
126     private void set(ConfigRegistryClient configRegistryClient,
127             EditConfigXmlParser.EditConfigExecution editConfigExecution) {
128         ObjectName taON = transactionProvider.getOrCreateTransaction();
129
130         // default strategy = replace wipes config
131         if (editConfigExecution.getDefaultStrategy() == EditStrategyType.replace) {
132             transactionProvider.wipeTransaction();
133         }
134
135         setOnTransaction(configRegistryClient, editConfigExecution.getResolvedXmlElements(),
136                 editConfigExecution.getServices(), taON);
137         setServicesOnTransaction(configRegistryClient, editConfigExecution.getServices(), taON);
138     }
139
140     private void setServicesOnTransaction(ConfigRegistryClient configRegistryClient, Services services,
141                 ObjectName taON) {
142         ConfigTransactionClient ta = configRegistryClient.getConfigTransactionClient(taON);
143
144         Map<String, Map<String, Map<String, Services.ServiceInstance>>> namespaceToServiceNameToRefNameToInstance = services
145                 .getNamespaceToServiceNameToRefNameToInstance();
146
147         for (String serviceNamespace : namespaceToServiceNameToRefNameToInstance.keySet()) {
148             for (String serviceName : namespaceToServiceNameToRefNameToInstance.get(serviceNamespace).keySet()) {
149
150                 String qnameOfService = getQname(ta, serviceNamespace, serviceName);
151                 Map<String, Services.ServiceInstance> refNameToInstance = namespaceToServiceNameToRefNameToInstance
152                         .get(serviceNamespace).get(serviceName);
153
154                 for (String refName : refNameToInstance.keySet()) {
155                     ObjectName on = refNameToInstance.get(refName).getObjectName(ta.getTransactionName());
156                     // TODO check for duplicates
157                     try {
158                         ta.saveServiceReference(qnameOfService, refName, on);
159                     } catch (InstanceNotFoundException e) {
160                         throw new IllegalStateException("Unable to save ref name " + refName + " for instance " + on, e);
161                     }
162                 }
163             }
164         }
165     }
166
167     private String getQname(ConfigTransactionClient ta, String namespace, String serviceName) {
168         return ta.getServiceInterfaceName(namespace, serviceName);
169     }
170
171     private void setOnTransaction(ConfigRegistryClient configRegistryClient,
172                                   Map<String, Multimap<String, ModuleElementResolved>> resolvedXmlElements, Services services, ObjectName taON) {
173         ConfigTransactionClient ta = configRegistryClient.getConfigTransactionClient(taON);
174
175         for (Multimap<String, ModuleElementResolved> modulesToResolved : resolvedXmlElements.values()) {
176             for (Entry<String, ModuleElementResolved> moduleToResolved : modulesToResolved.entries()) {
177                 String moduleName = moduleToResolved.getKey();
178
179                 ModuleElementResolved moduleElementResolved = moduleToResolved.getValue();
180                 String instanceName = moduleElementResolved.getInstanceName();
181
182                 InstanceConfigElementResolved ice = moduleElementResolved.getInstanceConfigElementResolved();
183                 EditConfigStrategy strategy = ice.getEditStrategy();
184                 strategy.executeConfiguration(moduleName, instanceName, ice.getConfiguration(), ta, services);
185             }
186         }
187     }
188
189     public static Config getConfigMapping(ConfigRegistryClient configRegistryClient,
190             Map<String/* Namespace from yang file */,
191                     Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> mBeanEntries) {
192         Map<String, Map<String, ModuleConfig>> factories = transform(configRegistryClient, mBeanEntries);
193
194         return new Config(factories);
195     }
196
197     // TODO refactor
198     private static Map<String/* Namespace from yang file */,
199             Map<String /* Name of module entry from yang file */, ModuleConfig>> transform
200     (final ConfigRegistryClient configRegistryClient, Map<String/* Namespace from yang file */,
201                     Map<String /* Name of module entry from yang file */, ModuleMXBeanEntry>> mBeanEntries) {
202         return Maps.transformEntries(mBeanEntries,
203                 new Maps.EntryTransformer<String, Map<String, ModuleMXBeanEntry>, Map<String, ModuleConfig>>() {
204
205                     @Override
206                     public Map<String, ModuleConfig> transformEntry(String arg0, Map<String, ModuleMXBeanEntry> arg1) {
207                         return Maps.transformEntries(arg1,
208                                 new Maps.EntryTransformer<String, ModuleMXBeanEntry, ModuleConfig>() {
209
210                                     @Override
211                                     public ModuleConfig transformEntry(String key, ModuleMXBeanEntry moduleMXBeanEntry) {
212                                         return new ModuleConfig(key, new InstanceConfig(configRegistryClient, moduleMXBeanEntry
213                                                 .getAttributes()), moduleMXBeanEntry.getProvidedServices().values());
214                                     }
215                                 });
216                     }
217                 });
218     }
219
220     @Override
221     protected String getOperationName() {
222         return EditConfigXmlParser.EDIT_CONFIG;
223     }
224
225     @Override
226     protected Element handle(Document document, XmlElement xml) throws NetconfDocumentedException {
227
228         EditConfigXmlParser.EditConfigExecution editConfigExecution;
229         Config cfg = getConfigMapping(configRegistryClient, yangStoreSnapshot.getModuleMXBeanEntryMap());
230         try {
231             editConfigExecution = editConfigXmlParser.fromXml(xml, cfg, transactionProvider, configRegistryClient);
232         } catch (IllegalStateException e) {
233             logger.warn("Error parsing xml", e);
234             final Map<String, String> errorInfo = new HashMap<>();
235             errorInfo.put(ErrorTag.missing_attribute.name(), "Error parsing xml: " + e.getMessage());
236             throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.missing_attribute,
237                     ErrorSeverity.error, errorInfo);
238         } catch (final IllegalArgumentException e) {
239             logger.warn("Error parsing xml", e);
240             final Map<String, String> errorInfo = new HashMap<>();
241             errorInfo.put(ErrorTag.bad_attribute.name(), e.getMessage());
242             throw new NetconfDocumentedException(e.getMessage(), ErrorType.rpc, ErrorTag.bad_attribute,
243                     ErrorSeverity.error, errorInfo);
244         } catch (final UnsupportedOperationException e) {
245             logger.warn("Unsupported", e);
246             final Map<String, String> errorInfo = new HashMap<>();
247             errorInfo.put(ErrorTag.operation_not_supported.name(), "Unsupported option for 'edit-config'");
248             throw new NetconfDocumentedException(e.getMessage(), ErrorType.application,
249                     ErrorTag.operation_not_supported, ErrorSeverity.error, errorInfo);
250         }
251
252         return getResponseInternal(document, editConfigExecution);
253     }
254 }