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