/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.netconf.confignetconfconnector.mapping.config; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditStrategyType; import org.opendaylight.controller.netconf.util.xml.XmlElement; import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import javax.management.ObjectName; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import static com.google.common.base.Preconditions.checkState; import static java.lang.String.format; public class Config { private final Logger logger = LoggerFactory.getLogger(Config.class); private final Map> moduleConfigs; private final Map moduleNamesToConfigs; public Config(Map> moduleConfigs) { this.moduleConfigs = moduleConfigs; Map moduleNamesToConfigs = new HashMap<>(); for (Entry> entry : moduleConfigs.entrySet()) { moduleNamesToConfigs.putAll(entry.getValue()); } this.moduleNamesToConfigs = Collections.unmodifiableMap(moduleNamesToConfigs); } public static Map>> getMappedInstances(Set instancesToMap, Services serviceTracker, Map> configs) { Multimap moduleToInstances = mapInstancesToModules(instancesToMap); Map>> retVal = Maps.newLinkedHashMap(); for (String namespace : configs.keySet()) { Map> innerRetVal = Maps.newHashMap(); for (Entry mbeEntry : configs.get(namespace).entrySet()) { String moduleName = mbeEntry.getKey(); Collection instances = moduleToInstances.get(moduleName); // TODO, this code does not support same module names from different namespaces // Namespace should be present in ObjectName if (instances == null) continue; innerRetVal.put(moduleName, instances); // All found instances add to service tracker in advance // This way all instances will be serialized as all available // services when get-config is triggered // (even if they are not used as services by other instances) // = more user friendly addServices(serviceTracker, instances, mbeEntry.getValue().getProvidedServices()); } retVal.put(namespace, innerRetVal); } return retVal; } private static void addServices(Services serviceTracker, Collection instances, Multimap providedServices) { for (ObjectName instanceOn : instances) { for (Entry serviceName : providedServices.entries()) { serviceTracker.addServiceEntry(serviceName.getKey(), serviceName.getValue(), instanceOn); } } } private static Multimap mapInstancesToModules(Set instancesToMap) { Multimap retVal = HashMultimap.create(); for (ObjectName objectName : instancesToMap) { String factoryName = ObjectNameUtil.getFactoryName(objectName); retVal.put(factoryName, objectName); } return retVal; } // public Element toXml(Set instancesToMap, String namespace, // Document document) { // return toXml(instancesToMap, Optional.of(namespace), document); // } public Element toXml(Set instancesToMap, Optional maybeNamespace, Document document, Element dataElement) { Services serviceTracker = new Services(); Map>> moduleToInstances = getMappedInstances(instancesToMap, serviceTracker, moduleConfigs); Element root = dataElement; if (maybeNamespace.isPresent()) { XmlUtil.addNamespaceAttr(root, maybeNamespace.get()); } Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY); XmlUtil.addNamespaceAttr(modulesElement, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); root.appendChild(modulesElement); for (String moduleNamespace : moduleToInstances.keySet()) { for (Entry> moduleMappingEntry : moduleToInstances.get(moduleNamespace) .entrySet()) { ModuleConfig mapping = moduleConfigs.get(moduleNamespace).get(moduleMappingEntry.getKey()); if (moduleMappingEntry.getValue().isEmpty()) { addEmptyModulesCommented(document, modulesElement, moduleNamespace, moduleMappingEntry); } else { for (ObjectName objectName : moduleMappingEntry.getValue()) { modulesElement .appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace)); } } } } root.appendChild(serviceTracker.toXml(serviceTracker.getMappedServices(), document)); return root; } // TODO remove commented modules from output private void addEmptyModulesCommented(Document document, Element root, String moduleNamespace, Entry> moduleMappingEntry) { Element emptyModule = document.createElement(XmlNetconfConstants.MODULE_KEY); Element typeElement = XmlUtil.createTextElement(document, XmlNetconfConstants.TYPE_KEY, moduleMappingEntry.getKey()); emptyModule.appendChild(typeElement); root.appendChild(document.createComment(XmlUtil.toString(emptyModule, false))); } // TODO refactor, replace string representing namespace with namespace class // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved // class public Map> fromXml(XmlElement xml, Set instancesForFillingServiceRefMapping, EditStrategyType defaultEditStrategyType) { Map> retVal = Maps.newHashMap(); List recognisedChildren = Lists.newArrayList(); Services serviceTracker = fromXmlServices(xml, recognisedChildren, instancesForFillingServiceRefMapping); List moduleElements = fromXmlModules(xml, recognisedChildren); xml.checkUnrecognisedElements(recognisedChildren); for (XmlElement moduleElement : moduleElements) { resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType); } return retVal; } private List fromXmlModules(XmlElement xml, List recognisedChildren) { Optional modulesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); List moduleElements; if (modulesElement.isPresent()) { moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY); recognisedChildren.add(modulesElement.get()); modulesElement.get().checkUnrecognisedElements(moduleElements); } else { moduleElements = Lists.newArrayList(); } return moduleElements; } private void resolveModule(Map> retVal, Services serviceTracker, XmlElement moduleElement, EditStrategyType defaultStrategy) { XmlElement typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY); Entry prefixToNamespace = typeElement.findNamespaceOfTextContent(); String moduleNamespace = prefixToNamespace.getValue(); XmlElement nameElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.NAME_KEY); String instanceName = nameElement.getTextContent(); String factoryNameWithPrefix = typeElement.getTextContent(); String prefixOrEmptyString = prefixToNamespace.getKey(); String factoryName = getFactoryName(factoryNameWithPrefix, prefixOrEmptyString); ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName); Multimap innerMap = retVal.get(moduleNamespace); if (innerMap == null) { innerMap = HashMultimap.create(); retVal.put(moduleNamespace, innerMap); } ModuleElementResolved moduleElementResolved = moduleMapping.fromXml(moduleElement, serviceTracker, instanceName, moduleNamespace, defaultStrategy); innerMap.put(factoryName, moduleElementResolved); } private Services fromXmlServices(XmlElement xml, List recognisedChildren, Set instancesForFillingServiceRefMapping) { Optional servicesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); Map>> mappedServices; if (servicesElement.isPresent()) { mappedServices = Services.fromXml(servicesElement.get()); recognisedChildren.add(servicesElement.get()); } else { mappedServices = new HashMap<>(); } Services services = Services.resolveServices(mappedServices); // merge with what candidate db contains by default - ref_ for(ObjectName existingON: instancesForFillingServiceRefMapping) { logger.trace("Filling services from {}", existingON); // get all its services String factoryName = ObjectNameUtil.getFactoryName(existingON); ModuleConfig moduleConfig = moduleNamesToConfigs.get(factoryName); checkState(moduleConfig != null, "Cannot find ModuleConfig with name " + factoryName + " in " + moduleNamesToConfigs); // Set services = ; for (Entry serviceName : moduleConfig.getProvidedServices().entries()) { services.addServiceEntry(serviceName.getKey(), serviceName.getValue(), existingON); } } return services; } private String getFactoryName(String factoryNameWithPrefix, String prefixOrEmptyString) { checkState( factoryNameWithPrefix.startsWith(prefixOrEmptyString), format("Internal error: text " + "content '%s' of type node does not start with prefix '%s'", factoryNameWithPrefix, prefixOrEmptyString)); int factoryNameAfterPrefixIndex; if (prefixOrEmptyString.isEmpty()) { factoryNameAfterPrefixIndex = 0; } else { factoryNameAfterPrefixIndex = prefixOrEmptyString.length() + 1; } return factoryNameWithPrefix.substring(factoryNameAfterPrefixIndex); } private ModuleConfig getModuleMapping(String moduleNamespace, String instanceName, String factoryName) { Map mappingsFromNamespace = moduleConfigs.get(moduleNamespace); Preconditions.checkNotNull(mappingsFromNamespace, "Namespace %s, defined in: module %s of type %s not found, available namespaces: %s", moduleNamespace, instanceName, factoryName, moduleConfigs.keySet()); ModuleConfig moduleMapping = mappingsFromNamespace.get(factoryName); checkState(moduleMapping != null, "Cannot find mapping for module type " + factoryName); return moduleMapping; } }