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