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