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