7aec4946e1af524e5fe5938ad20993cdf1dc2989
[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         for (XmlElement moduleElement : moduleElements) {
155             ResolvingStrategy<ModuleElementResolved> resolvingStrategy = (moduleMapping, moduleElement1,
156                     serviceTracker1, instanceName, moduleNamespace,
157                     defaultStrategy) -> moduleMapping.fromXml(moduleElement1, serviceTracker1, instanceName,
158                             moduleNamespace, defaultStrategy, identityMap, enumResolver);
159
160             resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy);
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         for (XmlElement moduleElement : moduleElements) {
178             ResolvingStrategy<ModuleElementDefinition> resolvingStrategy = (moduleMapping, moduleElement1,
179                     serviceTracker1, instanceName, moduleNamespace, defaultStrategy) -> {
180                 // TODO: add check for conflicts between global and local
181                 // edit strategy
182                 String perInstanceEditStrategy = moduleElement1.getAttribute(XmlMappingConstants.OPERATION_ATTR_KEY,
183                         XmlMappingConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
184                 return new ModuleElementDefinition(instanceName, perInstanceEditStrategy, defaultStrategy);
185             };
186
187             resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy);
188         }
189         return retVal;
190     }
191
192     private static Optional<XmlElement> getModulesElement(final XmlElement xml) {
193         return xml.getOnlyChildElementOptionally(XmlMappingConstants.MODULES_KEY,
194                 XmlMappingConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
195     }
196
197     private List<XmlElement> getModulesElementList(final Optional<XmlElement> modulesElement)
198             throws DocumentedException {
199         List<XmlElement> moduleElements;
200
201         if (modulesElement.isPresent()) {
202             moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlMappingConstants.MODULE_KEY);
203             modulesElement.get().checkUnrecognisedElements(moduleElements);
204         } else {
205             moduleElements = Lists.newArrayList();
206         }
207         return moduleElements;
208     }
209
210     private <T> void resolveModule(final Map<String, Multimap<String, T>> retVal,
211             final ServiceRegistryWrapper serviceTracker, final XmlElement moduleElement,
212             final EditStrategyType defaultStrategy, final ResolvingStrategy<T> resolvingStrategy)
213             throws DocumentedException {
214         XmlElement typeElement = null;
215         typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlMappingConstants.TYPE_KEY);
216         Entry<String, String> prefixToNamespace = typeElement.findNamespaceOfTextContent();
217         String moduleNamespace = prefixToNamespace.getValue();
218         XmlElement nameElement = null;
219         nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlMappingConstants.NAME_KEY);
220         String instanceName = nameElement.getTextContent();
221         String factoryNameWithPrefix = typeElement.getTextContent();
222         String prefixOrEmptyString = prefixToNamespace.getKey();
223         String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString);
224
225         ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
226
227         Multimap<String, T> innerMap = retVal.computeIfAbsent(moduleNamespace, k -> HashMultimap.create());
228
229         T resolvedElement = resolvingStrategy.resolveElement(moduleMapping, moduleElement, serviceTracker, instanceName,
230                 moduleNamespace, defaultStrategy);
231
232         innerMap.put(factoryName, resolvedElement);
233     }
234
235     public Services fromXmlServices(final XmlElement xml) throws DocumentedException {
236         Optional<XmlElement> servicesElement = getServicesElement(xml);
237
238         Services services;
239         if (servicesElement.isPresent()) {
240             services = Services.fromXml(servicesElement.get());
241         } else {
242             services = new Services();
243         }
244
245         return services;
246     }
247
248     private static Optional<XmlElement> getServicesElement(final XmlElement xml) {
249         return xml.getOnlyChildElementOptionally(XmlMappingConstants.SERVICES_KEY,
250                 XmlMappingConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
251     }
252
253     public static void checkUnrecognisedChildren(final XmlElement parent) throws DocumentedException {
254         Optional<XmlElement> servicesOpt = getServicesElement(parent);
255         Optional<XmlElement> modulesOpt = getModulesElement(parent);
256
257         List<XmlElement> recognised = Lists.newArrayList();
258         if (servicesOpt.isPresent()) {
259             recognised.add(servicesOpt.get());
260         }
261         if (modulesOpt.isPresent()) {
262             recognised.add(modulesOpt.get());
263         }
264
265         parent.checkUnrecognisedElements(recognised);
266     }
267
268     private String getFactoryName(final String factoryNameWithPrefix, final String prefixOrEmptyString) {
269         checkState(factoryNameWithPrefix.startsWith(prefixOrEmptyString),
270                 String.format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'",
271                         factoryNameWithPrefix, prefixOrEmptyString));
272
273         int factoryNameAfterPrefixIndex;
274         if (prefixOrEmptyString.isEmpty()) {
275             factoryNameAfterPrefixIndex = 0;
276         } else {
277             factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1;
278         }
279         return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex);
280     }
281
282     private ModuleConfig getModuleMapping(final String moduleNamespace, final String instanceName,
283             final String factoryName) {
284         Map<String, ModuleConfig> mappingsFromNamespace = moduleConfigs.get(moduleNamespace);
285
286         Preconditions.checkNotNull(mappingsFromNamespace,
287                 "Namespace %s, defined in: module %s of type %s not found, available namespaces: %s", moduleNamespace,
288                 instanceName, factoryName, moduleConfigs.keySet());
289
290         ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName);
291         checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName);
292         return moduleMapping;
293     }
294
295     private interface ResolvingStrategy<T> {
296         T resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker,
297                 String instanceName, String moduleNamespace, EditStrategyType defaultStrategy)
298                 throws DocumentedException;
299     }
300 }