10602ce7e5011e42bff511430bd46353c4565cde
[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 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 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.collect.HashMultimap;
15 import com.google.common.collect.Lists;
16 import com.google.common.collect.Maps;
17 import com.google.common.collect.Multimap;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Date;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Set;
25 import javax.management.ObjectName;
26 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
27 import org.opendaylight.controller.config.facade.xml.mapping.IdentityMapping;
28 import org.opendaylight.controller.config.facade.xml.osgi.EnumResolver;
29 import org.opendaylight.controller.config.facade.xml.strategy.EditStrategyType;
30 import org.opendaylight.controller.config.util.xml.DocumentedException;
31 import org.opendaylight.controller.config.util.xml.XmlElement;
32 import org.opendaylight.controller.config.util.xml.XmlMappingConstants;
33 import org.opendaylight.controller.config.util.xml.XmlUtil;
34 import org.w3c.dom.Document;
35 import org.w3c.dom.Element;
36
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 */, ModuleConfig>> moduleConfigs;
42
43     private final Map<String, Map<Date, IdentityMapping>> identityMap;
44
45     private final EnumResolver enumResolver;
46
47     public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs, final EnumResolver enumResolver) {
48         this(moduleConfigs, Collections.<String, Map<Date, IdentityMapping>>emptyMap(), enumResolver);
49     }
50
51     public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs, Map<String, Map<Date, IdentityMapping>> identityMap, final EnumResolver enumResolver) {
52         this.moduleConfigs = moduleConfigs;
53         this.identityMap = identityMap;
54         this.enumResolver = enumResolver;
55     }
56
57     public static Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
58                                                                                 Map<String, Map<String, ModuleConfig>> configs) {
59         Multimap<String, ObjectName> moduleToInstances = mapInstancesToModules(instancesToMap);
60
61         Map<String, Map<String, Collection<ObjectName>>> retVal = Maps.newLinkedHashMap();
62
63         for (Entry<String, Map<String, ModuleConfig>> namespaceToModuleToConfigEntry : configs.entrySet()) {
64
65             Map<String, Collection<ObjectName>> innerRetVal = Maps.newHashMap();
66
67             for (Entry<String, ModuleConfig> mbeEntry : namespaceToModuleToConfigEntry.getValue().entrySet()) {
68
69                 String moduleName = mbeEntry.getKey();
70                 Collection<ObjectName> instances = moduleToInstances.get(moduleName);
71
72                 // TODO, this code does not support same module names from different namespaces
73                 // Namespace should be present in ObjectName
74
75                 if (instances == null){
76                     continue;
77                 }
78
79                 innerRetVal.put(moduleName, instances);
80
81             }
82
83             retVal.put(namespaceToModuleToConfigEntry.getKey(), innerRetVal);
84         }
85         return retVal;
86     }
87
88     private static Multimap<String, ObjectName> mapInstancesToModules(Set<ObjectName> instancesToMap) {
89         Multimap<String, ObjectName> retVal = HashMultimap.create();
90
91         for (ObjectName objectName : instancesToMap) {
92             String factoryName = ObjectNameUtil.getFactoryName(objectName);
93             retVal.put(factoryName, objectName);
94         }
95         return retVal;
96     }
97
98     public Element toXml(Set<ObjectName> instancesToMap, Optional<String> maybeNamespace, Document document,
99             Element dataElement, ServiceRegistryWrapper serviceTracker) {
100
101         Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
102                 moduleConfigs);
103
104         if (maybeNamespace.isPresent()) {
105             dataElement.setAttributeNS(maybeNamespace.get(), dataElement.getNodeName(), "xmlns");
106         }
107
108         Element modulesElement = XmlUtil.createElement(document, XmlMappingConstants.MODULES_KEY, Optional.of(XmlMappingConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG));
109         dataElement.appendChild(modulesElement);
110         for (Entry<String, Map<String, Collection<ObjectName>>> moduleToInstanceEntry : moduleToInstances.entrySet()) {
111             for (Entry<String, Collection<ObjectName>> moduleMappingEntry : moduleToInstanceEntry.getValue()
112                     .entrySet()) {
113
114                 ModuleConfig mapping = moduleConfigs.get(moduleToInstanceEntry.getKey()).get(moduleMappingEntry.getKey());
115
116                 if (moduleMappingEntry.getValue().isEmpty()) {
117                     continue;
118                 }
119
120                 for (ObjectName objectName : moduleMappingEntry.getValue()) {
121                     modulesElement.appendChild(mapping.toXml(objectName, document, moduleToInstanceEntry.getKey(), enumResolver));
122                 }
123
124             }
125         }
126
127         dataElement.appendChild(Services.toXml(serviceTracker, document));
128
129         return dataElement;
130     }
131
132     public Element moduleToXml(String moduleNamespace, String factoryName, String instanceName,
133         ObjectName instanceON, Document document) {
134         ModuleConfig moduleConfig = getModuleMapping(moduleNamespace, instanceName, factoryName);
135         return moduleConfig.toXml(instanceON, document, moduleNamespace, enumResolver);
136     }
137
138     // TODO refactor, replace string representing namespace with namespace class
139     // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
140     // class
141
142     public Map<String, Multimap<String, ModuleElementResolved>> fromXmlModulesResolved(XmlElement xml, EditStrategyType defaultEditStrategyType, ServiceRegistryWrapper serviceTracker) throws DocumentedException {
143         Optional<XmlElement> modulesElement = getModulesElement(xml);
144         List<XmlElement> moduleElements = getModulesElementList(modulesElement);
145
146         Map<String, Multimap<String, ModuleElementResolved>> retVal = Maps.newHashMap();
147
148         for (XmlElement moduleElement : moduleElements) {
149             ResolvingStrategy<ModuleElementResolved> resolvingStrategy = new ResolvingStrategy<ModuleElementResolved>() {
150                 @Override
151                 public ModuleElementResolved resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker, String instanceName, String moduleNamespace, EditStrategyType defaultStrategy) throws DocumentedException {
152                     return moduleMapping.fromXml(moduleElement, serviceTracker,
153                             instanceName, moduleNamespace, defaultStrategy, identityMap, enumResolver);
154                 }
155             };
156
157             resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy);
158         }
159         return retVal;
160     }
161
162     /**
163      * return a map containing namespace -&gt; moduleName -&gt; instanceName map. Attribute parsing is omitted.
164      */
165     public Map<String, Multimap<String, ModuleElementDefinition>> fromXmlModulesMap(XmlElement xml,
166             EditStrategyType defaultEditStrategyType, ServiceRegistryWrapper serviceTracker) throws DocumentedException {
167         Optional<XmlElement> modulesElement = getModulesElement(xml);
168         List<XmlElement> moduleElements = getModulesElementList(modulesElement);
169
170         Map<String, Multimap<String, ModuleElementDefinition>> retVal = Maps.newHashMap();
171
172         for (XmlElement moduleElement : moduleElements) {
173             ResolvingStrategy<ModuleElementDefinition> resolvingStrategy = new ResolvingStrategy<ModuleElementDefinition>() {
174                 @Override
175                 public ModuleElementDefinition resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement,
176                         ServiceRegistryWrapper serviceTracker, String instanceName, String moduleNamespace,
177                         EditStrategyType defaultStrategy) {
178                     // TODO: add check for conflicts between global and local
179                     // edit strategy
180                     String perInstanceEditStrategy = moduleElement.getAttribute(XmlMappingConstants.OPERATION_ATTR_KEY,
181                             XmlMappingConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
182                     return new ModuleElementDefinition(instanceName, perInstanceEditStrategy, defaultStrategy);
183                 }
184             };
185
186             resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy);
187         }
188         return retVal;
189     }
190
191     private static Optional<XmlElement> getModulesElement(XmlElement xml) {
192         return xml.getOnlyChildElementOptionally(XmlMappingConstants.MODULES_KEY,
193                     XmlMappingConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
194     }
195
196     private List<XmlElement> getModulesElementList(Optional<XmlElement> modulesElement) throws DocumentedException {
197         List<XmlElement> moduleElements;
198
199         if (modulesElement.isPresent()) {
200             moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlMappingConstants.MODULE_KEY);
201             modulesElement.get().checkUnrecognisedElements(moduleElements);
202         } else {
203             moduleElements = Lists.newArrayList();
204         }
205         return moduleElements;
206     }
207
208     private <T> void resolveModule(Map<String, Multimap<String, T>> retVal, ServiceRegistryWrapper serviceTracker,
209             XmlElement moduleElement, EditStrategyType defaultStrategy, ResolvingStrategy<T> resolvingStrategy) throws DocumentedException {
210         XmlElement typeElement = null;
211         typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlMappingConstants.TYPE_KEY);
212         Entry<String, String> prefixToNamespace = typeElement.findNamespaceOfTextContent();
213         String moduleNamespace = prefixToNamespace.getValue();
214         XmlElement nameElement = null;
215         nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlMappingConstants.NAME_KEY);
216         String instanceName = nameElement.getTextContent();
217         String factoryNameWithPrefix = typeElement.getTextContent();
218         String prefixOrEmptyString = prefixToNamespace.getKey();
219         String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString);
220
221         ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
222
223         Multimap<String, T> innerMap = retVal.get(moduleNamespace);
224         if (innerMap == null) {
225             innerMap = HashMultimap.create();
226             retVal.put(moduleNamespace, innerMap);
227         }
228
229         T resolvedElement = resolvingStrategy.resolveElement(moduleMapping, moduleElement, serviceTracker,
230                 instanceName, moduleNamespace, defaultStrategy);
231
232         innerMap.put(factoryName, resolvedElement);
233     }
234
235     public Services fromXmlServices(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(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(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(String factoryNameWithPrefix, String prefixOrEmptyString) {
269         checkState(
270                 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(String moduleNamespace, String instanceName, 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) throws DocumentedException;
298     }
299 }