Merge "Bug 509: Fixed incorrect merging of Data Store Writes / Events"
[controller.git] / opendaylight / netconf / config-netconf-connector / src / main / java / org / opendaylight / controller / netconf / confignetconfconnector / mapping / config / Config.java
index 115fc2ccb6dc6f75c4a40cc15e95d2e6cc63a56a..decb091f866f085ae36034d21c7ee4908685170b 100644 (file)
@@ -8,13 +8,22 @@
 
 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 static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import javax.management.ObjectName;
+
 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.editconfig.EditConfig;
 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;
@@ -24,17 +33,12 @@ 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;
+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;
 
 public class Config {
     private final Logger logger = LoggerFactory.getLogger(Config.class);
@@ -43,42 +47,45 @@ public class Config {
             Map<String /* Name of module entry from yang file */, ModuleConfig>> moduleConfigs;
     private final Map<String, ModuleConfig> moduleNamesToConfigs;
 
+    private final Map<String, Map<Date, EditConfig.IdentityMapping>> identityMap;
+
     public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs) {
+        this(moduleConfigs, Collections.<String, Map<Date, EditConfig.IdentityMapping>>emptyMap());
+    }
+
+    public Config(Map<String, Map<String, ModuleConfig>> moduleConfigs, Map<String, Map<Date,EditConfig.IdentityMapping>> identityMap) {
         this.moduleConfigs = moduleConfigs;
         Map<String, ModuleConfig> moduleNamesToConfigs = new HashMap<>();
         for (Entry<String, Map<String, ModuleConfig>> entry : moduleConfigs.entrySet()) {
             moduleNamesToConfigs.putAll(entry.getValue());
         }
         this.moduleNamesToConfigs = Collections.unmodifiableMap(moduleNamesToConfigs);
+        this.identityMap = identityMap;
     }
 
-    private Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
-            Services serviceTracker) {
+    public static Map<String, Map<String, Collection<ObjectName>>> getMappedInstances(Set<ObjectName> instancesToMap,
+                                                                                Map<String, Map<String, ModuleConfig>> configs) {
         Multimap<String, ObjectName> moduleToInstances = mapInstancesToModules(instancesToMap);
 
         Map<String, Map<String, Collection<ObjectName>>> retVal = Maps.newLinkedHashMap();
 
-        for (String namespace : moduleConfigs.keySet()) {
+        for (String namespace : configs.keySet()) {
 
             Map<String, Collection<ObjectName>> innerRetVal = Maps.newHashMap();
 
-            for (Entry<String, ModuleConfig> mbeEntry : moduleConfigs.get(namespace).entrySet()) {
+            for (Entry<String, ModuleConfig> mbeEntry : configs.get(namespace).entrySet()) {
 
                 String moduleName = mbeEntry.getKey();
                 Collection<ObjectName> 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);
@@ -86,15 +93,6 @@ public class Config {
         return retVal;
     }
 
-    private void addServices(Services serviceTracker, Collection<ObjectName> instances,
-            Collection<String> providedServices) {
-        for (ObjectName instanceOn : instances) {
-            for (String serviceName : providedServices) {
-                serviceTracker.addServiceEntry(serviceName, instanceOn);
-            }
-        }
-    }
-
     private static Multimap<String, ObjectName> mapInstancesToModules(Set<ObjectName> instancesToMap) {
         Multimap<String, ObjectName> retVal = HashMultimap.create();
 
@@ -111,20 +109,17 @@ public class Config {
     // }
 
     public Element toXml(Set<ObjectName> instancesToMap, Optional<String> maybeNamespace, Document document,
-            Element dataElement) {
-        Services serviceTracker = new Services();
+            Element dataElement, ServiceRegistryWrapper serviceTracker) {
 
         Map<String, Map<String, Collection<ObjectName>>> moduleToInstances = getMappedInstances(instancesToMap,
-                serviceTracker);
+                moduleConfigs);
 
         Element root = dataElement;
         if (maybeNamespace.isPresent()) {
-            XmlUtil.addNamespaceAttr(root, maybeNamespace.get());
+            root.setAttributeNS(maybeNamespace.get(), dataElement.getNodeName(), "xmlns");
         }
 
-        Element modulesElement = document.createElement(XmlNetconfConstants.MODULES_KEY);
-        XmlUtil.addNamespaceAttr(modulesElement,
-                XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+        Element modulesElement = XmlUtil.createElement(document, XmlNetconfConstants.MODULES_KEY, Optional.of(XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG));
         root.appendChild(modulesElement);
         for (String moduleNamespace : moduleToInstances.keySet()) {
             for (Entry<String, Collection<ObjectName>> moduleMappingEntry : moduleToInstances.get(moduleNamespace)
@@ -133,61 +128,84 @@ public class Config {
                 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));
-                    }
+                    continue;
+                }
+
+                for (ObjectName objectName : moduleMappingEntry.getValue()) {
+                    modulesElement.appendChild(mapping.toXml(objectName, serviceTracker, document, moduleNamespace));
                 }
 
             }
         }
 
-        root.appendChild(serviceTracker.toXml(serviceTracker.getMappedServices(), document));
+        root.appendChild(Services.toXml(serviceTracker, document));
 
         return root;
     }
 
-    private void addEmptyModulesCommented(Document document, Element root, String moduleNamespace,
-            Entry<String, Collection<ObjectName>> 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<String, Multimap<String, ModuleElementResolved>> fromXml(XmlElement xml, Set<ObjectName> instancesForFillingServiceRefMapping,
-                                                                        EditStrategyType defaultEditStrategyType) {
+
+    public Map<String, Multimap<String, ModuleElementResolved>> fromXmlModulesResolved(XmlElement xml, EditStrategyType defaultEditStrategyType, ServiceRegistryWrapper serviceTracker) {
+        Optional<XmlElement> modulesElement = getModulesElement(xml);
+        List<XmlElement> moduleElements = getModulesElementList(modulesElement);
+
         Map<String, Multimap<String, ModuleElementResolved>> retVal = Maps.newHashMap();
 
-        List<XmlElement> recognisedChildren = Lists.newArrayList();
+        for (XmlElement moduleElement : moduleElements) {
+            ResolvingStrategy<ModuleElementResolved> resolvingStrategy = new ResolvingStrategy<ModuleElementResolved>() {
+                @Override
+                public ModuleElementResolved resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker, String instanceName, String moduleNamespace, EditStrategyType defaultStrategy) {
+                    return moduleMapping.fromXml(moduleElement, serviceTracker,
+                            instanceName, moduleNamespace, defaultStrategy, identityMap);
+                }
+            };
 
-        Services serviceTracker = fromXmlServices(xml, recognisedChildren, instancesForFillingServiceRefMapping);
-        List<XmlElement> moduleElements = fromXmlModules(xml, recognisedChildren);
+            resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType, resolvingStrategy);
+        }
+        return retVal;
+    }
+
+    /**
+     * return a map containing namespace -> moduleName -> instanceName map. Attribute parsing is omitted.
+     */
+    public Map<String, Multimap<String, ModuleElementDefinition>> fromXmlModulesMap(XmlElement xml,
+            EditStrategyType defaultEditStrategyType, ServiceRegistryWrapper serviceTracker) {
+        Optional<XmlElement> modulesElement = getModulesElement(xml);
+        List<XmlElement> moduleElements = getModulesElementList(modulesElement);
 
-        xml.checkUnrecognisedElements(recognisedChildren);
+        Map<String, Multimap<String, ModuleElementDefinition>> retVal = Maps.newHashMap();
 
         for (XmlElement moduleElement : moduleElements) {
-            resolveModule(retVal, serviceTracker, moduleElement, defaultEditStrategyType);
-        }
+            ResolvingStrategy<ModuleElementDefinition> resolvingStrategy = new ResolvingStrategy<ModuleElementDefinition>() {
+                @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 List<XmlElement> fromXmlModules(XmlElement xml, List<XmlElement> recognisedChildren) {
-        Optional<XmlElement> modulesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY,
-                XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+    private static Optional<XmlElement> getModulesElement(XmlElement xml) {
+        return xml.getOnlyChildElementOptionally(XmlNetconfConstants.MODULES_KEY,
+                    XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+    }
+
+    private List<XmlElement> getModulesElementList(Optional<XmlElement> modulesElement) {
         List<XmlElement> moduleElements;
+
         if (modulesElement.isPresent()) {
             moduleElements = modulesElement.get().getChildElementsWithSameNamespace(XmlNetconfConstants.MODULE_KEY);
-            recognisedChildren.add(modulesElement.get());
             modulesElement.get().checkUnrecognisedElements(moduleElements);
         } else {
             moduleElements = Lists.newArrayList();
@@ -195,8 +213,8 @@ public class Config {
         return moduleElements;
     }
 
-    private void resolveModule(Map<String, Multimap<String, ModuleElementResolved>> retVal, Services serviceTracker,
-            XmlElement moduleElement, EditStrategyType defaultStrategy) {
+    private <T> void resolveModule(Map<String, Multimap<String, T>> retVal, ServiceRegistryWrapper serviceTracker,
+            XmlElement moduleElement, EditStrategyType defaultStrategy, ResolvingStrategy<T> resolvingStrategy) {
         XmlElement typeElement = moduleElement.getOnlyChildElementWithSameNamespace(XmlNetconfConstants.TYPE_KEY);
         Entry<String, String> prefixToNamespace = typeElement.findNamespaceOfTextContent();
         String moduleNamespace = prefixToNamespace.getValue();
@@ -208,48 +226,49 @@ public class Config {
 
         ModuleConfig moduleMapping = getModuleMapping(moduleNamespace, instanceName, factoryName);
 
-        Multimap<String, ModuleElementResolved> innerMap = retVal.get(moduleNamespace);
+        Multimap<String, T> innerMap = retVal.get(moduleNamespace);
         if (innerMap == null) {
             innerMap = HashMultimap.create();
             retVal.put(moduleNamespace, innerMap);
         }
 
-        ModuleElementResolved moduleElementResolved = moduleMapping.fromXml(moduleElement, serviceTracker,
+        T resolvedElement = resolvingStrategy.resolveElement(moduleMapping, moduleElement, serviceTracker,
                 instanceName, moduleNamespace, defaultStrategy);
 
-        innerMap.put(factoryName, moduleElementResolved);
+        innerMap.put(factoryName, resolvedElement);
     }
 
-    private Services fromXmlServices(XmlElement xml, List<XmlElement> recognisedChildren, Set<ObjectName> instancesForFillingServiceRefMapping) {
-        Optional<XmlElement> servicesElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SERVICES_KEY,
-                XmlNetconfConstants.URN_OPENDAYLIGHT_PARAMS_XML_NS_YANG_CONTROLLER_CONFIG);
+    public Services fromXmlServices(XmlElement xml) {
+        Optional<XmlElement> servicesElement = getServicesElement(xml);
 
-        Map<String, Map<String, String>> mappedServices;
+        Services services;
         if (servicesElement.isPresent()) {
-            mappedServices = Services.fromXml(servicesElement.get());
-            recognisedChildren.add(servicesElement.get());
+            services = Services.fromXml(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<String> services = ;
-            for (String serviceName : moduleConfig.getProvidedServices()) {
-                services.addServiceEntry(serviceName, existingON);
-            }
+            services = new Services();
         }
 
         return services;
     }
 
+    private static Optional<XmlElement> 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<XmlElement> servicesOpt = getServicesElement(parent);
+        Optional<XmlElement> modulesOpt = getModulesElement(parent);
+
+        List<XmlElement> 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),
@@ -277,4 +296,8 @@ public class Config {
         return moduleMapping;
     }
 
+    private interface ResolvingStrategy<T> {
+        public T resolveElement(ModuleConfig moduleMapping, XmlElement moduleElement, ServiceRegistryWrapper serviceTracker,
+                String instanceName, String moduleNamespace, EditStrategyType defaultStrategy);
+    }
 }