Refactor persister: require only capabilities referenced by the xml snapshot. 44/3144/4
authorTomas Olvecky <tolvecky@cisco.com>
Wed, 27 Nov 2013 17:04:40 +0000 (18:04 +0100)
committerTomas Olvecky <tolvecky@cisco.com>
Thu, 28 Nov 2013 21:02:18 +0000 (22:02 +0100)
Also repair logback bug preventing it from taking snapshot configuration.

Change-Id: I48c0383441bfaee3c192159f5158f833e7e1d938
Signed-off-by: Tomas Olvecky <tolvecky@cisco.com>
15 files changed:
opendaylight/config/config-persister-api/src/main/java/org/opendaylight/controller/config/persist/api/Persister.java
opendaylight/config/config-persister-file-adapter/src/main/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapter.java
opendaylight/config/config-persister-file-adapter/src/test/java/org/opendaylight/controller/config/persist/storage/file/FileStorageAdapterTest.java
opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/ContextSetterImpl.java
opendaylight/config/logback-config/src/main/java/org/opendaylight/controller/config/yang/logback/config/LogbackModule.java
opendaylight/netconf/config-persister-impl/pom.xml
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-all.txt [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-stripped.txt [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/test/resources/logback-test.xml [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/test/resources/snapshot.xml [new file with mode: 0644]
opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfITTest.java

index f9c9301b887a1cdf36593cfeb2e0410b4a1725f1..78ce2b9718734dece2dc934befc950b0e09bc54a 100644 (file)
@@ -12,7 +12,7 @@ import com.google.common.base.Optional;
 
 import java.io.Closeable;
 import java.io.IOException;
-import java.util.Set;
+import java.util.SortedSet;
 
 /**
  * Base interface for persister implementation.
@@ -25,8 +25,15 @@ public interface Persister extends Closeable {
 
     public static interface ConfigSnapshotHolder {
 
+        /**
+         * Get part of get-config document that contains just
+         */
         String getConfigSnapshot();
 
-        Set<String> getCapabilities();
+
+        /**
+         * Get only required capabilities referenced by the snapshot.
+         */
+        SortedSet<String> getCapabilities();
     }
 }
index 5098c6f352b515e43f0adba943aeef65575802be..d3508939d78ffc9b676c7b0e5c85e51be789ddfe 100644 (file)
@@ -12,7 +12,6 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Charsets;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.collect.Sets;
 import com.google.common.io.Files;
 import org.apache.commons.lang3.StringUtils;
 import org.opendaylight.controller.config.persist.api.storage.StorageAdapter;
@@ -25,6 +24,8 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 /**
  * StorageAdapter that stores configuration in a plan file.
@@ -187,7 +188,7 @@ public class FileStorageAdapter implements StorageAdapter {
 
         private boolean inLastConfig, inLastSnapshot;
         private final StringBuffer snapshotBuffer = new StringBuffer();
-        private final Set<String> caps = Sets.newHashSet();
+        private final SortedSet<String> caps = new TreeSet<>();
 
         @Override
         public String getResult() {
@@ -231,7 +232,7 @@ public class FileStorageAdapter implements StorageAdapter {
                 return Optional.of(xmlContent);
         }
 
-        Set<String> getCapabilities() throws IOException, SAXException, ParserConfigurationException {
+        SortedSet<String> getCapabilities() throws IOException, SAXException, ParserConfigurationException {
             return caps;
         }
 
@@ -250,9 +251,9 @@ public class FileStorageAdapter implements StorageAdapter {
     private class PersistedConfigImpl implements ConfigSnapshotHolder {
 
         private final String snapshot;
-        private final Set<String> caps;
+        private final SortedSet<String> caps;
 
-        public PersistedConfigImpl(Optional<String> configSnapshot, Set<String> capabilities) {
+        public PersistedConfigImpl(Optional<String> configSnapshot, SortedSet<String> capabilities) {
             this.snapshot = configSnapshot.get();
             this.caps = capabilities;
         }
@@ -263,7 +264,7 @@ public class FileStorageAdapter implements StorageAdapter {
         }
 
         @Override
-        public Set<String> getCapabilities() {
+        public SortedSet<String> getCapabilities() {
             return caps;
         }
     }
index c7d37dcbd8c65b3cea42541943ebf3d8eae0870d..886298a88177e242ce2127e874131dbac0413551 100644 (file)
@@ -12,7 +12,6 @@ import com.google.common.base.Charsets;
 import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
-import com.google.common.collect.Sets;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -21,8 +20,8 @@ import org.opendaylight.controller.config.persist.api.Persister;
 import java.io.File;
 import java.nio.file.Files;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -56,7 +55,7 @@ public class FileStorageAdapterTest {
             }
 
             @Override
-            public Set<String> getCapabilities() {
+            public SortedSet<String> getCapabilities() {
                 return createCaps();
             }
         };
@@ -83,8 +82,8 @@ public class FileStorageAdapterTest {
         assertEquals(createCaps(), lastConf.get().getCapabilities());
     }
 
-    private Set<String> createCaps() {
-        Set<String> caps = Sets.newHashSet();
+    private SortedSet<String> createCaps() {
+        SortedSet<String> caps = new TreeSet<>();
 
         caps.add("cap1");
         caps.add("cap2");
@@ -104,7 +103,7 @@ public class FileStorageAdapterTest {
             }
 
             @Override
-            public Set<String> getCapabilities() {
+            public SortedSet<String> getCapabilities() {
                 return createCaps();
             }
         };
@@ -142,7 +141,7 @@ public class FileStorageAdapterTest {
             }
 
             @Override
-            public Set<String> getCapabilities() {
+            public SortedSet<String> getCapabilities() {
                 return createCaps();
             }
         };
@@ -199,8 +198,8 @@ public class FileStorageAdapterTest {
             }
 
             @Override
-            public Set<String> getCapabilities() {
-                return Collections.<String> emptySet();
+            public SortedSet<String> getCapabilities() {
+                return new TreeSet<>();
             }
         } );
     }
index 205f123b1de6e28cea70fbb6bacac91b9162d2fb..02fba141b338872fe6a1fefa473d56110f9777d8 100644 (file)
@@ -101,13 +101,15 @@ public class ContextSetterImpl implements ContextSetter, Closeable {
 
     private void addNewAppenders(Map<String, Appender<ILoggingEvent>> appendersMap, LoggerTO logger,
             ch.qos.logback.classic.Logger logbackLogger, Optional<Set<Appender<ILoggingEvent>>> appendersBefore) {
-        for (String appenderName : logger.getAppenders()) {
-            if (appendersMap.containsKey(appenderName)) {
-                logbackLogger.addAppender(appendersMap.get(appenderName));
-                classLogger.trace("Logger {}: Adding new appender: {}", logger.getLoggerName(), appenderName);
-            } else {
-                throw new IllegalStateException("No appender " + appenderName
-                        + " found. This error should have been discovered by validation");
+        if (logger.getAppenders() != null) {
+            for (String appenderName : logger.getAppenders()) {
+                if (appendersMap.containsKey(appenderName)) {
+                    logbackLogger.addAppender(appendersMap.get(appenderName));
+                    classLogger.trace("Logger {}: Adding new appender: {}", logger.getLoggerName(), appenderName);
+                } else {
+                    throw new IllegalStateException("No appender " + appenderName
+                            + " found. This error should have been discovered by validation");
+                }
             }
         }
     }
index 9cbcd47b20dbe48cc6cfacdade3d50a8c3ed474e..d75bf6367a6d393d7ee2773a5d903733ae2b21aa 100644 (file)
@@ -54,11 +54,12 @@ public final class LogbackModule extends org.opendaylight.controller.config.yang
                     "LoggerName needs to be set", loggersJmxAttribute);
             JmxAttributeValidationException.checkCondition(!loggerToValidate.getLevel().isEmpty(),
                     "Level needs to be set", loggersJmxAttribute);
-
-            for (String appenderName : loggerToValidate.getAppenders()) {
-                JmxAttributeValidationException.checkCondition(appenderNames.contains(appenderName), "Appender "
-                        + appenderName + " referenced by logger " + loggerToValidate.getLoggerName()
-                        + " not present in configuration, present appenders: " + appenderNames, loggersJmxAttribute);
+            if (loggerToValidate.getAppenders() != null) {
+                for (String appenderName : loggerToValidate.getAppenders()) {
+                    JmxAttributeValidationException.checkCondition(appenderNames.contains(appenderName), "Appender "
+                            + appenderName + " referenced by logger " + loggerToValidate.getLoggerName()
+                            + " not present in configuration, present appenders: " + appenderNames, loggersJmxAttribute);
+                }
             }
 
         }
index 21ecd3133949f2665a7132a417b929951692efc5..e9cf2b70bdc6a86f2a8c353944cbd9a13ea41fd9 100644 (file)
             <version>${bgpcep.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java
new file mode 100644 (file)
index 0000000..0440cbd
--- /dev/null
@@ -0,0 +1,119 @@
+/**
+ * @author Tomas Olvecky
+ *
+ * 11 2013
+ *
+ * Copyright (c) 2013 by Cisco Systems, Inc.
+ * All rights reserved.
+ */
+package org.opendaylight.controller.netconf.persist.impl;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.opendaylight.controller.config.persist.api.Persister.ConfigSnapshotHolder;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHolder {
+    private static final Logger logger = LoggerFactory.getLogger(CapabilityStrippingConfigSnapshotHolder.class);
+
+    private final String configSnapshot;
+    private final StripCapabilitiesResult stripCapabilitiesResult;
+
+    public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set<String> capabilities, Pattern ignoredMissingCapabilityRegex) {
+        final XmlElement configElement = XmlElement.fromDomElement(snapshot);
+        configSnapshot = XmlUtil.toString(configElement.getDomElement());
+        stripCapabilitiesResult = stripCapabilities(configElement, capabilities, ignoredMissingCapabilityRegex);
+    }
+
+    private static class StripCapabilitiesResult {
+        private final SortedSet<String> requiredCapabilities, missingNamespaces;
+
+        private StripCapabilitiesResult(SortedSet<String> requiredCapabilities, SortedSet<String> missingNamespaces) {
+            this.requiredCapabilities = Collections.unmodifiableSortedSet(requiredCapabilities);
+            this.missingNamespaces = Collections.unmodifiableSortedSet(missingNamespaces);
+        }
+    }
+
+
+    @VisibleForTesting
+    static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set<String> allCapabilitiesFromHello,
+                                                     Pattern ignoredMissingCapabilityRegex) {
+        // collect all namespaces
+        Set<String> foundNamespacesInXML = getNamespaces(configElement);
+        logger.trace("All capabilities {}\nFound namespaces in XML {}", allCapabilitiesFromHello, foundNamespacesInXML);
+        // required are referenced both in xml and hello
+        SortedSet<String> requiredCapabilities = new TreeSet<>();
+        // can be removed
+        Set<String> obsoleteCapabilities = new HashSet<>();
+        // are in xml but not in hello
+        SortedSet<String> missingNamespaces = new TreeSet<>(foundNamespacesInXML);
+        for (String capability : allCapabilitiesFromHello) {
+            String namespace = capability.replaceAll("\\?.*","");
+            if (foundNamespacesInXML.contains(namespace)) {
+                requiredCapabilities.add(capability);
+                checkState(missingNamespaces.remove(namespace));
+            } else {
+                obsoleteCapabilities.add(capability);
+            }
+        }
+
+        logger.trace("Required capabilities {}, \nObsolete capabilities {}",
+                requiredCapabilities, obsoleteCapabilities);
+
+        for(Iterator<String> iterator = missingNamespaces.iterator();iterator.hasNext(); ){
+            String capability = iterator.next();
+            if (ignoredMissingCapabilityRegex.matcher(capability).matches()){
+                logger.trace("Ignoring missing capability {}", capability);
+                iterator.remove();
+            }
+        }
+        if (missingNamespaces.size() > 0) {
+            logger.warn("Some capabilities are missing: {}", missingNamespaces);
+        }
+        return new StripCapabilitiesResult(requiredCapabilities, missingNamespaces);
+    }
+
+    static Set<String> getNamespaces(XmlElement element){
+        Set<String> result = new HashSet<>();
+        for (Entry<String,Attr> attribute : element.getAttributes().entrySet()) {
+            if  (attribute.getKey().startsWith("xmlns")){
+                result.add(attribute.getValue().getValue());
+            }
+        }
+        //element.getAttributes()
+        for(XmlElement child: element.getChildElements()) {
+            result.addAll(getNamespaces(child));
+        }
+        return result;
+    }
+
+    @Override
+    public SortedSet<String> getCapabilities() {
+        return stripCapabilitiesResult.requiredCapabilities;
+    }
+
+    @VisibleForTesting
+    Set<String> getMissingNamespaces(){
+        return stripCapabilitiesResult.missingNamespaces;
+    }
+
+    @Override
+    public String getConfigSnapshot() {
+        return configSnapshot;
+    }
+}
index 747965c0645faeb3cb02914fa260867252da20e5..748303160df8d35ba78adb781abd6707c7c37067 100644 (file)
@@ -43,6 +43,7 @@ import java.net.InetSocketAddress;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 /**
  * Responsible for listening for notifications from netconf containing latest
@@ -53,6 +54,8 @@ import java.util.Set;
 public class ConfigPersisterNotificationHandler implements NotificationListener, Closeable {
 
     private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
+    private static final int NETCONF_SEND_ATTEMPT_MS_DELAY = 1000;
+    private static final int NETCONF_SEND_ATTEMPTS = 20;
 
     private final InetSocketAddress address;
     private final EventLoopGroup nettyThreadgroup;
@@ -66,22 +69,25 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
 
     private final ObjectName on = DefaultCommitOperationMXBean.objectName;
 
-    public static final long DEFAULT_TIMEOUT = 40000L;
+    public static final long DEFAULT_TIMEOUT = 120000L;// 120 seconds until netconf must be stable
     private final long timeout;
+    private final Pattern ignoredMissingCapabilityRegex;
 
     public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address,
-            MBeanServerConnection mbeanServer) {
-        this(persister, address, mbeanServer, DEFAULT_TIMEOUT);
+            MBeanServerConnection mbeanServer, Pattern ignoredMissingCapabilityRegex) {
+        this(persister, address, mbeanServer, DEFAULT_TIMEOUT, ignoredMissingCapabilityRegex);
+
     }
 
     public ConfigPersisterNotificationHandler(Persister persister, InetSocketAddress address,
-            MBeanServerConnection mbeanServer, long timeout) {
+            MBeanServerConnection mbeanServer, long timeout, Pattern ignoredMissingCapabilityRegex) {
         this.persister = persister;
         this.address = address;
         this.mbeanServer = mbeanServer;
         this.timeout = timeout;
 
         this.nettyThreadgroup = new NioEventLoopGroup();
+        this.ignoredMissingCapabilityRegex = ignoredMissingCapabilityRegex;
     }
 
     public void init() throws InterruptedException {
@@ -220,18 +226,8 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
 
     private void handleAfterCommitNotification(final CommitJMXNotification notification) {
         try {
-            final XmlElement configElement = XmlElement.fromDomElement(notification.getConfigSnapshot());
-            persister.persistConfig(new Persister.ConfigSnapshotHolder() {
-                @Override
-                public String getConfigSnapshot() {
-                    return XmlUtil.toString(configElement.getDomElement());
-                }
-
-                @Override
-                public Set<String> getCapabilities() {
-                    return notification.getCapabilities();
-                }
-            });
+            persister.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(),
+                    notification.getCapabilities(), ignoredMissingCapabilityRegex));
             logger.debug("Configuration persisted successfully");
         } catch (IOException e) {
             throw new RuntimeException("Unable to persist configuration snapshot", e);
@@ -256,7 +252,7 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
         NetconfMessage message = createEditConfigMessage(xmlToBePersisted, "/netconfOp/editConfig.xml");
 
         // sending message to netconf
-        NetconfMessage responseMessage = netconfClient.sendMessage(message);
+        NetconfMessage responseMessage = netconfClient.sendMessage(message, NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY);
 
         XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument());
         Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
@@ -265,7 +261,7 @@ public class ConfigPersisterNotificationHandler implements NotificationListener,
         checkIsOk(element, responseMessage);
         response.append(XmlUtil.toString(responseMessage.getDocument()));
         response.append("}");
-        responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml"));
+        responseMessage = netconfClient.sendMessage(getNetconfMessageFromResource("/netconfOp/commit.xml"), NETCONF_SEND_ATTEMPTS, NETCONF_SEND_ATTEMPT_MS_DELAY);
 
         element = XmlElement.fromDomDocument(responseMessage.getDocument());
         Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY));
index 241830ddc33ebb74a4d95168887f48c225d77bc8..036cb757ae9b0d20e88b22f696e64afc7284731f 100644 (file)
@@ -20,12 +20,14 @@ import org.slf4j.LoggerFactory;
 import javax.management.MBeanServer;
 import java.lang.management.ManagementFactory;
 import java.net.InetSocketAddress;
+import java.util.regex.Pattern;
 
 public class ConfigPersisterActivator implements BundleActivator {
 
     private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class);
 
     private final static MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
+    private static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex";
 
     private ConfigPersisterNotificationHandler configPersisterNotificationHandler;
 
@@ -33,6 +35,7 @@ public class ConfigPersisterActivator implements BundleActivator {
 
     private static final String NETCONF_CONFIG_PERSISTER_PREFIX = "netconf.config.persister.";
     public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX =  "storageAdapterClass";
+    public static final String DEFAULT_IGNORED_REGEX = "^urn:ietf:params:xml:ns:netconf:base:1.0";
 
     @Override
     public void start(final BundleContext context) throws Exception {
@@ -50,12 +53,20 @@ public class ConfigPersisterActivator implements BundleActivator {
             }
         };
 
+        String regexProperty = propertiesProvider.getProperty(IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX);
+        String regex;
+        if (regexProperty != null) {
+            regex = regexProperty;
+        } else {
+            regex = DEFAULT_IGNORED_REGEX;
+        }
+        Pattern ignoredMissingCapabilityRegex = Pattern.compile(regex);
         PersisterImpl persister = PersisterImpl.createFromProperties(propertiesProvider);
 
         InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context,
                 "Netconf is not configured, persister is not operational");
         configPersisterNotificationHandler = new ConfigPersisterNotificationHandler(persister, address,
-                platformMBeanServer);
+                platformMBeanServer, ignoredMissingCapabilityRegex);
 
         // offload initialization to another thread in order to stop blocking activator
         Runnable initializationRunnable = new Runnable() {
diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java
new file mode 100644 (file)
index 0000000..7e4e048
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * @author Tomas Olvecky
+ *
+ * 11 2013
+ *
+ * Copyright (c) 2013 by Cisco Systems, Inc.
+ * All rights reserved.
+ */
+package org.opendaylight.controller.netconf.persist.impl;
+
+import com.google.common.collect.Sets;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Element;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.assertEquals;
+
+public class CapabilityStrippingConfigSnapshotHolderTest {
+
+    @Test
+    public void  testCapabilityStripping() throws Exception {
+        Set<String> allCapabilities = readLines("/capabilities-all.txt");
+        Set<String> expectedCapabilities = readLines("/capabilities-stripped.txt");
+        String snapshotAsString = readToString("/snapshot.xml");
+        Element element = XmlUtil.readXmlToElement(snapshotAsString);
+        {
+            CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
+                    element, allCapabilities, Pattern.compile(
+                    ConfigPersisterActivator.DEFAULT_IGNORED_REGEX
+            ));
+            assertEquals(expectedCapabilities, tested.getCapabilities());
+            assertEquals(Collections.emptySet(), tested.getMissingNamespaces());
+        }
+        {
+            // test regex
+            CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder(
+                    element, allCapabilities, Pattern.compile(
+                    "^bar"
+            ));
+            assertEquals(expectedCapabilities, tested.getCapabilities());
+            assertEquals(Sets.newHashSet(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX.substring(1)),
+                    tested.getMissingNamespaces());
+        }
+    }
+
+    private Set<String> readLines(String fileName) throws IOException {
+        return new HashSet<>(IOUtils.readLines(getClass().getResourceAsStream(fileName)));
+    }
+
+    private String readToString(String fileName) throws IOException {
+        return IOUtils.toString(getClass().getResourceAsStream(fileName));
+    }
+
+}
diff --git a/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-all.txt b/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-all.txt
new file mode 100644 (file)
index 0000000..84c85b7
--- /dev/null
@@ -0,0 +1,20 @@
+urn:opendaylight:l2:types?module=opendaylight-l2-types&revision=2013-08-27
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:threadpool?module=threadpool&revision=2013-04-09
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
+urn:ietf:params:netconf:capability:candidate:1.0
+urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04
+urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor?module=netty-event-executor&revision=2013-11-12
+urn:ietf:params:xml:ns:yang:rpc-context?module=rpc-context&revision=2013-06-17
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
+urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
+urn:ietf:params:netconf:capability:rollback-on-error:1.0
+urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2010-09-24
+urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl?module=threadpool-impl&revision=2013-04-05
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:logback:config?module=config-logging&revision=2013-07-16
+urn:opendaylight:yang:extension:yang-ext?module=yang-ext&revision=2013-07-09
+urn:opendaylight:params:xml:ns:yang:iana?module=iana&revision=2013-08-16
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:common?module=opendaylight-md-sal-common&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:ieee754?module=ieee754&revision=2013-08-19
diff --git a/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-stripped.txt b/opendaylight/netconf/config-persister-impl/src/test/resources/capabilities-stripped.txt
new file mode 100644 (file)
index 0000000..4a0b7ff
--- /dev/null
@@ -0,0 +1,5 @@
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding?module=opendaylight-md-sal-binding&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom?module=opendaylight-md-sal-dom&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:config?module=config&revision=2013-04-05
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl?module=opendaylight-sal-binding-broker-impl&revision=2013-10-28
+urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl?module=opendaylight-sal-dom-broker-impl&revision=2013-10-28
diff --git a/opendaylight/netconf/config-persister-impl/src/test/resources/logback-test.xml b/opendaylight/netconf/config-persister-impl/src/test/resources/logback-test.xml
new file mode 100644 (file)
index 0000000..a4ff3ab
--- /dev/null
@@ -0,0 +1,13 @@
+<configuration scan="true">
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="error">
+        <appender-ref ref="STDOUT" />
+    </root>
+    <logger name="org.opendaylight.controller.netconf.persist.impl" level="TRACE"/>
+</configuration>
diff --git a/opendaylight/netconf/config-persister-impl/src/test/resources/snapshot.xml b/opendaylight/netconf/config-persister-impl/src/test/resources/snapshot.xml
new file mode 100644 (file)
index 0000000..a6a57d7
--- /dev/null
@@ -0,0 +1,103 @@
+<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+        <module>
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:schema-service-singleton</type>
+            <name>yang-schema-service</name>
+        </module>
+        <module>
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:hash-map-data-store</type>
+            <name>hash-map-data-store</name>
+        </module>
+        <module>
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">prefix:dom-broker-impl</type>
+            <name>dom-broker</name>
+            <data-store xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl">
+                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+                <name>ref_hash-map-data-store</name>
+            </data-store>
+        </module>
+        <module>
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-broker-impl</type>
+            <name>binding-broker-impl</name>
+            <notification-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+                <name>ref_binding-notification-broker</name>
+            </notification-service>
+            <data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+                <name>ref_binding-data-broker</name>
+            </data-broker>
+        </module>
+        <module>
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:runtime-generated-mapping</type>
+            <name>runtime-mapping-singleton</name>
+        </module>
+        <module>
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-notification-broker</type>
+            <name>binding-notification-broker</name>
+        </module>
+        <module>
+            <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-data-broker</type>
+            <name>binding-data-broker</name>
+            <dom-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+                <name>ref_dom-broker</name>
+            </dom-broker>
+            <mapping-service xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+                <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+                <name>ref_runtime-mapping-singleton</name>
+            </mapping-service>
+        </module>
+    </modules>
+    <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
+        <service>
+            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:schema-service</type>
+            <instance>
+                <name>ref_yang-schema-service</name>
+                <provider>/config/modules/module[name='schema-service-singleton']/instance[name='yang-schema-service']</provider>
+            </instance>
+        </service>
+        <service>
+            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-notification-service</type>
+            <instance>
+                <name>ref_binding-notification-broker</name>
+                <provider>/config/modules/module[name='binding-notification-broker']/instance[name='binding-notification-broker']</provider>
+            </instance>
+        </service>
+        <service>
+            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-data-store</type>
+            <instance>
+                <name>ref_hash-map-data-store</name>
+                <provider>/config/modules/module[name='hash-map-data-store']/instance[name='hash-map-data-store']</provider>
+            </instance>
+        </service>
+        <service>
+            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-broker-osgi-registry</type>
+            <instance>
+                <name>ref_binding-broker-impl</name>
+                <provider>/config/modules/module[name='binding-broker-impl']/instance[name='binding-broker-impl']</provider>
+            </instance>
+        </service>
+        <service>
+            <type xmlns:binding-impl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding-impl:binding-dom-mapping-service</type>
+            <instance>
+                <name>ref_runtime-mapping-singleton</name>
+                <provider>/config/modules/module[name='runtime-generated-mapping']/instance[name='runtime-mapping-singleton']</provider>
+            </instance>
+        </service>
+        <service>
+            <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+            <instance>
+                <name>ref_dom-broker</name>
+                <provider>/config/modules/module[name='dom-broker-impl']/instance[name='dom-broker']</provider>
+            </instance>
+        </service>
+        <service>
+            <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-data-broker</type>
+            <instance>
+                <name>ref_binding-data-broker</name>
+                <provider>/config/modules/module[name='binding-data-broker']/instance[name='binding-data-broker']</provider>
+            </instance>
+        </service>
+    </services>
+</data>
index d08bb957e1cbd60aec752b1e9507870e3a28db03..d35ac28d8aa4c275c0561a72bab158802a07dd6c 100644 (file)
@@ -27,6 +27,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
 import javax.management.ObjectName;
 import javax.net.ssl.SSLContext;
 import javax.xml.parsers.ParserConfigurationException;
@@ -65,6 +66,7 @@ import org.opendaylight.controller.netconf.impl.mapping.ExiDecoderHandler;
 import org.opendaylight.controller.netconf.impl.mapping.ExiEncoderHandler;
 import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl;
 import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler;
+import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator;
 import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
 import org.opendaylight.controller.netconf.util.test.XmlFileLoader;
 import org.opendaylight.controller.netconf.util.xml.ExiParameters;
@@ -215,7 +217,8 @@ public class NetconfITTest extends AbstractConfigTest {
         Persister persister = mock(Persister.class);
         doReturn("mockPersister").when(persister).toString();
         doReturn(Optional.absent()).when(persister).loadLastConfig();
-        ConfigPersisterNotificationHandler h = new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer());
+        ConfigPersisterNotificationHandler h =
+                new ConfigPersisterNotificationHandler(persister, tcpAddress, ManagementFactory.getPlatformMBeanServer(), Pattern.compile(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX));
         h.init();
     }