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