Add running configuration data to get netconf operation response
[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                                                                                 Services serviceTracker, 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                 // All found instances add to service tracker in advance
79                 // This way all instances will be serialized as all available
80                 // services when get-config is triggered
81                 // (even if they are not used as services by other instances)
82                 // = more user friendly
83                 addServices(serviceTracker, instances, mbeEntry.getValue().getProvidedServices());
84
85             }
86
87             retVal.put(namespace, innerRetVal);
88         }
89         return retVal;
90     }
91
92     private static void addServices(Services serviceTracker, Collection<ObjectName> instances,
93             Multimap<String, String> providedServices) {
94         for (ObjectName instanceOn : instances) {
95             for (Entry<String, String> serviceName : providedServices.entries()) {
96                 serviceTracker.addServiceEntry(serviceName.getKey(), serviceName.getValue(), instanceOn);
97             }
98         }
99     }
100
101     private static Multimap<String, ObjectName> mapInstancesToModules(Set<ObjectName> instancesToMap) {
102         Multimap<String, ObjectName> retVal = HashMultimap.create();
103
104         for (ObjectName objectName : instancesToMap) {
105             String factoryName = ObjectNameUtil.getFactoryName(objectName);
106             retVal.put(factoryName, objectName);
107         }
108         return retVal;
109     }
110
111     // public Element toXml(Set<ObjectName> instancesToMap, String namespace,
112     // Document document) {
113     // return toXml(instancesToMap, Optional.of(namespace), document);
114     // }
115
116     public Element toXml(Set<ObjectName> instancesToMap, Optional<String> maybeNamespace, Document document,
117             Element dataElement) {
118         Services serviceTracker = new Services();
119
120         Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
121                 serviceTracker, moduleConfigs);
122
123         Element root = dataElement;
124         if (maybeNamespace.isPresent()) {
125             XmlUtil.addNamespaceAttr(root, maybeNamespace.get());
126         }
127
128         Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY);
129         XmlUtil.addNamespaceAttr(modulesElement,
130                 XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
131         root.appendChild(modulesElement);
132         for (String moduleNamespace : moduleToInstances.keySet()) {
133             for (Entry<String, Collection<ObjectName>> moduleMappingEntry : moduleToInstances.get(moduleNamespace)
134                     .entrySet()) {
135
136                 ModuleConfig mapping = moduleConfigs.get(moduleNamespace).get(moduleMappingEntry.getKey());
137
138                 if (moduleMappingEntry.getValue().isEmpty()) {
139                     addEmptyModulesCommented(document, modulesElement, moduleNamespace, moduleMappingEntry);
140                 } else {
141                     for (ObjectName objectName : moduleMappingEntry.getValue()) {
142                         modulesElement
143                                 .appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace));
144                     }
145                 }
146
147             }
148         }
149
150         root.appendChild(serviceTracker.toXml(serviceTracker.getMappedServices(), document));
151
152         return root;
153     }
154
155     // TODO remove commented modules from output
156     private void addEmptyModulesCommented(Document document, Element root, String moduleNamespace,
157             Entry<String, Collection<ObjectName>> moduleMappingEntry) {
158         Element emptyModule = document.createElement(XmlNetconfConstants.MODULE_KEY);
159
160         Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY,
161                 moduleMappingEntry.getKey());
162         emptyModule.appendChild(typeElement);
163
164         root.appendChild(document.createComment(XmlUtil.toString(emptyModule, false)));
165     }
166
167     // TODO refactor, replace string representing namespace with namespace class
168     // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
169     // class
170     public Map<String, Multimap<String, ModuleElementResolved>> fromXml(XmlElement xml, Set<ObjectName> instancesForFillingServiceRefMapping,
171                                                                         EditStrategyType defaultEditStrategyType) {
172         Map<String, Multimap<String, ModuleElementResolved>> retVal = Maps.newHashMap();
173
174         List<XmlElement> recognisedChildren = Lists.newArrayList();
175
176         Services serviceTracker = fromXmlServices(xml, recognisedChildren, instancesForFillingServiceRefMapping);
177         List<XmlElement> moduleElements = fromXmlModules(xml, recognisedChildren);
178
179         xml.checkUnrecognisedElements(recognisedChildren);
180
181         for (XmlElement moduleElement : moduleElements) {
182             resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType);
183         }
184
185         return retVal;
186     }
187
188     private List<XmlElement> fromXmlModules(XmlElement xml, List<XmlElement> recognisedChildren) {
189         Optional<XmlElement> modulesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY,
190                 XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
191         List<XmlElement> moduleElements;
192         if (modulesElement.isPresent()) {
193             moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY);
194             recognisedChildren.add(modulesElement.get());
195             modulesElement.get().checkUnrecognisedElements(moduleElements);
196         } else {
197             moduleElements = Lists.newArrayList();
198         }
199         return moduleElements;
200     }
201
202     private void resolveModule(Map<String, Multimap<String, ModuleElementResolved>> retVal, Services serviceTracker,
203             XmlElement moduleElement, EditStrategyType defaultStrategy) {
204         XmlElement typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
205         Entry<String, String> prefixToNamespace = typeElement.findNamespaceOfTextContent();
206         String moduleNamespace = prefixToNamespace.getValue();
207         XmlElement nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY);
208         String instanceName = nameElement.getTextContent();
209         String factoryNameWithPrefix = typeElement.getTextContent();
210         String prefixOrEmptyString = prefixToNamespace.getKey();
211         String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString);
212
213         ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
214
215         Multimap<String, ModuleElementResolved> innerMap = retVal.get(moduleNamespace);
216         if (innerMap == null) {
217             innerMap = HashMultimap.create();
218             retVal.put(moduleNamespace, innerMap);
219         }
220
221         ModuleElementResolved moduleElementResolved = moduleMapping.fromXml(moduleElement, serviceTracker,
222                 instanceName, moduleNamespace, defaultStrategy);
223
224         innerMap.put(factoryName, moduleElementResolved);
225     }
226
227     private Services fromXmlServices(XmlElement xml, List<XmlElement> recognisedChildren, Set<ObjectName> instancesForFillingServiceRefMapping) {
228         Optional<XmlElement> servicesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY,
229                 XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
230
231         Map<String, Map<String, Map<String, String>>> mappedServices;
232         if (servicesElement.isPresent()) {
233             mappedServices = Services.fromXml(servicesElement.get());
234             recognisedChildren.add(servicesElement.get());
235         } else {
236             mappedServices = new HashMap<>();
237         }
238         Services services = Services.resolveServices(mappedServices);
239         // merge with what candidate db contains by default - ref_
240
241         for(ObjectName existingON: instancesForFillingServiceRefMapping) {
242             logger.trace("Filling services from {}", existingON);
243             // get all its services
244             String factoryName = ObjectNameUtil.getFactoryName(existingON);
245             ModuleConfig moduleConfig = moduleNamesToConfigs.get(factoryName);
246
247             checkState(moduleConfig != null, "Cannot find ModuleConfig with name " + factoryName + " in " + moduleNamesToConfigs);
248             // Set<String> services = ;
249             for (Entry<String, String> serviceName : moduleConfig.getProvidedServices().entries()) {
250
251                 services.addServiceEntry(serviceName.getKey(), serviceName.getValue(), existingON);
252             }
253         }
254
255         return services;
256     }
257
258     private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) {
259         checkState(
260                 factoryNameWithPrefix.startsWith(prefixOrEmptyString),
261                 format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'",
262                         factoryNameWithPrefix, prefixOrEmptyString));
263
264         int factoryNameAfterPrefixIndex;
265         if (prefixOrEmptyString.isEmpty()) {
266             factoryNameAfterPrefixIndex = 0;
267         } else {
268             factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1;
269         }
270         return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex);
271     }
272
273     private ModuleConfig getModuleMapping(String moduleNamespace, String instanceName, String factoryName) {
274         Map<String, ModuleConfig> mappingsFromNamespace = moduleConfigs.get(moduleNamespace);
275
276         Preconditions.checkNotNull(mappingsFromNamespace,
277                 "Namespace %s, defined in: module %s of type %s not found, available namespaces: %s", moduleNamespace,
278                 instanceName, factoryName, moduleConfigs.keySet());
279
280         ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName);
281         checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName);
282         return moduleMapping;
283     }
284
285 }