/* * 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, 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); } retVal.put(namespace, innerRetVal); } return retVal; } 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, ServiceRegistryWrapper serviceTracker) { Map>> moduleToInstances = getMappedInstances(instancesToMap, 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()) { continue; } for (ObjectName objectName : moduleMappingEntry.getValue()) { modulesElement.appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace)); } } } root.appendChild(Services.toXml(serviceTracker, document)); return root; } // TODO refactor, replace string representing namespace with namespace class // TODO refactor, replace Map->Multimap with e.g. ConfigElementResolved // class public Map> fromXmlModulesResolved(XmlElement xml, EditStrategyType defaultEditStrategyType, ServiceRegistryWrapper serviceTracker) { Optional modulesElement = getModulesElement(xml); List moduleElements = getModulesElementList(modulesElement); Map> retVal = Maps.newHashMap(); for (XmlElement moduleElement : moduleElements) { ResolvingStrategy resolvingStrategy = new ResolvingStrategy() { @Override public ModuleElementResolved resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker, String instanceName, String moduleNamespace, EditStrategyType defaultStrategy) { return moduleMapping.fromXml(moduleElement, serviceTracker, instanceName, moduleNamespace, defaultStrategy); } }; resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy); } return retVal; } /** * return a map containing namespace -> moduleName -> instanceName map. Attribute parsing is omitted. */ public Map> fromXmlModulesMap(XmlElement xml, EditStrategyType defaultEditStrategyType, ServiceRegistryWrapper serviceTracker) { Optional modulesElement = getModulesElement(xml); List moduleElements = getModulesElementList(modulesElement); Map> retVal = Maps.newHashMap(); for (XmlElement moduleElement : moduleElements) { ResolvingStrategy resolvingStrategy = new ResolvingStrategy() { @Override public ModuleElementDefinition resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker, String instanceName, String moduleNamespace, EditStrategyType defaultStrategy) { // TODO: add check for conflicts between global and local // edit strategy String perInstanceEditStrategy = moduleElement.getAttribute(XmlNetconfConstants.OPERATION_ATTR_KEY, XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); return new ModuleElementDefinition(instanceName, perInstanceEditStrategy, defaultStrategy); } }; resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy); } return retVal; } private static Optional getModulesElement(XmlElement xml) { return xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); } private List getModulesElementList(Optional modulesElement) { List moduleElements; if (modulesElement.isPresent()) { moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY); modulesElement.get().checkUnrecognisedElements(moduleElements); } else { moduleElements = Lists.newArrayList(); } return moduleElements; } private void resolveModule(Map> retVal, ServiceRegistryWrapper serviceTracker, XmlElement moduleElement, EditStrategyType defaultStrategy, ResolvingStrategy resolvingStrategy) { 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); } T resolvedElement = resolvingStrategy.resolveElement(moduleMapping, moduleElement, serviceTracker, instanceName, moduleNamespace, defaultStrategy); innerMap.put(factoryName, resolvedElement); } public Services fromXmlServices(XmlElement xml) { Optional servicesElement = getServicesElement(xml); Services services; if (servicesElement.isPresent()) { services = Services.fromXml(servicesElement.get()); } else { services = new Services(); } return services; } private static Optional getServicesElement(XmlElement xml) { return xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY, XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG); } public static void checkUnrecognisedChildren(XmlElement parent) { Optional servicesOpt = getServicesElement(parent); Optional modulesOpt = getModulesElement(parent); List recognised = Lists.newArrayList(); if(servicesOpt.isPresent()) recognised.add(servicesOpt.get()); if(modulesOpt.isPresent()) recognised.add(modulesOpt.get()); parent.checkUnrecognisedElements(recognised); } 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; } private interface ResolvingStrategy { public T resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker, String instanceName, String moduleNamespace, EditStrategyType defaultStrategy); } }