Merge "Bug 451 - Fix netconf exception handling"
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / mapping / config / Config.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.mapping.config;
10
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.lang.String.format;
13
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.Date;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22
23 import javax.management.ObjectName;
24
25 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
26 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
27 import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
28 import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType;
29 import org.opendaylight.controller.netconf.util.xml.XmlElement;
30 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
31 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.w3c.dom.Document;
35 import org.w3c.dom.Element;
36
37 import com.google.common.base.Optional;
38 import com.google.common.base.Preconditions;
39 import com.google.common.collect.HashMultimap;
40 import com.google.common.collect.Lists;
41 import com.google.common.collect.Maps;
42 import com.google.common.collect.Multimap;
43
44 public class Config {
45     private final Logger logger = LoggerFactory.getLogger(Config.class);
46
47     private final Map<String/* Namespace from yang file */,
48             Map<String /* Name of module entry from yang file */, ModuleConfig>> moduleConfigs;
49     private final Map<String, ModuleConfig> moduleNamesToConfigs;
50
51     private final Map<String, Map<Date, EditConfig.IdentityMapping>> identityMap;
52
53     public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs) {
54         this(moduleConfigs, Collections.<String, Map<Date, EditConfig.IdentityMapping>>emptyMap());
55     }
56
57     public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs, Map<String, Map<Date,EditConfig.IdentityMapping>> identityMap) {
58         this.moduleConfigs = moduleConfigs;
59         Map<String, ModuleConfig> moduleNamesToConfigs = new HashMap<>();
60         for (Entry<String, Map<String, ModuleConfig>> entry : moduleConfigs.entrySet()) {
61             moduleNamesToConfigs.putAll(entry.getValue());
62         }
63         this.moduleNamesToConfigs = Collections.unmodifiableMap(moduleNamesToConfigs);
64         this.identityMap = identityMap;
65     }
66
67     public static Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
68                                                                                 Map<String, Map<String, ModuleConfig>> configs) {
69         Multimap<String, ObjectName> moduleToInstances = mapInstancesToModules(instancesToMap);
70
71         Map<String, Map<String, Collection<ObjectName>>> retVal = Maps.newLinkedHashMap();
72
73         for (String namespace : configs.keySet()) {
74
75             Map<String, Collection<ObjectName>> innerRetVal = Maps.newHashMap();
76
77             for (Entry<String, ModuleConfig> mbeEntry : configs.get(namespace).entrySet()) {
78
79                 String moduleName = mbeEntry.getKey();
80                 Collection<ObjectName> instances = moduleToInstances.get(moduleName);
81
82                 // TODO, this code does not support same module names from different namespaces
83                 // Namespace should be present in ObjectName
84
85                 if (instances == null)
86                     continue;
87
88                 innerRetVal.put(moduleName, instances);
89
90             }
91
92             retVal.put(namespace, innerRetVal);
93         }
94         return retVal;
95     }
96
97     private static Multimap<String, ObjectName> mapInstancesToModules(Set<ObjectName> instancesToMap) {
98         Multimap<String, ObjectName> retVal = HashMultimap.create();
99
100         for (ObjectName objectName : instancesToMap) {
101             String factoryName = ObjectNameUtil.getFactoryName(objectName);
102             retVal.put(factoryName, objectName);
103         }
104         return retVal;
105     }
106
107     // public Element toXml(Set<ObjectName> instancesToMap, String namespace,
108     // Document document) {
109     // return toXml(instancesToMap, Optional.of(namespace), document);
110     // }
111
112     public Element toXml(Set<ObjectName> instancesToMap, Optional<String> maybeNamespace, Document document,
113             Element dataElement, ServiceRegistryWrapper serviceTracker) {
114
115         Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
116                 moduleConfigs);
117
118         Element root = dataElement;
119         if (maybeNamespace.isPresent()) {
120             root.setAttributeNS(maybeNamespace.get(), dataElement.getNodeName(), "xmlns");
121         }
122
123         Element modulesElement = XmlUtil.createElement(document, XmlNetconfConstants.MODULES_KEY, Optional.of(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG));
124         root.appendChild(modulesElement);
125         for (String moduleNamespace : moduleToInstances.keySet()) {
126             for (Entry<String, Collection<ObjectName>> moduleMappingEntry : moduleToInstances.get(moduleNamespace)
127                     .entrySet()) {
128
129                 ModuleConfig mapping = moduleConfigs.get(moduleNamespace).get(moduleMappingEntry.getKey());
130
131                 if (moduleMappingEntry.getValue().isEmpty()) {
132                     continue;
133                 }
134
135                 for (ObjectName objectName : moduleMappingEntry.getValue()) {
136                     modulesElement.appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace));
137                 }
138
139             }
140         }
141
142         root.appendChild(Services.toXml(serviceTracker, document));
143
144         return root;
145     }
146
147     // TODO refactor, replace string representing namespace with namespace class
148     // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
149     // class
150
151     public Map<String, Multimap<String, ModuleElementResolved>> fromXmlModulesResolved(XmlElement xml, EditStrategyType defaultEditStrategyType, ServiceRegistryWrapper serviceTracker) throws NetconfDocumentedException {
152         Optional<XmlElement> modulesElement = getModulesElement(xml);
153         List<XmlElement> moduleElements = getModulesElementList(modulesElement);
154
155         Map<String, Multimap<String, ModuleElementResolved>> retVal = Maps.newHashMap();
156
157         for (XmlElement moduleElement : moduleElements) {
158             ResolvingStrategy<ModuleElementResolved> resolvingStrategy = new ResolvingStrategy<ModuleElementResolved>() {
159                 @Override
160                 public ModuleElementResolved resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker, String instanceName, String moduleNamespace, EditStrategyType defaultStrategy) throws NetconfDocumentedException {
161                     return moduleMapping.fromXml(moduleElement, serviceTracker,
162                             instanceName, moduleNamespace, defaultStrategy, identityMap);
163                 }
164             };
165
166             resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy);
167         }
168         return retVal;
169     }
170
171     /**
172      * return a map containing namespace -> moduleName -> instanceName map. Attribute parsing is omitted.
173      */
174     public Map<String, Multimap<String, ModuleElementDefinition>> fromXmlModulesMap(XmlElement xml,
175             EditStrategyType defaultEditStrategyType, ServiceRegistryWrapper serviceTracker) throws NetconfDocumentedException {
176         Optional<XmlElement> modulesElement = getModulesElement(xml);
177         List<XmlElement> moduleElements = getModulesElementList(modulesElement);
178
179         Map<String, Multimap<String, ModuleElementDefinition>> retVal = Maps.newHashMap();
180
181         for (XmlElement moduleElement : moduleElements) {
182             ResolvingStrategy<ModuleElementDefinition> resolvingStrategy = new ResolvingStrategy<ModuleElementDefinition>() {
183                 @Override
184                 public ModuleElementDefinition resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement,
185                         ServiceRegistryWrapper serviceTracker, String instanceName, String moduleNamespace,
186                         EditStrategyType defaultStrategy) {
187                     // TODO: add check for conflicts between global and local
188                     // edit strategy
189                     String perInstanceEditStrategy = moduleElement.getAttribute(XmlNetconfConstants.OPERATION_ATTR_KEY,
190                             XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
191                     return new ModuleElementDefinition(instanceName, perInstanceEditStrategy, defaultStrategy);
192                 }
193             };
194
195             resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy);
196         }
197         return retVal;
198     }
199
200     private static Optional<XmlElement> getModulesElement(XmlElement xml) {
201         return xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY,
202                     XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
203     }
204
205     private List<XmlElement> getModulesElementList(Optional<XmlElement> modulesElement) throws NetconfDocumentedException {
206         List<XmlElement> moduleElements;
207
208         if (modulesElement.isPresent()) {
209             moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY);
210             modulesElement.get().checkUnrecognisedElements(moduleElements);
211         } else {
212             moduleElements = Lists.newArrayList();
213         }
214         return moduleElements;
215     }
216
217     private <T> void resolveModule(Map<String, Multimap<String, T>> retVal, ServiceRegistryWrapper serviceTracker,
218             XmlElement moduleElement, EditStrategyType defaultStrategy, ResolvingStrategy<T> resolvingStrategy) throws NetconfDocumentedException {
219         XmlElement typeElement = null;
220         typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
221         Entry<String, String> prefixToNamespace = typeElement.findNamespaceOfTextContent();
222         String moduleNamespace = prefixToNamespace.getValue();
223         XmlElement nameElement = null;
224         nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
225         String instanceName = nameElement.getTextContent();
226         String factoryNameWithPrefix = typeElement.getTextContent();
227         String prefixOrEmptyString = prefixToNamespace.getKey();
228         String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString);
229
230         ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
231
232         Multimap<String, T> innerMap = retVal.get(moduleNamespace);
233         if (innerMap == null) {
234             innerMap = HashMultimap.create();
235             retVal.put(moduleNamespace, innerMap);
236         }
237
238         T resolvedElement = resolvingStrategy.resolveElement(moduleMapping, moduleElement, serviceTracker,
239                 instanceName, moduleNamespace, defaultStrategy);
240
241         innerMap.put(factoryName, resolvedElement);
242     }
243
244     public Services fromXmlServices(XmlElement xml) throws NetconfDocumentedException {
245         Optional<XmlElement> servicesElement = getServicesElement(xml);
246
247         Services services;
248         if (servicesElement.isPresent()) {
249             services = Services.fromXml(servicesElement.get());
250         } else {
251             services = new Services();
252         }
253
254         return services;
255     }
256
257     private static Optional<XmlElement> getServicesElement(XmlElement xml) {
258         return xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY,
259                     XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
260     }
261
262     public static void checkUnrecognisedChildren(XmlElement parent) throws NetconfDocumentedException {
263         Optional<XmlElement> servicesOpt = getServicesElement(parent);
264         Optional<XmlElement> modulesOpt = getModulesElement(parent);
265
266         List<XmlElement> recognised = Lists.newArrayList();
267         if(servicesOpt.isPresent())
268             recognised.add(servicesOpt.get());
269         if(modulesOpt.isPresent())
270             recognised.add(modulesOpt.get());
271
272         parent.checkUnrecognisedElements(recognised);
273     }
274
275     private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) {
276         checkState(
277                 factoryNameWithPrefix.startsWith(prefixOrEmptyString),
278                 format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'",
279                         factoryNameWithPrefix, prefixOrEmptyString));
280
281         int factoryNameAfterPrefixIndex;
282         if (prefixOrEmptyString.isEmpty()) {
283             factoryNameAfterPrefixIndex = 0;
284         } else {
285             factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1;
286         }
287         return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex);
288     }
289
290     private ModuleConfig getModuleMapping(String moduleNamespace, String instanceName, String factoryName) {
291         Map<String, ModuleConfig> mappingsFromNamespace = moduleConfigs.get(moduleNamespace);
292
293         Preconditions.checkNotNull(mappingsFromNamespace,
294                 "Namespace %s, defined in: module %s of type %s not found, available namespaces: %s", moduleNamespace,
295                 instanceName, factoryName, moduleConfigs.keySet());
296
297         ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName);
298         checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName);
299         return moduleMapping;
300     }
301
302     private interface ResolvingStrategy<T> {
303         public T resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker,
304                 String instanceName, String moduleNamespace, EditStrategyType defaultStrategy) throws NetconfDocumentedException;
305     }
306 }