You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by sv...@apache.org on 2017/03/16 09:26:04 UTC

[2/4] brooklyn-server git commit: Testing rebind of SshFeed (historic state)

Testing rebind of SshFeed (historic state)


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/bcaf76bc
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/bcaf76bc
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/bcaf76bc

Branch: refs/heads/master
Commit: bcaf76bc8e5825c765d6511541ca5edb3372d6b0
Parents: ad73fcf
Author: Aled Sage <al...@gmail.com>
Authored: Wed Mar 15 19:24:24 2017 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Thu Mar 16 00:17:54 2017 +0000

----------------------------------------------------------------------
 .../mgmt/persist/XmlMementoSerializerTest.java  |  99 ++++++-
 .../mgmt/rebind/RebindHistoricSshFeedTest.java  |  95 +++++++
 .../ssh-feed-no-bundle-prefixes-zv7t8bim62      | 268 +++++++++++++++++++
 .../core/mgmt/rebind/ssh-feed-zv7t8bim62        | 268 +++++++++++++++++++
 4 files changed, 729 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bcaf76bc/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
index a2ffb16..ed07b07 100644
--- a/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/persist/XmlMementoSerializerTest.java
@@ -445,7 +445,104 @@ public class XmlMementoSerializerTest {
             }
         }
     }
