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