Merge "Bug 116 - Revisit SanityTest"
[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.ServiceReferenceReadableRegistry;
18 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
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.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.Set;
36
37 import static com.google.common.base.Preconditions.checkState;
38 import static java.lang.String.format;
39
40 public class Config {
41     private final Logger logger = LoggerFactory.getLogger(Config.class);
42
43     private final Map<String/* Namespace from yang file */,
44             Map<String /* Name of module entry from yang file */, ModuleConfig>> moduleConfigs;
45     private final Map<String, ModuleConfig> moduleNamesToConfigs;
46
47     public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs) {
48         this.moduleConfigs = moduleConfigs;
49         Map<String, ModuleConfig> moduleNamesToConfigs = new HashMap<>();
50         for (Entry<String, Map<String, ModuleConfig>> entry : moduleConfigs.entrySet()) {
51             moduleNamesToConfigs.putAll(entry.getValue());
52         }
53         this.moduleNamesToConfigs = Collections.unmodifiableMap(moduleNamesToConfigs);
54     }
55
56     public static Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
57                                                                                 Map<String, Map<String, ModuleConfig>> configs) {
58         Multimap<String, ObjectName> moduleToInstances = mapInstancesToModules(instancesToMap);
59
60         Map<String, Map<String, Collection<ObjectName>>> retVal = Maps.newLinkedHashMap();
61
62         for (String namespace : configs.keySet()) {
63
64             Map<String, Collection<ObjectName>> innerRetVal = Maps.newHashMap();
65
66             for (Entry<String, ModuleConfig> mbeEntry : configs.get(namespace).entrySet()) {
67
68                 String moduleName = mbeEntry.getKey();
69                 Collection<ObjectName> instances = moduleToInstances.get(moduleName);
70
71                 // TODO, this code does not support same module names from different namespaces
72                 // Namespace should be present in ObjectName
73
74                 if (instances == null)
75                     continue;
76
77                 innerRetVal.put(moduleName, instances);
78
79             }
80
81             retVal.put(namespace, innerRetVal);
82         }
83         return retVal;
84     }
85
86     private static Multimap<String, ObjectName> mapInstancesToModules(Set<ObjectName> instancesToMap) {
87         Multimap<String, ObjectName> retVal = HashMultimap.create();
88
89         for (ObjectName objectName : instancesToMap) {
90             String factoryName = ObjectNameUtil.getFactoryName(objectName);
91             retVal.put(factoryName, objectName);
92         }
93         return retVal;
94     }
95
96     // public Element toXml(Set<ObjectName> instancesToMap, String namespace,
97     // Document document) {
98     // return toXml(instancesToMap, Optional.of(namespace), document);
99     // }
100
101     public Element toXml(Set<ObjectName> instancesToMap, Optional<String> maybeNamespace, Document document,
102             Element dataElement, Services serviceTracker) {
103
104         Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
105                 moduleConfigs);
106
107         Element root = dataElement;
108         if (maybeNamespace.isPresent()) {
109             XmlUtil.addNamespaceAttr(root, maybeNamespace.get());
110         }
111
112         Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY);
113         XmlUtil.addNamespaceAttr(modulesElement,
114                 XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
115         root.appendChild(modulesElement);
116         for (String moduleNamespace : moduleToInstances.keySet()) {
117             for (Entry<String, Collection<ObjectName>> moduleMappingEntry : moduleToInstances.get(moduleNamespace)
118                     .entrySet()) {
119
120                 ModuleConfig mapping = moduleConfigs.get(moduleNamespace).get(moduleMappingEntry.getKey());
121
122                 if (moduleMappingEntry.getValue().isEmpty()) {
123                     addEmptyModulesCommented(document, modulesElement, moduleNamespace, moduleMappingEntry);
124                 } else {
125                     for (ObjectName objectName : moduleMappingEntry.getValue()) {
126                         modulesElement
127                                 .appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace));
128                     }
129                 }
130
131             }
132         }
133
134         root.appendChild(serviceTracker.toXml(serviceTracker.getMappedServices(), document));
135
136         return root;
137     }
138
139     // TODO remove commented modules from output
140     private void addEmptyModulesCommented(Document document, Element root, String moduleNamespace,
141             Entry<String, Collection<ObjectName>> moduleMappingEntry) {
142         Element emptyModule = document.createElement(XmlNetconfConstants.MODULE_KEY);
143
144         Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY,
145                 moduleMappingEntry.getKey());
146         emptyModule.appendChild(typeElement);
147
148         root.appendChild(document.createComment(XmlUtil.toString(emptyModule, false)));
149     }
150
151     // TODO refactor, replace string representing namespace with namespace class
152     // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
153     // class
154     public ConfigElementResolved fromXml(XmlElement xml,
155                                          EditStrategyType defaultEditStrategyType, ServiceReferenceReadableRegistry taClient) {
156         Map<String, Multimap<String, ModuleElementResolved>> retVal = Maps.newHashMap();
157
158         List<XmlElement> recognisedChildren = Lists.newArrayList();
159
160         Services serviceTracker = fromXmlServices(xml, recognisedChildren, taClient);
161         List<XmlElement> moduleElements = fromXmlModules(xml, recognisedChildren);
162
163         xml.checkUnrecognisedElements(recognisedChildren);
164
165         for (XmlElement moduleElement : moduleElements) {
166             resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType);
167         }
168
169         return new ConfigElementResolved(retVal, serviceTracker);
170     }
171
172     public static class ConfigElementResolved {
173
174         private final Map<String, Multimap<String, ModuleElementResolved>> resolvedModules;
175         private final Services services;
176
177         public ConfigElementResolved(Map<String, Multimap<String, ModuleElementResolved>> retVal, Services serviceTracker) {
178             this.resolvedModules = retVal;
179             this.services = serviceTracker;
180         }
181
182         public Map<String, Multimap<String, ModuleElementResolved>> getResolvedModules() {
183             return resolvedModules;
184         }
185
186         public Services getServices() {
187             return services;
188         }
189     }
190
191     private List<XmlElement> fromXmlModules(XmlElement xml, List<XmlElement> recognisedChildren) {
192         Optional<XmlElement> modulesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY,
193                 XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
194         List<XmlElement> moduleElements;
195         if (modulesElement.isPresent()) {
196             moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY);
197             recognisedChildren.add(modulesElement.get());
198             modulesElement.get().checkUnrecognisedElements(moduleElements);
199         } else {
200             moduleElements = Lists.newArrayList();
201         }
202         return moduleElements;
203     }
204
205     private void resolveModule(Map<String, Multimap<String, ModuleElementResolved>> retVal, Services serviceTracker,
206             XmlElement moduleElement, EditStrategyType defaultStrategy) {
207         XmlElement typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
208         Entry<String, String> prefixToNamespace = typeElement.findNamespaceOfTextContent();
209         String moduleNamespace = prefixToNamespace.getValue();
210         XmlElement nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
211         String instanceName = nameElement.getTextContent();
212         String factoryNameWithPrefix = typeElement.getTextContent();
213         String prefixOrEmptyString = prefixToNamespace.getKey();
214         String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString);
215
216         ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
217
218         Multimap<String, ModuleElementResolved> innerMap = retVal.get(moduleNamespace);
219         if (innerMap == null) {
220             innerMap = HashMultimap.create();
221             retVal.put(moduleNamespace, innerMap);
222         }
223
224         ModuleElementResolved moduleElementResolved = moduleMapping.fromXml(moduleElement, serviceTracker,
225                 instanceName, moduleNamespace, defaultStrategy);
226
227         innerMap.put(factoryName, moduleElementResolved);
228     }
229
230     private Services fromXmlServices(XmlElement xml, List<XmlElement> recognisedChildren,
231                                      ServiceReferenceReadableRegistry taClient) {
232         Optional<XmlElement> servicesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY,
233                 XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
234
235         Map<String, Map<String, Map<String, String>>> mappedServices;
236         if (servicesElement.isPresent()) {
237             mappedServices = Services.fromXml(servicesElement.get());
238             recognisedChildren.add(servicesElement.get());
239         } else {
240             mappedServices = new HashMap<>();
241         }
242         Services services = Services.resolveServices(mappedServices, taClient);
243
244         return services;
245     }
246
247     private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) {
248         checkState(
249                 factoryNameWithPrefix.startsWith(prefixOrEmptyString),
250                 format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'",
251                         factoryNameWithPrefix, prefixOrEmptyString));
252
253         int factoryNameAfterPrefixIndex;
254         if (prefixOrEmptyString.isEmpty()) {
255             factoryNameAfterPrefixIndex = 0;
256         } else {
257             factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1;
258         }
259         return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex);
260     }
261
262     private ModuleConfig getModuleMapping(String moduleNamespace, String instanceName, String factoryName) {
263         Map<String, ModuleConfig> mappingsFromNamespace = moduleConfigs.get(moduleNamespace);
264
265         Preconditions.checkNotNull(mappingsFromNamespace,
266                 "Namespace %s, defined in: module %s of type %s not found, available namespaces: %s", moduleNamespace,
267                 instanceName, factoryName, moduleConfigs.keySet());
268
269         ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName);
270         checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName);
271         return moduleMapping;
272     }
273
274 }