-    
+
+    // A sanity-check, to confirm that normal loading works (before we look at success/failure of rename tests)
+    @Test
+    public void testNoRenameOsgiClass() throws Exception {
+        String bundlePath = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH;
+        String bundleUrl = "classpath:" + bundlePath;
+        String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_OBJECT;
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath);
+        
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        Bundle bundle = installBundle(mgmt, bundleUrl);
+
+        String bundlePrefix = bundle.getSymbolicName();
+        Class<?> osgiObjectClazz = bundle.loadClass(classname);
+        Object obj = Reflections.invokeConstructorFromArgs(osgiObjectClazz, "myval").get();
+
+        serializer = new XmlMementoSerializer<Object>(mgmt.getCatalogClassLoader(),
+                ImmutableMap.<String,String>of());
+        
+        serializer.setLookupContext(new LookupContextImpl(mgmt,
+                ImmutableList.<Entity>of(), ImmutableList.<Location>of(), ImmutableList.<Policy>of(),
+                ImmutableList.<Enricher>of(), ImmutableList.<Feed>of(), ImmutableList.<CatalogItem<?,?>>of(), true));
+
+        // i.e. prepended with bundle name
+        String serializedForm = Joiner.on("\n").join(
+                "<"+bundlePrefix+":"+classname+">",
+                "  <val>myval</val>",
+                "</"+bundlePrefix+":"+classname+">");
+
+        runRenamed(serializedForm, obj, ImmutableMap.<String, String>of());
+    }
+
+    @Test
+    public void testRenamedOsgiClassMovedBundle() throws Exception {
+        String bundlePath = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH;
+        String bundleUrl = "classpath:" + bundlePath;
+        String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_OBJECT;
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath);
+        
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        Bundle bundle = installBundle(mgmt, bundleUrl);
+        
+        String oldBundlePrefix = "com.old.symbolicname";
+        
+        String bundlePrefix = bundle.getSymbolicName();
+        Class<?> osgiObjectClazz = bundle.loadClass(classname);
+        Object obj = Reflections.invokeConstructorFromArgs(osgiObjectClazz, "myval").get();
+
+        serializer = new XmlMementoSerializer<Object>(mgmt.getCatalogClassLoader(),
+                ImmutableMap.of(oldBundlePrefix + ":" + classname, bundlePrefix + ":" + classname));
+        
+        serializer.setLookupContext(new LookupContextImpl(mgmt,
+                ImmutableList.<Entity>of(), ImmutableList.<Location>of(), ImmutableList.<Policy>of(),
+                ImmutableList.<Enricher>of(), ImmutableList.<Feed>of(), ImmutableList.<CatalogItem<?,?>>of(), true));
+
+        // i.e. prepended with bundle name
+        String serializedForm = Joiner.on("\n").join(
+                "<"+bundlePrefix+":"+classname+">",
+                "  <val>myval</val>",
+                "</"+bundlePrefix+":"+classname+">");
+
+        runRenamed(serializedForm, obj, ImmutableMap.<String, String>of(
+                bundlePrefix + ":" + classname, oldBundlePrefix + ":" + classname));
+    }
+
+    @Test
+    public void testRenamedOsgiClassWithoutBundlePrefixInRename() throws Exception {
+        String bundlePath = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_PATH;
+        String bundleUrl = "classpath:" + bundlePath;
+        String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_COM_EXAMPLE_OBJECT;
+        String oldClassname = "com.old.package.name.OldClassName";
+        TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath);
+        
+        mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build();
+        Bundle bundle = installBundle(mgmt, bundleUrl);
+        
+        String bundlePrefix = bundle.getSymbolicName();
+        
+        Class<?> osgiObjectClazz = bundle.loadClass(classname);
+        Object obj = Reflections.invokeConstructorFromArgs(osgiObjectClazz, "myval").get();
+
+        serializer = new XmlMementoSerializer<Object>(mgmt.getCatalogClassLoader(),
+                ImmutableMap.of(oldClassname, classname));
+        
+        serializer.setLookupContext(new LookupContextImpl(mgmt,
+                ImmutableList.<Entity>of(), ImmutableList.<Location>of(), ImmutableList.<Policy>of(),
+                ImmutableList.<Enricher>of(), ImmutableList.<Feed>of(), ImmutableList.<CatalogItem<?,?>>of(), true));
+
+        // i.e. prepended with bundle name
+        String serializedForm = Joiner.on("\n").join(
+                "<"+bundlePrefix+":"+classname+">",
+                "  <val>myval</val>",
+                "</"+bundlePrefix+":"+classname+">");
+
+        runRenamed(serializedForm, obj, ImmutableMap.<String, String>of(
+                bundlePrefix + ":" + classname, bundlePrefix + ":" + oldClassname));
+    }
+
     // TODO This doesn't get the bundleName - should we expect it to? Is this because of 
     // how we're using Felix? Would it also be true in Karaf?
     @Test(groups="Broken")

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bcaf76bc/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindHistoricSshFeedTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindHistoricSshFeedTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindHistoricSshFeedTest.java
new file mode 100644
index 0000000..548660c
--- /dev/null
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindHistoricSshFeedTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.mgmt.rebind;
+
+import java.io.File;
+
+import org.apache.brooklyn.api.mgmt.rebind.RebindExceptionHandler;
+import org.apache.brooklyn.api.mgmt.rebind.RebindManager.RebindFailureMode;
+import org.apache.brooklyn.api.objs.BrooklynObjectType;
+import org.apache.brooklyn.core.test.entity.TestApplication;
+import org.apache.brooklyn.util.os.Os;
+import org.apache.brooklyn.util.stream.Streams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.io.Files;
+
+public class RebindHistoricSshFeedTest extends RebindTestFixtureWithApp {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = LoggerFactory.getLogger(RebindHistoricSshFeedTest.class);
+
+    @Override
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+        
+    // The persisted state contains renamed classes, such as:
+    //   org.apache.brooklyn.feed.ssh.SshFeed$SshPollIdentifier : org.apache.brooklyn.feed.AbstractCommandFeed$CommandPollIdentifier
+    //
+    // These classnames include the bundle prefix (e.g. "org.apache.brooklyn.core:").
+    // Prior to 2017-01-20 (commit dfd4315565c6767ccb16979c8d098717ed2a4853), classes in whitelisted
+    // bundles would not include this prefix.
+    @Test
+    public void testSshFeed_2017_01() throws Exception {
+        addMemento(BrooklynObjectType.FEED, "ssh-feed", "zv7t8bim62");
+        rebind();
+    }
+    
+    // This test is similar to testSshFeed_2017_01, except the persisted state file has been 
+    // hand-crafted to remove the bundle prefixes for "org.apache.brooklyn.*" bundles.
+    @Test
+    public void testFoo_2017_01_withoutBundlePrefixes() throws Exception {
+        addMemento(BrooklynObjectType.FEED, "ssh-feed-no-bundle-prefixes", "zv7t8bim62");
+        rebind();
+    }
+    
+    @Override
+    protected TestApplication rebind() throws Exception {
+        RebindExceptionHandler exceptionHandler = RebindExceptionHandlerImpl.builder()
+                .danglingRefFailureMode(RebindFailureMode.FAIL_AT_END)
+                .rebindFailureMode(RebindFailureMode.FAIL_AT_END)
+                .addConfigFailureMode(RebindFailureMode.FAIL_AT_END)
+                .addPolicyFailureMode(RebindFailureMode.FAIL_AT_END)
+                .loadPolicyFailureMode(RebindFailureMode.FAIL_AT_END)
+                .build();
+        return super.rebind(RebindOptions.create().exceptionHandler(exceptionHandler));
+    }
+    
+    protected void addMemento(BrooklynObjectType type, String label, String id) throws Exception {
+        String mementoFilename = label+"-"+id;
+        String memento = Streams.readFullyString(getClass().getResourceAsStream(mementoFilename));
+        
+        File persistedFile = getPersistanceFile(type, id);
+        Files.write(memento.getBytes(), persistedFile);
+    }
+    
+    protected File getPersistanceFile(BrooklynObjectType type, String id) {
+        String dir;
+        switch (type) {
+            case FEED: dir = "feeds"; break;
+            default: throw new UnsupportedOperationException("type="+type);
+        }
+        return new File(mementoDir, Os.mergePaths(dir, id));
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bcaf76bc/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-feed-no-bundle-prefixes-zv7t8bim62
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-feed-no-bundle-prefixes-zv7t8bim62 b/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-feed-no-bundle-prefixes-zv7t8bim62
new file mode 100644
index 0000000..75fda08
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-feed-no-bundle-prefixes-zv7t8bim62
@@ -0,0 +1,268 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<feed>
+  <brooklynVersion>0.11.0-20170126.1332</brooklynVersion>
+  <type>org.apache.brooklyn.feed.ssh.SshFeed</type>
+  <id>zv7t8bim62</id>
+  <displayName>org.apache.brooklyn.feed.ssh.SshFeed</displayName>
+  <tags>
+    <string>SshFeed[ssh[cat /proc/uptime-&gt;machine.uptime], ssh[free | grep Mem:-&gt;machine.../52e0b96f</string>
+  </tags>
+  <uniqueTag>SshFeed[ssh[cat /proc/uptime-&gt;machine.uptime], ssh[free | grep Mem:-&gt;machine.../52e0b96f</uniqueTag>
+  <config>
+    <feed.onlyIfServiceUp type="boolean">false</feed.onlyIfServiceUp>
+    <machine>
+      <null/>
+    </machine>
+    <execAsCommand type="boolean">false</execAsCommand>
+    <polls>
+      <com.google.guava:com.google.common.collect.HashMultimap serialization="custom">
+        <unserializable-parents/>
+        <com.google.guava:com.google.common.collect.HashMultimap>
+          <default/>
+          <int>2</int>
+          <int>4</int>
+          <org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+            <command class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">uptime</instance>
+            </command>
+            <env class="org.apache.brooklyn.feed.ssh.SshPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>0</size>
+                  </default>
+                  <int>0</int>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+          <int>1</int>
+          <org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Double</type>
+              <name>machine.loadAverage</name>
+              <description>Current load average</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.feed.ssh.SshPollConfig$1"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance" reference="../../org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+            <command class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">ps -A -o pcpu</instance>
+            </command>
+            <env class="org.apache.brooklyn.feed.ssh.SshPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>0</size>
+                  </default>
+                  <int>0</int>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+          <int>1</int>
+          <org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Double</type>
+              <name>machine.cpu</name>
+              <description>Current CPU usage</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance" reference="../../org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier[2]/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+            <command class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">free | grep Mem:</instance>
+            </command>
+            <env class="org.apache.brooklyn.feed.ssh.SshPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>0</size>
+                  </default>
+                  <int>0</int>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+          <int>3</int>
+          <org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Long</type>
+              <name>machine.memory.used</name>
+              <description>Current memory usage</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance" reference="../../org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier[3]/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Long</type>
+              <name>machine.memory.free</name>
+              <description>Current free memory</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">free | grep Mem:</instance>
+            </commandSupplier>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Long</type>
+              <name>machine.memory.total</name>
+              <description>Total memory</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">free | grep Mem:</instance>
+            </commandSupplier>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+            <command class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">cat /proc/uptime</instance>
+            </command>
+            <env class="org.apache.brooklyn.feed.ssh.SshPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>0</size>
+                  </default>
+                  <int>0</int>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+          <int>1</int>
+          <org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>org.apache.brooklyn.util.time.Duration</type>
+              <name>machine.uptime</name>
+              <description>Current uptime</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance" reference="../../org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier[4]/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.feed.ssh.SshPollConfig>
+        </com.google.guava:com.google.common.collect.HashMultimap>
+      </com.google.guava:com.google.common.collect.HashMultimap>
+    </polls>
+  </config>
+</feed>

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/bcaf76bc/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-feed-zv7t8bim62
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-feed-zv7t8bim62 b/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-feed-zv7t8bim62
new file mode 100644
index 0000000..467aa84
--- /dev/null
+++ b/core/src/test/resources/org/apache/brooklyn/core/mgmt/rebind/ssh-feed-zv7t8bim62
@@ -0,0 +1,268 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<feed>
+  <brooklynVersion>0.11.0-20170126.1332</brooklynVersion>
+  <type>org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed</type>
+  <id>zv7t8bim62</id>
+  <displayName>org.apache.brooklyn.feed.ssh.SshFeed</displayName>
+  <tags>
+    <string>SshFeed[ssh[cat /proc/uptime-&gt;machine.uptime], ssh[free | grep Mem:-&gt;machine.../52e0b96f</string>
+  </tags>
+  <uniqueTag>SshFeed[ssh[cat /proc/uptime-&gt;machine.uptime], ssh[free | grep Mem:-&gt;machine.../52e0b96f</uniqueTag>
+  <config>
+    <feed.onlyIfServiceUp type="boolean">false</feed.onlyIfServiceUp>
+    <machine>
+      <null/>
+    </machine>
+    <execAsCommand type="boolean">false</execAsCommand>
+    <polls>
+      <com.google.guava:com.google.common.collect.HashMultimap serialization="custom">
+        <unserializable-parents/>
+        <com.google.guava:com.google.common.collect.HashMultimap>
+          <default/>
+          <int>2</int>
+          <int>4</int>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+            <command class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">uptime</instance>
+            </command>
+            <env class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>0</size>
+                  </default>
+                  <int>0</int>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+          <int>1</int>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Double</type>
+              <name>machine.loadAverage</name>
+              <description>Current load average</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$1"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+            <command class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">ps -A -o pcpu</instance>
+            </command>
+            <env class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>0</size>
+                  </default>
+                  <int>0</int>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+          <int>1</int>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Double</type>
+              <name>machine.cpu</name>
+              <description>Current CPU usage</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier[2]/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+            <command class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">free | grep Mem:</instance>
+            </command>
+            <env class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>0</size>
+                  </default>
+                  <int>0</int>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+          <int>3</int>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Long</type>
+              <name>machine.memory.used</name>
+              <description>Current memory usage</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier[3]/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Long</type>
+              <name>machine.memory.free</name>
+              <description>Current free memory</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">free | grep Mem:</instance>
+            </commandSupplier>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>java.lang.Long</type>
+              <name>machine.memory.total</name>
+              <description>Total memory</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">free | grep Mem:</instance>
+            </commandSupplier>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+            <command class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance">
+              <instance class="string">cat /proc/uptime</instance>
+            </command>
+            <env class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$CombiningEnvSupplier">
+              <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+                <unserializable-parents/>
+                <list>
+                  <default>
+                    <size>0</size>
+                  </default>
+                  <int>0</int>
+                </list>
+              </dynamicEnvironmentSupplier>
+            </env>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier>
+          <int>1</int>
+          <org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+            <sensor class="attributeSensor">
+              <type>org.apache.brooklyn.util.time.Duration</type>
+              <name>machine.uptime</name>
+              <description>Current uptime</description>
+              <persistence>REQUIRED</persistence>
+            </sensor>
+            <onsuccess class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onfailure class="com.google.guava:com.google.common.base.Functions$ConstantFunction"/>
+            <onexception class="com.google.guava:com.google.common.base.Functions$ConstantFunction" reference="../onfailure"/>
+            <checkSuccess class="org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig$1" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig/checkSuccess"/>
+            <suppressDuplicates>false</suppressDuplicates>
+            <enabled>true</enabled>
+            <period>30000</period>
+            <commandSupplier class="com.google.guava:com.google.common.base.Suppliers$SupplierOfInstance" reference="../../org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshFeed_-SshPollIdentifier[4]/command"/>
+            <dynamicEnvironmentSupplier class="MutableList" serialization="custom">
+              <unserializable-parents/>
+              <list>
+                <default>
+                  <size>0</size>
+                </default>
+                <int>0</int>
+              </list>
+            </dynamicEnvironmentSupplier>
+          </org.apache.brooklyn.core:org.apache.brooklyn.feed.ssh.SshPollConfig>
+        </com.google.guava:com.google.common.collect.HashMultimap>
+      </com.google.guava:com.google.common.collect.HashMultimap>
+    </polls>
+  </config>
+</feed>