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 2015/11/24 22:19:35 UTC

[1/3] incubator-brooklyn git commit: Fix javadoc typo in SshTool

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master b264fc98a -> 3922b0ef9


Fix javadoc typo in SshTool

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

Branch: refs/heads/master
Commit: f8c7673604ebc572f3eb3bb01d0dfe53a954c8e6
Parents: 42c1113
Author: Aled Sage <al...@gmail.com>
Authored: Wed Nov 18 15:33:22 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Nov 18 22:05:45 2015 +0000

----------------------------------------------------------------------
 .../java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/f8c76736/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java
index df3947f..ee1a626 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/internal/ssh/SshTool.java
@@ -46,7 +46,7 @@ import org.apache.brooklyn.util.time.Duration;
 public interface SshTool extends ShellTool {
     
     /** Public-facing global config keys for Brooklyn are defined in ConfigKeys, 
-     * and have this prefix pre-prended to the config keys in this class. 
+     * and have this prefix prepended to the config keys in this class. 
      * These keys are detected from entity/global config and automatically applied to ssh executions. */
     public static final String BROOKLYN_CONFIG_KEY_PREFIX = "brooklyn.ssh.config.";
     


[3/3] incubator-brooklyn git commit: Closes #1045

Posted by sv...@apache.org.
Closes #1045

Adds WinRmTool interface


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

Branch: refs/heads/master
Commit: 3922b0ef94259e2b1789a18b5e1f83886113581c
Parents: b264fc9 f8c7673
Author: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Authored: Tue Nov 24 23:19:25 2015 +0200
Committer: Svetoslav Neykov <sv...@cloudsoftcorp.com>
Committed: Tue Nov 24 23:19:25 2015 +0200

----------------------------------------------------------------------
 .../core/entity/BrooklynConfigKeys.java         |   9 +
 .../util/core/internal/ssh/SshTool.java         |   2 +-
 .../AbstractSoftwareProcessWinRmDriver.java     |  31 ++-
 .../location/WinRmMachineLocationLiveTest.java  |   5 +-
 .../windows/WindowsPerformanceCounterFeed.java  |   2 +-
 .../location/winrm/WinRmMachineLocation.java    | 265 +++++++++----------
 .../util/core/internal/winrm/WinRmTool.java     |  74 ++++++
 .../core/internal/winrm/WinRmToolResponse.java  |  46 ++++
 .../internal/winrm/pywinrm/Winrm4jTool.java     | 156 +++++++++++
 .../WindowsPerformanceCounterFeedTest.java      |   3 +-
 10 files changed, 439 insertions(+), 154 deletions(-)
----------------------------------------------------------------------



[2/3] incubator-brooklyn git commit: Adds WinRmTool interface

Posted by sv...@apache.org.
Adds WinRmTool interface


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

Branch: refs/heads/master
Commit: 42c1113258bb66ba98c7fcde6c304dad4faed38c
Parents: ca89ed4
Author: Aled Sage <al...@gmail.com>
Authored: Wed Nov 18 15:33:07 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Nov 18 22:05:45 2015 +0000

----------------------------------------------------------------------
 .../core/entity/BrooklynConfigKeys.java         |   9 +
 .../AbstractSoftwareProcessWinRmDriver.java     |  31 ++-
 .../location/WinRmMachineLocationLiveTest.java  |   5 +-
 .../windows/WindowsPerformanceCounterFeed.java  |   2 +-
 .../location/winrm/WinRmMachineLocation.java    | 265 +++++++++----------
 .../util/core/internal/winrm/WinRmTool.java     |  74 ++++++
 .../core/internal/winrm/WinRmToolResponse.java  |  46 ++++
 .../internal/winrm/pywinrm/Winrm4jTool.java     | 156 +++++++++++
 .../WindowsPerformanceCounterFeedTest.java      |   3 +-
 9 files changed, 438 insertions(+), 153 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/core/src/main/java/org/apache/brooklyn/core/entity/BrooklynConfigKeys.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/BrooklynConfigKeys.java b/core/src/main/java/org/apache/brooklyn/core/entity/BrooklynConfigKeys.java
index 1185791..974f88c 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/BrooklynConfigKeys.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/BrooklynConfigKeys.java
@@ -177,6 +177,10 @@ public class BrooklynConfigKeys {
      * and have this prefix pre-prended to the config keys in this class. */
     public static final String BROOKLYN_SSH_CONFIG_KEY_PREFIX = "brooklyn.ssh.config.";
 
+    /** Public-facing global config keys for Brooklyn are defined in ConfigKeys, 
+     * and have this prefix pre-prended to the config keys in this class. */
+    public static final String BROOKLYN_WINRM_CONFIG_KEY_PREFIX = "brooklyn.winrm.config.";
+
     // some checks (this line, and a few Preconditions below) that the remote values aren't null, 
     // because they have some funny circular references
     static { assert BROOKLYN_SSH_CONFIG_KEY_PREFIX.equals(SshTool.BROOKLYN_CONFIG_KEY_PREFIX) : "static final initializer classload ordering problem"; }
@@ -186,6 +190,11 @@ public class BrooklynConfigKeys {
             "SshTool implementation to use (or null for default)", 
             null);
 
+    public static final ConfigKey<String> WINRM_TOOL_CLASS = newStringConfigKey(
+            BROOKLYN_WINRM_CONFIG_KEY_PREFIX + "winrmToolClass", 
+            "WinRmTool implementation to use (or null for default)", 
+            null);
+
     /**
      * @deprecated since 0.9.0; use {@link #SSH_TOOL_CLASS}
      */

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
index 4b3da76..6a39cc2 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
@@ -18,9 +18,17 @@
  */
 package org.apache.brooklyn.entity.software.base;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
+import static org.apache.brooklyn.util.JavaGroovyEquivalents.elvis;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
 import org.apache.brooklyn.api.entity.EntityLocal;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
@@ -31,6 +39,8 @@ import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.entity.software.base.lifecycle.NativeWindowsScriptRunner;
 import org.apache.brooklyn.entity.software.base.lifecycle.WinRmExecuteHelper;
 import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.util.core.internal.winrm.WinRmTool;
+import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.exceptions.ReferenceWithError;
@@ -42,16 +52,9 @@ import org.python.core.PyException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-
-import static org.apache.brooklyn.util.JavaGroovyEquivalents.elvis;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
 
 public abstract class AbstractSoftwareProcessWinRmDriver extends AbstractSoftwareProcessDriver implements NativeWindowsScriptRunner {
     private static final Logger LOG = LoggerFactory.getLogger(AbstractSoftwareProcessWinRmDriver.class);
@@ -260,7 +263,7 @@ public abstract class AbstractSoftwareProcessWinRmDriver extends AbstractSoftwar
     }
 
     public int executePsScriptNoRetry(List<String> psScript) {
-        return getLocation().executePsScriptNoRetry(psScript).getStatusCode();
+        return getLocation().executePsScript(ImmutableMap.of(WinRmTool.PROP_EXEC_TRIES, 1), psScript).getStatusCode();
     }
 
     public int executePsScript(List<String> psScript) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
index 4eab220..bfca1d8 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java
@@ -39,6 +39,7 @@ import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
 import org.apache.brooklyn.core.test.entity.TestApplication;
 import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
 import org.apache.brooklyn.util.text.Identifiers;
 import org.apache.brooklyn.util.time.Time;
 import org.slf4j.Logger;
@@ -59,8 +60,6 @@ import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
 
-import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
-
 /**
  * Tests execution of commands (batch and powershell) on Windows over WinRM, and of
  * file upload.
@@ -72,8 +71,8 @@ import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
  * Please update the docs if you encountered new situations, or change the behaviuor 
  * of existing use-cases.
  */
-public class WinRmMachineLocationLiveTest {
 
+public class WinRmMachineLocationLiveTest {
     private static final int MAX_EXECUTOR_THREADS = 100;
 
     /*

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
----------------------------------------------------------------------
diff --git a/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java b/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
index 73a76cb..b6f8684 100644
--- a/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
+++ b/software/winrm/src/main/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeed.java
@@ -20,7 +20,6 @@ package org.apache.brooklyn.feed.windows;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
-import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
 
 import java.util.Collection;
 import java.util.Iterator;
@@ -49,6 +48,7 @@ import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.feed.windows.WindowsPerformanceCounterPollConfig;
 import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
 import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
----------------------------------------------------------------------
diff --git a/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java b/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
index 3a7dbd5..7f2e4ed 100644
--- a/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
+++ b/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java
@@ -18,15 +18,11 @@
  */
 package org.apache.brooklyn.location.winrm;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -37,65 +33,74 @@ import org.apache.brooklyn.api.location.MachineDetails;
 import org.apache.brooklyn.api.location.MachineLocation;
 import org.apache.brooklyn.api.location.OsDetails;
 import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
 import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.ConfigUtils;
+import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
 import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.location.access.PortForwardManager;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.internal.ssh.SshTool;
+import org.apache.brooklyn.util.core.internal.winrm.WinRmTool;
+import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
+import org.apache.brooklyn.util.core.internal.winrm.pywinrm.Winrm4jTool;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.text.Strings;
 import org.apache.commons.codec.binary.Base64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Charsets;
+import com.google.common.base.Function;
 import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
+import com.google.common.collect.Iterables;
 import com.google.common.net.HostAndPort;
 import com.google.common.reflect.TypeToken;
 
-import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.stream.Streams;
-import org.apache.brooklyn.util.time.Duration;
-import org.apache.brooklyn.util.time.Time;
-
-import io.cloudsoft.winrm4j.winrm.WinRmTool;
-import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
-
 public class WinRmMachineLocation extends AbstractLocation implements MachineLocation {
 
     private static final Logger LOG = LoggerFactory.getLogger(WinRmMachineLocation.class);
 
-    // FIXME Respect `port` config when using {@link WinRmTool}
-    public static final ConfigKey<Integer> WINRM_PORT = ConfigKeys.newIntegerConfigKey(
-            "port",
-            "WinRM port to use when connecting to the remote machine",
-            5985);
-    
-    // TODO merge with {link SshTool#PROP_USER} and {@link SshMachineLocation#user}
-    public static final ConfigKey<String> USER = ConfigKeys.newStringConfigKey("user",
-            "Username to use when connecting to the remote machine");
-
-    // TODO merge with {link SshTool#PROP_PASSWORD}
-    public static final ConfigKey<String> PASSWORD = ConfigKeys.newStringConfigKey("password",
-            "Password to use when connecting to the remote machine");
-
-    public static final ConfigKey<Integer> COPY_FILE_CHUNK_SIZE_BYTES = ConfigKeys.newIntegerConfigKey("windows.copy.file.size.bytes",
-            "Size of file chunks (in bytes) to be used when copying a file to the remote server", 1024);
-
-     public static final ConfigKey<InetAddress> ADDRESS = ConfigKeys.newConfigKey(
+    public static final ConfigKey<InetAddress> ADDRESS = ConfigKeys.newConfigKey(
             InetAddress.class,
             "address",
             "Address of the remote machine");
 
+    public static final ConfigKey<Integer> WINRM_PORT = WinRmTool.PROP_PORT;
+    
+    // TODO merge with {link SshTool#PROP_USER} and {@link SshMachineLocation#user}?
+    public static final ConfigKey<String> USER = WinRmTool.PROP_USER;
+
+    // TODO merge with {link SshTool#PROP_PASSWORD}?
+    public static final ConfigKey<String> PASSWORD = WinRmTool.PROP_PASSWORD;
+
+    // TODO Delete once winrm4j supports this better?
+    public static final ConfigKey<Integer> COPY_FILE_CHUNK_SIZE_BYTES = WinRmTool.COPY_FILE_CHUNK_SIZE_BYTES;
+
+    // Note that SshTool's implementation class *must* use a different key name. Both may be used 
+    // within a location's configuration to indicate the implementation to use for WinRmTool and 
+    // for SshTool - that will require two different configuration values.
+    public static final ConfigKey<String> WINRM_TOOL_CLASS = ConfigKeys.newConfigKeyWithPrefixRemoved(
+            BrooklynConfigKeys.BROOKLYN_WINRM_CONFIG_KEY_PREFIX,
+            Preconditions.checkNotNull(BrooklynConfigKeys.WINRM_TOOL_CLASS, "static final initializer classload ordering problem"));
+
+    /**
+     * @deprecated since 0.9.0; config never read; will be removed in future version.
+     */
+    @Deprecated
     public static final ConfigKey<Integer> EXECUTION_ATTEMPTS = ConfigKeys.newIntegerConfigKey(
             "windows.exec.attempts",
             "Number of attempts to execute a remote command",
             1);
     
     // TODO See SshTool#PROP_SSH_TRIES, where it was called "sshTries"; remove duplication? Merge into one well-named thing?
-    public static final ConfigKey<Integer> EXEC_TRIES = ConfigKeys.newIntegerConfigKey(
-            "execTries", 
-            "Max number of times to attempt WinRM operations", 
-            10);
+    public static final ConfigKey<Integer> EXEC_TRIES = WinRmTool.PROP_EXEC_TRIES;
 
     public static final ConfigKey<Iterable<String>> PRIVATE_ADDRESSES = ConfigKeys.newConfigKey(
             new TypeToken<Iterable<String>>() {},
@@ -109,6 +114,43 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc
             "NAT'ed ports, giving the mapping from private TCP port to a public host:port", 
             null);
 
+    public static final Set<HasConfigKey<?>> ALL_WINRM_CONFIG_KEYS =
+            ImmutableSet.<HasConfigKey<?>>builder()
+                    .addAll(ConfigUtils.getStaticKeysOnClass(WinRmMachineLocation.class))
+                    .addAll(ConfigUtils.getStaticKeysOnClass(WinRmTool.class))
+                    .build();
+
+    public static final Set<String> ALL_SSH_CONFIG_KEY_NAMES =
+            ImmutableSet.copyOf(Iterables.transform(ALL_WINRM_CONFIG_KEYS, new Function<HasConfigKey<?>,String>() {
+                @Override
+                public String apply(HasConfigKey<?> input) {
+                    return input.getConfigKey().getName();
+                }
+            }));
+    
+    @Override
+    public void init() {
+        super.init();
+
+        // Register any pre-existing port-mappings with the PortForwardManager
+        Map<Integer, String> tcpPortMappings = getConfig(TCP_PORT_MAPPINGS);
+        if (tcpPortMappings != null) {
+            PortForwardManager pfm = (PortForwardManager) getManagementContext().getLocationRegistry().resolve("portForwardManager(scope=global)");
+            for (Map.Entry<Integer, String> entry : tcpPortMappings.entrySet()) {
+                int targetPort = entry.getKey();
+                HostAndPort publicEndpoint = HostAndPort.fromString(entry.getValue());
+                if (!publicEndpoint.hasPort()) {
+                    throw new IllegalArgumentException("Invalid portMapping ('"+entry.getValue()+"') for port "+targetPort+" in machine "+this);
+                }
+                pfm.associate(publicEndpoint.getHostText(), publicEndpoint, this, targetPort);
+            }
+        }
+    }
+    
+    public String getUser() {
+        return config().get(USER);
+    }
+
     @Override
     public InetAddress getAddress() {
         return getConfig(ADDRESS);
@@ -154,63 +196,66 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc
     }
 
     public WinRmToolResponse executeScript(List<String> script) {
-        int execTries = getRequiredConfig(EXEC_TRIES);
-        Collection<Throwable> exceptions = Lists.newArrayList();
-        for (int i = 0; i < execTries; i++) {
-            try {
-                return executeScriptNoRetry(script);
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                if (i == (execTries+1)) {
-                    LOG.info("Propagating WinRM exception (attempt "+(i+1)+" of "+execTries+")", e);
-                } else if (i == 0) {
-                    LOG.warn("Ignoring WinRM exception and retrying (attempt "+(i+1)+" of "+execTries+")", e);
-                } else {
-                    LOG.debug("Ignoring WinRM exception and retrying (attempt "+(i+1)+" of "+execTries+")", e);
-                }
-                exceptions.add(e);
-            }
-        }
-        throw Exceptions.propagate("failed to execute shell script", exceptions);
+        return executeScript(ImmutableMap.of(), script);
     }
-
-    protected WinRmToolResponse executeScriptNoRetry(List<String> script) {
-        WinRmTool winRmTool = WinRmTool.connect(getHostAndPort(), getUser(), getPassword());
-        WinRmToolResponse response = winRmTool.executeScript(script);
-        return response;
+    
+    public WinRmToolResponse executeScript(Map<?,?> props, List<String> script) {
+        WinRmTool tool = newWinRmTool(props);
+        return tool.executeScript(script);
     }
 
     public WinRmToolResponse executePsScript(String psScript) {
-        return executePsScript(ImmutableList.of(psScript));
+        return executePsScript(ImmutableMap.of(), ImmutableList.of(psScript));
     }
 
     public WinRmToolResponse executePsScript(List<String> psScript) {
-        int execTries = getRequiredConfig(EXEC_TRIES);
-        Collection<Throwable> exceptions = Lists.newArrayList();
-        for (int i = 0; i < execTries; i++) {
-            try {
-                return executePsScriptNoRetry(psScript);
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                if (i == (execTries+1)) {
-                    LOG.info("Propagating WinRM exception (attempt "+(i+1)+" of "+execTries+")", e);
-                } else if (i == 0) {
-                    LOG.warn("Ignoring WinRM exception and retrying after 5 seconds (attempt "+(i+1)+" of "+execTries+")", e);
-                    Time.sleep(Duration.FIVE_SECONDS);
+        return executePsScript(ImmutableMap.of(), psScript);
+    }
+    
+    public WinRmToolResponse executePsScript(Map<?,?> props, List<String> psScript) {
+        WinRmTool tool = newWinRmTool(props);
+        return tool.executePs(psScript);
+    }
+
+    protected WinRmTool newWinRmTool(Map<?,?> props) {
+        // TODO See comments/TODOs in SshMachineLocation.connectSsh()
+        try {
+            ConfigBag args = new ConfigBag();
+
+            for (Map.Entry<String,Object> entry: config().getBag().getAllConfig().entrySet()) {
+                String key = entry.getKey();
+                if (key.startsWith(WinRmTool.BROOKLYN_CONFIG_KEY_PREFIX)) {
+                    key = Strings.removeFromStart(key, WinRmTool.BROOKLYN_CONFIG_KEY_PREFIX);
+                    args.putStringKey(key, entry.getValue());
+                }
+            }
+
+            for (Map.Entry<String,Object> entry: config().getBag().getAllConfig().entrySet()) {
+                String key = entry.getKey();
+                if (ALL_SSH_CONFIG_KEY_NAMES.contains(key)) {
                 } else {
-                    LOG.debug("Ignoring WinRM exception and retrying after 5 seconds (attempt "+(i+1)+" of "+execTries+")", e);
-                    Time.sleep(Duration.FIVE_SECONDS);
+                    // this key is not applicable here; ignore it
+                    continue;
                 }
-                exceptions.add(e);
+                args.putStringKey(key, entry.getValue());
             }
-        }
-        throw Exceptions.propagate("failed to execute powershell script", exceptions);
-    }
 
-    public WinRmToolResponse executePsScriptNoRetry(List<String> psScript) {
-        WinRmTool winRmTool = WinRmTool.connect(getHostAndPort(), getUser(), getPassword());
-        WinRmToolResponse response = winRmTool.executePs(psScript);
-        return response;
+            args.putAll(props);
+            args.configure(SshTool.PROP_HOST, getAddress().getHostAddress());
+
+            if (LOG.isTraceEnabled()) LOG.trace("creating WinRM session for "+Sanitizer.sanitize(args));
+
+            // look up tool class
+            String toolClass = args.get(WINRM_TOOL_CLASS);
+            if (toolClass == null) toolClass = Winrm4jTool.class.getName();
+            WinRmTool tool = (WinRmTool) Class.forName(toolClass).getConstructor(Map.class).newInstance(args.getAllConfig());
+
+            if (LOG.isTraceEnabled()) LOG.trace("using ssh-tool {} (of type {}); props ", tool, toolClass);
+
+            return tool;
+        } catch (Exception e) {
+            throw Exceptions.propagate(e);
+        }
     }
 
     public int copyTo(File source, String destination) {
@@ -228,61 +273,15 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc
     }
 
     public int copyTo(InputStream source, String destination) {
-        executePsScript(ImmutableList.of("rm -ErrorAction SilentlyContinue " + destination));
-        try {
-            int chunkSize = getConfig(COPY_FILE_CHUNK_SIZE_BYTES);
-            byte[] inputData = new byte[chunkSize];
-            int bytesRead;
-            int expectedFileSize = 0;
-            while ((bytesRead = source.read(inputData)) > 0) {
-                byte[] chunk;
-                if (bytesRead == chunkSize) {
-                    chunk = inputData;
-                } else {
-                    chunk = Arrays.copyOf(inputData, bytesRead);
-                }
-                executePsScript(ImmutableList.of("If ((!(Test-Path " + destination + ")) -or ((Get-Item '" + destination + "').length -eq " +
-                        expectedFileSize + ")) {Add-Content -Encoding Byte -path " + destination +
-                        " -value ([System.Convert]::FromBase64String(\"" + new String(Base64.encodeBase64(chunk)) + "\"))}"));
-                expectedFileSize += bytesRead;
-            }
-
-            return 0;
-        } catch (java.io.IOException e) {
-            throw Exceptions.propagate(e);
-        }
-    }
-
-    @Override
-    public void init() {
-        super.init();
-
-        // Register any pre-existing port-mappings with the PortForwardManager
-        Map<Integer, String> tcpPortMappings = getConfig(TCP_PORT_MAPPINGS);
-        if (tcpPortMappings != null) {
-            PortForwardManager pfm = (PortForwardManager) getManagementContext().getLocationRegistry().resolve("portForwardManager(scope=global)");
-            for (Map.Entry<Integer, String> entry : tcpPortMappings.entrySet()) {
-                int targetPort = entry.getKey();
-                HostAndPort publicEndpoint = HostAndPort.fromString(entry.getValue());
-                if (!publicEndpoint.hasPort()) {
-                    throw new IllegalArgumentException("Invalid portMapping ('"+entry.getValue()+"') for port "+targetPort+" in machine "+this);
-                }
-                pfm.associate(publicEndpoint.getHostText(), publicEndpoint, this, targetPort);
-            }
-        }
+        return copyTo(ImmutableMap.of(), source, destination);
     }
-    public String getUser() {
-        return config().get(USER);
-    }
-
-    private String getPassword() {
-        return config().get(PASSWORD);
+    
+    public int copyTo(Map<?,?> props, InputStream source, String destination) {
+        WinRmTool tool = newWinRmTool(props);
+        WinRmToolResponse response = tool.copyToServer(source, destination);
+        return response.getStatusCode();
     }
 
-    private <T> T getRequiredConfig(ConfigKey<T> key) {
-        return checkNotNull(getConfig(key), "key %s must be set", key);
-    }
-    
     public static String getDefaultUserMetadataString() {
         // Using an encoded command obviates the need to escape
         String unencodePowershell = Joiner.on("\r\n").join(ImmutableList.of(

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
----------------------------------------------------------------------
diff --git a/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
new file mode 100644
index 0000000..6d2fb06
--- /dev/null
+++ b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java
@@ -0,0 +1,74 @@
+/*
+ * 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.util.core.internal.winrm;
+
+import static org.apache.brooklyn.core.config.ConfigKeys.newConfigKey;
+import static org.apache.brooklyn.core.config.ConfigKeys.newIntegerConfigKey;
+import static org.apache.brooklyn.core.config.ConfigKeys.newStringConfigKey;
+
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
+import org.apache.brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+@Beta
+public interface WinRmTool {
+
+    /** Public-facing global config keys for Brooklyn are defined in ConfigKeys, 
+     * and have this prefix prepended to the config keys in this class. 
+     * These keys are detected from entity/global config and automatically applied to ssh executions. */
+    String BROOKLYN_CONFIG_KEY_PREFIX = Preconditions.checkNotNull(BrooklynConfigKeys.BROOKLYN_WINRM_CONFIG_KEY_PREFIX, 
+            "static final initializer classload ordering problem");
+    
+    ConfigKey<String> PROP_HOST = newStringConfigKey("host", "Host to connect to (required)", null);
+    ConfigKey<Integer> PROP_PORT = newIntegerConfigKey("port", "WinRM port to use when connecting to the remote machine", 5985);
+    ConfigKey<String> PROP_USER = newStringConfigKey("user", "User to connect as", null);
+    ConfigKey<String> PROP_PASSWORD = newStringConfigKey("password", "Password to use to connect", null);
+
+    // TODO See SshTool#PROP_SSH_TRIES, where it was called "sshTries"; remove duplication? Merge into one well-named thing?
+    ConfigKey<Integer> PROP_EXEC_TRIES = ConfigKeys.newIntegerConfigKey(
+            "execTries", 
+            "Max number of times to attempt WinRM operations", 
+            10);
+
+    ConfigKey<Duration> PROP_EXEC_RETRY_DELAY = newConfigKey(
+            Duration.class,
+            "execRetryDelay",
+            "Max time between retries (backing off exponentially to this delay)",
+            Duration.TEN_SECONDS);
+
+    // May be ignored by implementations that more efficiently copy the file.
+    @Beta
+    ConfigKey<Integer> COPY_FILE_CHUNK_SIZE_BYTES = ConfigKeys.newIntegerConfigKey(
+            "windows.copy.file.size.bytes",
+            "Size of file chunks (in bytes) to be used when copying a file to the remote server", 
+            1024);
+
+    WinRmToolResponse executeScript(List<String> commands);
+
+    WinRmToolResponse executePs(List<String> commands);
+    
+    WinRmToolResponse copyToServer(InputStream source, String destination);
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmToolResponse.java
----------------------------------------------------------------------
diff --git a/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmToolResponse.java b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmToolResponse.java
new file mode 100644
index 0000000..a3ef888
--- /dev/null
+++ b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmToolResponse.java
@@ -0,0 +1,46 @@
+/*
+ * 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.util.core.internal.winrm;
+
+import com.google.common.annotations.Beta;
+
+@Beta
+public class WinRmToolResponse {
+    private final String stdout;
+    private final String stderr;
+    private final int statusCode;
+
+    public WinRmToolResponse(String stdout, String stderr, int statusCode) {
+        this.stdout = stdout;
+        this.stderr = stderr;
+        this.statusCode = statusCode;
+    }
+
+    public String getStdOut() {
+        return stdout;
+    }
+
+    public String getStdErr() {
+        return stderr;
+    }
+
+    public int getStatusCode() {
+        return statusCode;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java
----------------------------------------------------------------------
diff --git a/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java
new file mode 100644
index 0000000..9a4dfb9
--- /dev/null
+++ b/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java
@@ -0,0 +1,156 @@
+/*
+ * 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.util.core.internal.winrm.pywinrm;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
+
+@Beta
+public class Winrm4jTool implements org.apache.brooklyn.util.core.internal.winrm.WinRmTool {
+
+    private static final Logger LOG = LoggerFactory.getLogger(Winrm4jTool.class);
+
+    private final ConfigBag bag;
+    private final String host;
+    private final Integer port;
+    private final String user;
+    private final String password;
+    private final int execTries;
+    private final Duration execRetryDelay;
+
+    public Winrm4jTool(Map<String,?> config) {
+        this(ConfigBag.newInstance(config));
+    }
+    
+    public Winrm4jTool(ConfigBag config) {
+        this.bag = checkNotNull(config, "config bag");
+        host = getRequiredConfig(config, PROP_HOST);
+        port = getRequiredConfig(config, PROP_PORT);
+        user = getRequiredConfig(config, PROP_USER);
+        password = getRequiredConfig(config, PROP_PASSWORD);
+        execTries = getRequiredConfig(config, PROP_EXEC_TRIES);
+        execRetryDelay = getRequiredConfig(config, PROP_EXEC_RETRY_DELAY);
+    }
+    
+    @Override
+    public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeScript(final List<String> commands) {
+        return exec(new Callable<io.cloudsoft.winrm4j.winrm.WinRmToolResponse>() {
+            @Override public WinRmToolResponse call() throws Exception {
+                return connect().executeScript(commands);
+            }
+        });
+    }
+
+    @Override
+    public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executePs(final List<String> commands) {
+        return exec(new Callable<io.cloudsoft.winrm4j.winrm.WinRmToolResponse>() {
+            @Override public WinRmToolResponse call() throws Exception {
+                return connect().executePs(commands);
+            }
+        });
+    }
+
+    @Override
+    public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse copyToServer(InputStream source, String destination) {
+        executePs(ImmutableList.of("rm -ErrorAction SilentlyContinue " + destination));
+        try {
+            int chunkSize = getRequiredConfig(bag, COPY_FILE_CHUNK_SIZE_BYTES);
+            byte[] inputData = new byte[chunkSize];
+            int bytesRead;
+            int expectedFileSize = 0;
+            while ((bytesRead = source.read(inputData)) > 0) {
+                byte[] chunk;
+                if (bytesRead == chunkSize) {
+                    chunk = inputData;
+                } else {
+                    chunk = Arrays.copyOf(inputData, bytesRead);
+                }
+                executePs(ImmutableList.of("If ((!(Test-Path " + destination + ")) -or ((Get-Item '" + destination + "').length -eq " +
+                        expectedFileSize + ")) {Add-Content -Encoding Byte -path " + destination +
+                        " -value ([System.Convert]::FromBase64String(\"" + new String(Base64.encodeBase64(chunk)) + "\"))}"));
+                expectedFileSize += bytesRead;
+            }
+
+            return new org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse("", "", 0);
+        } catch (java.io.IOException e) {
+            throw Exceptions.propagate(e);
+        }
+    }
+
+    private org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse exec(Callable<io.cloudsoft.winrm4j.winrm.WinRmToolResponse> task) {
+        Collection<Throwable> exceptions = Lists.newArrayList();
+        for (int i = 0; i < execTries; i++) {
+            try {
+                return wrap(task.call());
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                Duration sleep = Duration.millis(Math.min(Math.pow(2, i) * 1000, execRetryDelay.toMilliseconds()));
+                if (i == (execTries+1)) {
+                    LOG.info("Propagating WinRM exception (attempt "+(i+1)+" of "+execTries+")", e);
+                } else if (i == 0) {
+                    LOG.warn("Ignoring WinRM exception and will retry after "+sleep+" (attempt "+(i+1)+" of "+execTries+")", e);
+                    Time.sleep(sleep);
+                } else {
+                    LOG.debug("Ignoring WinRM exception and will retry after "+sleep+" (attempt "+(i+1)+" of "+execTries+")", e);
+                    Time.sleep(sleep);
+                }
+                exceptions.add(e);
+            }
+        }
+        throw Exceptions.propagate("failed to execute command", exceptions);
+    }
+
+    private io.cloudsoft.winrm4j.winrm.WinRmTool connect() {
+        return io.cloudsoft.winrm4j.winrm.WinRmTool.connect(host+":"+port, user, password);
+    }
+    
+    private <T> T getRequiredConfig(ConfigBag bag, ConfigKey<T> key) {
+        T result = bag.get(key);
+        if (result == null) {
+            throw new IllegalArgumentException("Missing config "+key+" in "+Sanitizer.sanitize(bag));
+        }
+        return result;
+    }
+    
+    private org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse wrap(io.cloudsoft.winrm4j.winrm.WinRmToolResponse resp) {
+        return new org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse(resp.getStdOut(), resp.getStdErr(), resp.getStatusCode());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/42c11132/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedTest.java
----------------------------------------------------------------------
diff --git a/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedTest.java b/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedTest.java
index 059797b..17d7f0a 100644
--- a/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedTest.java
+++ b/software/winrm/src/test/java/org/apache/brooklyn/feed/windows/WindowsPerformanceCounterFeedTest.java
@@ -34,6 +34,7 @@ import org.apache.brooklyn.core.sensor.Sensors;
 import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import org.apache.brooklyn.test.EntityTestUtils;
+import org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse;
 import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -42,8 +43,6 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation;
 
-import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;