2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config;
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;
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;
33 import java.util.Map.Entry;
36 import static com.google.common.base.Preconditions.checkState;
37 import static java.lang.String.format;
40 private final Logger logger = LoggerFactory.getLogger(Config.class);
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;
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());
52 this.moduleNamesToConfigs = Collections.unmodifiableMap(moduleNamesToConfigs);
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);
59 Map<String, Map<String, Collection<ObjectName>>> retVal = Maps.newLinkedHashMap();
61 for (String namespace : configs.keySet()) {
63 Map<String, Collection<ObjectName>> innerRetVal = Maps.newHashMap();
65 for (Entry<String, ModuleConfig> mbeEntry : configs.get(namespace).entrySet()) {
67 String moduleName = mbeEntry.getKey();
68 Collection<ObjectName> instances = moduleToInstances.get(moduleName);
70 // TODO, this code does not support same module names from different namespaces
71 // Namespace should be present in ObjectName
73 if (instances == null)
76 innerRetVal.put(moduleName, instances);
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());
87 retVal.put(namespace, innerRetVal);
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);
101 private static Multimap<String, ObjectName> mapInstancesToModules(Set<ObjectName> instancesToMap) {
102 Multimap<String, ObjectName> retVal = HashMultimap.create();
104 for (ObjectName objectName : instancesToMap) {
105 String factoryName = ObjectNameUtil.getFactoryName(objectName);
106 retVal.put(factoryName, objectName);
111 // public Element toXml(Set<ObjectName> instancesToMap, String namespace,
112 // Document document) {
113 // return toXml(instancesToMap, Optional.of(namespace), document);
116 public Element toXml(Set<ObjectName> instancesToMap, Optional<String> maybeNamespace, Document document,
117 Element dataElement) {
118 Services serviceTracker = new Services();
120 Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
121 serviceTracker, moduleConfigs);
123 Element root = dataElement;
124 if (maybeNamespace.isPresent()) {
125 XmlUtil.addNamespaceAttr(root, maybeNamespace.get());
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)
136 ModuleConfig mapping = moduleConfigs.get(moduleNamespace).get(moduleMappingEntry.getKey());
138 if (moduleMappingEntry.getValue().isEmpty()) {
139 addEmptyModulesCommented(document, modulesElement, moduleNamespace, moduleMappingEntry);
141 for (ObjectName objectName : moduleMappingEntry.getValue()) {
143 .appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace));
150 root.appendChild(serviceTracker.toXml(serviceTracker.getMappedServices(), document));
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);
160 Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY,
161 moduleMappingEntry.getKey());
162 emptyModule.appendChild(typeElement);
164 root.appendChild(document.createComment(XmlUtil.toString(emptyModule, false)));
167 // TODO refactor, replace string representing namespace with namespace class
168 // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved
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();
174 List<XmlElement> recognisedChildren = Lists.newArrayList();
176 Services serviceTracker = fromXmlServices(xml, recognisedChildren, instancesForFillingServiceRefMapping);
177 List<XmlElement> moduleElements = fromXmlModules(xml, recognisedChildren);
179 xml.checkUnrecognisedElements(recognisedChildren);
181 for (XmlElement moduleElement : moduleElements) {
182 resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType);
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);
197 moduleElements = Lists.newArrayList();
199 return moduleElements;
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);
213 ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
215 Multimap<String, ModuleElementResolved> innerMap = retVal.get(moduleNamespace);
216 if (innerMap == null) {
217 innerMap = HashMultimap.create();
218 retVal.put(moduleNamespace, innerMap);
221 ModuleElementResolved moduleElementResolved = moduleMapping.fromXml(moduleElement, serviceTracker,
222 instanceName, moduleNamespace, defaultStrategy);
224 innerMap.put(factoryName, moduleElementResolved);
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);
231 Map<String, Map<String, Map<String, String>>> mappedServices;
232 if (servicesElement.isPresent()) {
233 mappedServices = Services.fromXml(servicesElement.get());
234 recognisedChildren.add(servicesElement.get());
236 mappedServices = new HashMap<>();
238 Services services = Services.resolveServices(mappedServices);
239 // merge with what candidate db contains by default - ref_
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);
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()) {
251 services.addServiceEntry(serviceName.getKey(), serviceName.getValue(), existingON);
258 private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) {
260 factoryNameWithPrefix.startsWith(prefixOrEmptyString),
261 format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'",
262 factoryNameWithPrefix, prefixOrEmptyString));
264 int factoryNameAfterPrefixIndex;
265 if (prefixOrEmptyString.isEmpty()) {
266 factoryNameAfterPrefixIndex = 0;
268 factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1;
270 return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex);
273 private ModuleConfig getModuleMapping(String moduleNamespace, String instanceName, String factoryName) {
274 Map<String, ModuleConfig> mappingsFromNamespace = moduleConfigs.get(moduleNamespace);
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());
280 ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName);
281 checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName);
282 return moduleMapping;