From f3ddf8fc030d8fba312601d73895e416acfbc421 Mon Sep 17 00:00:00 2001 From: Valentin Aitken Date: Mon, 12 Oct 2015 12:32:11 +0300 Subject: [PATCH] WinRM commands Use executePsCommand and executeCmdCommand instead of executeScript WinRM Script tests --- .../catalog/CatalogYamlTemplateTest.java | 2 +- .../Windows7zipBlueprintLiveTest.java | 2 +- .../location/jclouds/JcloudsLocation.java | 6 +- brooklyn-server/pom.xml | 2 +- .../AbstractSoftwareProcessWinRmDriver.java | 67 ++++--- .../base/lifecycle/WinRmExecuteHelper.java | 5 +- ...WindowsProcessWinrmExitStatusLiveTest.java | 18 ++ ...WindowsScriptsWinrmExitStatusLiveTest.java | 163 ++++++++++++++++++ .../WinRmMachineLocationLiveTest.java | 30 ++-- .../winrm/NaiveWindowsScriptRunner.java} | 12 +- .../location/winrm/WinRmMachineLocation.java | 81 +++++++-- .../core/internal/winrm/WinRmScriptTool.java | 115 ++++++++++++ .../util/core/internal/winrm/WinRmTool.java | 35 ++++ .../internal/winrm/pywinrm/Winrm4jTool.java | 30 +++- .../java/org/apache/brooklyn/util/os/Os.java | 11 ++ 15 files changed, 513 insertions(+), 66 deletions(-) create mode 100644 brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/WindowsScriptsWinrmExitStatusLiveTest.java rename brooklyn-server/software/{base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/NativeWindowsScriptRunner.java => winrm/src/main/java/org/apache/brooklyn/location/winrm/NaiveWindowsScriptRunner.java} (72%) create mode 100644 brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmScriptTool.java diff --git a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java index 165fd49f7b..27e0dbbbf7 100644 --- a/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java +++ b/brooklyn-server/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlTemplateTest.java @@ -23,6 +23,7 @@ import java.util.List; +import com.google.common.collect.Iterables; import org.apache.brooklyn.api.entity.Application; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; @@ -41,7 +42,6 @@ import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.test.support.TestResourceUnavailableException; import org.apache.brooklyn.util.osgi.OsgiTestResources; -import org.python.google.common.collect.Iterables; import org.testng.Assert; import org.testng.TestListenerAdapter; import org.testng.TestNG; diff --git a/brooklyn-server/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/Windows7zipBlueprintLiveTest.java b/brooklyn-server/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/Windows7zipBlueprintLiveTest.java index bc3e62764e..cb1ee0a28d 100644 --- a/brooklyn-server/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/Windows7zipBlueprintLiveTest.java +++ b/brooklyn-server/launcher/src/test/java/org/apache/brooklyn/launcher/blueprints/Windows7zipBlueprintLiveTest.java @@ -88,7 +88,7 @@ public void test7zip() throws Exception { String password = Strings.getFirstWordAfter(winRMAddress, ":"); WinRmTool winRmTool = WinRmTool.connect(ipPort, user, password); - WinRmToolResponse winRmResponse = winRmTool.executePs(ImmutableList.of("(Get-Item \"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\").name")); + WinRmToolResponse winRmResponse = winRmTool.executePs("(Get-Item \"C:\\\\Program Files\\\\7-Zip\\\\7z.exe\").name"); LOG.info("winRmResponse: code="+winRmResponse.getStatusCode()+"; out="+winRmResponse.getStdOut()+"; err="+winRmResponse.getStdErr()); return "7z.exe\r\n".equals(winRmResponse.getStdOut()); diff --git a/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java index 20897d95c8..b1bd07082c 100644 --- a/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java +++ b/brooklyn-server/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java @@ -862,7 +862,7 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable String scriptContent = ResourceUtils.create(this).getResourceAsString(setupScriptItem); String script = TemplateProcessor.processTemplateContents(scriptContent, getManagementContext(), substitutions); if (windows) { - ((WinRmMachineLocation)machineLocation).executeScript(ImmutableList.copyOf((script.replace("\r", "").split("\n")))); + ((WinRmMachineLocation)machineLocation).executeCommand(script); } else { ((SshMachineLocation)machineLocation).execCommands("Customizing node " + this, ImmutableList.of(script)); } @@ -2664,7 +2664,9 @@ protected LoginCredentials waitForWinRmAvailable(final ComputeService computeSer public Boolean call() { for (Map.Entry entry : machinesToTry.entrySet()) { WinRmMachineLocation machine = entry.getKey(); - WinRmToolResponse response = machine.executeScript( + + // TODO explicitely execute no retry + WinRmToolResponse response = machine.executeCommand( ImmutableMap.of(WinRmTool.PROP_EXEC_TRIES.getName(), 1), ImmutableList.of("echo testing")); boolean success = (response.getStatusCode() == 0); diff --git a/brooklyn-server/pom.xml b/brooklyn-server/pom.xml index c497ee4e03..4e6012ba8c 100644 --- a/brooklyn-server/pom.xml +++ b/brooklyn-server/pom.xml @@ -146,7 +146,7 @@ 1.1.1 0.8.1 4.0.0 - 0.1.0 + 0.2.0 1.4.0 diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java index 6a39cc2aa2..9a779c94e2 100644 --- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java +++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java @@ -29,6 +29,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; +import com.google.common.collect.ImmutableList; import org.apache.brooklyn.api.entity.EntityLocal; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.api.sensor.AttributeSensor; @@ -36,9 +37,11 @@ import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; 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.NaiveWindowsScriptRunner; import org.apache.brooklyn.location.winrm.WinRmMachineLocation; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.internal.winrm.WinRmScriptTool; 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; @@ -48,15 +51,13 @@ import org.apache.brooklyn.util.stream.Streams; import org.apache.brooklyn.util.text.Strings; import org.apache.brooklyn.util.time.Duration; -import org.python.core.PyException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -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 { +public abstract class AbstractSoftwareProcessWinRmDriver extends AbstractSoftwareProcessDriver implements NaiveWindowsScriptRunner { private static final Logger LOG = LoggerFactory.getLogger(AbstractSoftwareProcessWinRmDriver.class); AttributeSensor WINDOWS_USERNAME = Sensors.newStringSensor("windows.username", @@ -176,7 +177,7 @@ public int copyResource(Map sshFlags, String source, String targ if (createParentDir) { createDirectory(getDirectory(target), "Creating resource directory"); } - + InputStream stream = null; try { Tasks.setBlockingDetails("retrieving resource "+source+" for copying across"); @@ -205,15 +206,15 @@ protected void createDirectory(String directoryName, String summaryForLogging) { } @Override - public Integer executeNativeOrPsCommand(Map flags, String regularCommand, String powerShellCommand, String phase, Boolean allowNoOp) { + public Integer executeNativeOrPsCommand(Map flags, String regularCommand, final String powerShellCommand, String summaryForLogging, Boolean allowNoOp) { if (Strings.isBlank(regularCommand) && Strings.isBlank(powerShellCommand)) { if (allowNoOp) { return new WinRmToolResponse("", "", 0).getStatusCode(); } else { - throw new IllegalStateException(String.format("Exactly one of cmd or psCmd must be set for %s of %s", phase, entity)); + throw new IllegalStateException(String.format("Exactly one of cmd or psCmd must be set for %s of %s", summaryForLogging, entity)); } } else if (!Strings.isBlank(regularCommand) && !Strings.isBlank(powerShellCommand)) { - throw new IllegalStateException(String.format("%s and %s cannot both be set for %s of %s", regularCommand, powerShellCommand, phase, entity)); + throw new IllegalStateException(String.format("%s and %s cannot both be set for %s of %s", regularCommand, powerShellCommand, summaryForLogging, entity)); } ByteArrayOutputStream stdIn = new ByteArrayOutputStream(); @@ -237,9 +238,9 @@ public Integer executeNativeOrPsCommand(Map flags, String regularCommand, String WinRmToolResponse response; if (Strings.isBlank(regularCommand)) { - response = getLocation().executePsScript(ImmutableList.of(powerShellCommand)); + response = getLocation().executePsScript(powerShellCommand); } else { - response = getLocation().executeScript(ImmutableList.of(regularCommand)); + response = getLocation().executeCommand(regularCommand); } if (currentTask != null) { @@ -250,6 +251,24 @@ public Integer executeNativeOrPsCommand(Map flags, String regularCommand, String return response.getStatusCode(); } + public int executeNativeOrPsScript(Map flags, List regularCommands, final List powerShellCommands, String summaryForLogging, Boolean allowNoOp) { + if ((regularCommands == null || regularCommands.size() == 0) && (powerShellCommands == null || powerShellCommands.size() == 0)) { + if (allowNoOp) { + return new WinRmToolResponse("", "", 0).getStatusCode(); + } else { + throw new IllegalStateException(String.format("Exactly one of cmd or psCmd must be set for %s", summaryForLogging)); + } + } + + MutableMap scriptProps = MutableMap.of(WinRmScriptTool.PROP_SUMMARY.getName(), summaryForLogging); + WinRmScriptTool scriptTool = new WinRmScriptTool(scriptProps, this); + if (regularCommands == null || regularCommands.size() == 0) { + return scriptTool.execPsScript(flags, powerShellCommands); + } else { + return scriptTool.execNativeScript(flags, regularCommands); + } + } + private void writeToStream(ByteArrayOutputStream stream, String string) { try { stream.write(string.getBytes()); @@ -258,18 +277,30 @@ private void writeToStream(ByteArrayOutputStream stream, String string) { } } - public int execute(List script) { - return getLocation().executeScript(script).getStatusCode(); + public int execute(String script) { + return getLocation().executeCommand(script).getStatusCode(); } - public int executePsScriptNoRetry(List psScript) { - return getLocation().executePsScript(ImmutableMap.of(WinRmTool.PROP_EXEC_TRIES, 1), psScript).getStatusCode(); + public int executePsCommandNoRetry(String psCommand) { + return getLocation().executePsScript(ImmutableMap.of(WinRmTool.PROP_EXEC_TRIES, 1), ImmutableList.of(psCommand)).getStatusCode(); + } + + public int executeCommandNoRetry(String command) { + return getLocation().executeCommand(ImmutableMap.of(WinRmTool.PROP_EXEC_TRIES, 1), ImmutableList.of(command)).getStatusCode(); } public int executePsScript(List psScript) { return getLocation().executePsScript(psScript).getStatusCode(); } + public WinRmToolResponse executeCommand(String cmdCommand) { + return getLocation().executeCommand(cmdCommand); + } + + public WinRmToolResponse executePsScript(String psCommand) { + return getLocation().executePsScript(psCommand); + } + public int copyTo(File source, String destination) { return getLocation().copyTo(source, destination); } @@ -279,11 +310,7 @@ public int copyTo(InputStream source, String destination) { } public void rebootAndWait() { - try { - executePsScriptNoRetry(ImmutableList.of("Restart-Computer -Force")); - } catch (PyException e) { - // Restarting the computer will cause the command to fail; ignore the exception and continue - } + executePsCommandNoRetry("Restart-Computer -Force"); waitForWinRmStatus(false, entity.getConfig(VanillaWindowsProcess.REBOOT_BEGUN_TIMEOUT)); waitForWinRmStatus(true, entity.getConfig(VanillaWindowsProcess.REBOOT_COMPLETED_TIMEOUT)).getWithError(); } @@ -298,7 +325,7 @@ private ReferenceWithError waitForWinRmStatus(final boolean requiredSta @Override public Boolean call() throws Exception { try { - return (execute(ImmutableList.of("hostname")) == 0) == requiredStatus; + return (execute("hostname") == 0) == requiredStatus; } catch (Exception e) { return !requiredStatus; } diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java index 65d05abac3..7e5b88c7c8 100644 --- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java +++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/WinRmExecuteHelper.java @@ -32,6 +32,7 @@ import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.api.mgmt.TaskQueueingContext; import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; +import org.apache.brooklyn.location.winrm.NaiveWindowsScriptRunner; import org.apache.brooklyn.util.core.task.DynamicTasks; import org.apache.brooklyn.util.core.task.TaskBuilder; import org.apache.brooklyn.util.core.task.Tasks; @@ -51,7 +52,7 @@ public class WinRmExecuteHelper { private Task task; - protected final NativeWindowsScriptRunner runner; + protected final NaiveWindowsScriptRunner runner; public final String summary; private String command; @@ -62,7 +63,7 @@ public class WinRmExecuteHelper { protected Predicate resultCodeCheck = Predicates.alwaysTrue(); protected ByteArrayOutputStream stdout, stderr, stdin; - public WinRmExecuteHelper(NativeWindowsScriptRunner runner, String summary) { + public WinRmExecuteHelper(NaiveWindowsScriptRunner runner, String summary) { this.runner = runner; this.summary = summary; } diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmExitStatusLiveTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmExitStatusLiveTest.java index 998d987484..469a7cd69a 100644 --- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmExitStatusLiveTest.java +++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/VanillaWindowsProcessWinrmExitStatusLiveTest.java @@ -34,6 +34,7 @@ import org.apache.brooklyn.entity.software.base.test.location.WindowsTestFixture; import org.apache.brooklyn.location.winrm.WinRmMachineLocation; import org.apache.brooklyn.test.EntityTestUtils; +import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -147,6 +148,23 @@ public void testExecPsWithZeroExitCodes() { EntityTestUtils.assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, false); } + @Test(groups = "Live", expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "Execution failed, invalid result 1.*") + public void testExecPsWithExitCodesWithMultiLineCommands() throws Throwable { + try { + VanillaWindowsProcess entity = app.createAndManageChild(EntitySpec.create(VanillaWindowsProcess.class) + .configure( + VanillaWindowsProcess.INSTALL_POWERSHELL_COMMAND, + "exit 1") + .configure(VanillaWindowsProcess.LAUNCH_POWERSHELL_COMMAND, "Write-Host launch") + .configure(VanillaWindowsProcess.CHECK_RUNNING_POWERSHELL_COMMAND, "Write-Host checkrunning") + .configure(VanillaWindowsProcess.STOP_POWERSHELL_COMMAND, "Write-Host stop")); + + app.start(ImmutableList.of(machine)); + } catch (Exception e) { + throw Exceptions.getFirstInteresting(e); + } + } + @Test(groups = "Live") public void testPreInstallNonZeroExitCode() { runExecNonZeroExitCode("pre-install-command"); diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/WindowsScriptsWinrmExitStatusLiveTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/WindowsScriptsWinrmExitStatusLiveTest.java new file mode 100644 index 0000000000..b297153e81 --- /dev/null +++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/WindowsScriptsWinrmExitStatusLiveTest.java @@ -0,0 +1,163 @@ +/* + * 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.entity.software.base; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.MachineProvisioningLocation; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.internal.BrooklynProperties; +import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.entity.software.base.test.location.WindowsTestFixture; +import org.apache.brooklyn.location.winrm.WinRmMachineLocation; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.internal.winrm.WinRmTool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Map; + +public class WindowsScriptsWinrmExitStatusLiveTest { + private static final Logger LOG = LoggerFactory.getLogger(WindowsScriptsWinrmExitStatusLiveTest.class); + + private static final String INVALID_CMD = "thisCommandDoesNotExistAEFafiee3d"; + + protected ManagementContextInternal mgmt; + protected MachineProvisioningLocation location; + protected WinRmMachineLocation machine; + protected VanillaWindowsProcess vanillaWindowsProcessEntity; + protected AbstractSoftwareProcessWinRmDriver winRmDriver; + + @BeforeClass(alwaysRun=true) + public void setUpClass() throws Exception { + mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault()); + + location = WindowsTestFixture.setUpWindowsLocation(mgmt); + machine = location.obtain(ImmutableMap.of()); + + vanillaWindowsProcessEntity = mgmt.getEntityManager().createEntity(EntitySpec.create(VanillaWindowsProcess.class)); + + winRmDriver = new VanillaWindowsProcessWinRmDriver((EntityLocal)vanillaWindowsProcessEntity, machine); + } + + @AfterClass(alwaysRun=true) + public void tearDownClass() throws Exception { + try { + try { + if (location != null) location.release(machine); + } finally { + if (mgmt != null) Entities.destroyAll(mgmt); + } + } catch (Throwable t) { + LOG.error("Caught exception in tearDownClass method", t); + } finally { + mgmt = null; + } + } + + @Test(groups = "Live") + public void testExecWithZeroExitCodes() { + Assert.assertEquals(winRmDriver.executeNativeOrPsScript(MutableMap.of(), ImmutableList.of("echo HiWorld"), null, "working-script", false), 0, "an echo script should return zero"); + Assert.assertEquals(winRmDriver.executeNativeOrPsScript(MutableMap.of(), ImmutableList.of(INVALID_CMD), null, "working-script", false), 1, "an echo script should return zero"); + } + + @Test(groups = "Live") + public void testExecWithNonZeroExitCodes() { + List simpleScript = ImmutableList.of(INVALID_CMD, "exit /b %ERRORLEVEL%"); + Assert.assertEquals(winRmDriver.executeNativeOrPsScript(MutableMap.of(), simpleScript, null, "non-working-script", false), 9009, "an echo script should return one (invalid command)"); + + List simpleScript2 = ImmutableList.of("echo hi", "exit /b 333"); + Assert.assertEquals(winRmDriver.executeNativeOrPsScript(MutableMap.of(), simpleScript2, null, "non-working-script", false), 333, "an echo script should return one (invalid command)"); + } + + //// https://social.technet.microsoft.com/forums/en-US/6f414857-8288-4b11-b8b0-c6fa2339ed7c/run-net-script-powershell-exit-and-published-data + @Test(groups = "Live") + public void testPsExecWithNonZeroExitCodes() { + List simpleScript = ImmutableList.of(INVALID_CMD, "exit 444"); + + // in the current winrm implementation we use a cmd call to implement powershell commands + // If a native powershell call is made in winrm then the exit code is 1 + // NOTE: exit code is 1 on a native powershell implementation + Assert.assertEquals(machine.executePsScript(simpleScript).getStatusCode(), 444, "an echo script should return one (invalid command)"); + } + + //// https://technet.microsoft.com/en-us/library/hh847796.aspx + @Test(groups = "Live") + public void testPsExecWithErrorActionPreferenceExitCodes() { + Map flags = MutableMap.of("scriptFilename", null); + + List simpleScript = ImmutableList.of( + "$ErrorActionPreference = \"Stop\"", + "Write-Error \"writing error\"", + "Write-Host goodbye"); + Assert.assertEquals(winRmDriver.executeNativeOrPsScript(flags, null, simpleScript, "non-working-script", false), 1, "simple script should return 1"); + String scriptFilename1 = (String) flags.get("scriptFilename"); + + Assert.assertEquals(machine.executeCommand(ImmutableMap.of(WinRmTool.PROP_EXEC_TRIES, 1), ImmutableList.of("powershell -File %TEMP%\\" + scriptFilename1)).getStatusCode(), 0, "consecutive failing commands script should return 444"); + } + + @Test(groups = "Live") + public void testPsExecWithConsecutiveNonZeroExitCodes() { + Map flags = MutableMap.of("scriptFilename", null); + + List simpleScript = ImmutableList.of( + "Write-Host hi", + "exit 111"); + Assert.assertEquals(winRmDriver.executeNativeOrPsScript(flags, null, simpleScript, "non-working-script", false), 1, "simple script should return 1"); + String scriptFilename1 = (String)flags.get("scriptFilename"); + + Assert.assertEquals(winRmDriver.executeCommandNoRetry("powershell -File %TEMP%\\" + scriptFilename1), 111, "consecutive failing commands script should return 111"); + + //// + + List consecutiveFailingScript = MutableList.of( + "Write-Host hi", + "& $env:temp\\" + scriptFilename1, + "Write-Host bye", + "exit 222"); + Assert.assertEquals(winRmDriver.executeNativeOrPsScript(flags, null, consecutiveFailingScript, "non-working-script", false), 1, "consecutive failing commands script should return 1"); + String scriptFilename2 = (String)flags.get("scriptFilename"); + + Assert.assertEquals(winRmDriver.executeCommandNoRetry("powershell -File %TEMP%\\" + scriptFilename2), 222, "consecutive failing commands script should return 222"); + + //// + + List complicatedPsScript = MutableList.of( + "Write-Host hi", + "& $env:temp\\" + scriptFilename1, + "If ($lastexitcode -ne 0) {", + "exit $lastexitcode", + "}", + "Write-Host bye", + "exit 333"); + Assert.assertEquals(winRmDriver.executeNativeOrPsScript(flags, null, complicatedPsScript, "non-working-script", false), 1, "Again returns 1, but this time because it failed before bye command"); + String scriptFilename3 = (String)flags.get("scriptFilename"); + + Assert.assertEquals(winRmDriver.executeCommandNoRetry("powershell -File %TEMP%\\" + scriptFilename3), 111, "consecutive failing commands script should return 444"); + } +} diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java index 884da192de..d5e1cfc9ce 100644 --- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java +++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/location/WinRmMachineLocationLiveTest.java @@ -146,7 +146,7 @@ protected void runCopyTo(String contents) throws Exception { String remotePath = "C:\\myfile-"+Identifiers.makeRandomId(4)+".txt"; machine.copyTo(new ByteArrayInputStream(contents.getBytes()), remotePath); - WinRmToolResponse response = machine.executeScript("type "+remotePath); + WinRmToolResponse response = machine.executeCommand("type " + remotePath); String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr(); assertEquals(response.getStatusCode(), 0, msg); assertEquals(response.getStdOut().trim(), contents, msg); @@ -159,7 +159,7 @@ protected void runCopyFileTo(String contents) throws Exception { String remotePath = "C:\\myfile-"+Identifiers.makeRandomId(4)+".txt"; machine.copyTo(localFile, remotePath); - WinRmToolResponse response = machine.executeScript("type "+remotePath); + WinRmToolResponse response = machine.executeCommand("type " + remotePath); String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr(); assertEquals(response.getStatusCode(), 0, msg); assertEquals(response.getStdOut().trim(), contents, msg); @@ -193,7 +193,7 @@ public void testExecMultiLineScript() throws Exception { */ @Test(groups={"Live", "WIP"}) public void testExecMultiPartScript() throws Exception { - assertExecSucceeds(ImmutableList.of("echo first", "echo second"), "first"+"\r\n"+"second", ""); + assertExecSucceeds("echo first"+"\r\n"+"echo second", "first"+"\r\n"+"second", ""); } @Test(groups="Live") @@ -202,13 +202,11 @@ public void testExecFailingScript() throws Exception { // Single commands assertExecFails(INVALID_CMD); - assertExecFails(ImmutableList.of(INVALID_CMD)); } @Test(groups="Live") public void testExecScriptExit0() throws Exception { assertExecSucceeds("exit /B 0", "", ""); - assertExecSucceeds(ImmutableList.of("exit /B 0"), "", ""); } /* @@ -224,7 +222,6 @@ public void testExecScriptExit0() throws Exception { public void testExecScriptExit1() throws Exception { // Single commands assertExecFails("exit /B 1"); - assertExecFails(ImmutableList.of("exit /B 1")); } @Test(groups="Live") @@ -342,7 +339,7 @@ public void testExecPsMultiLineScriptWithoutSlashR() throws Exception { @Test(groups="Live") public void testExecPsMultiPartScript() throws Exception { - assertExecPsSucceeds(ImmutableList.of("Write-Host first", "Write-Host second"), "first"+"\n"+"second", ""); + assertExecPsSucceeds("Write-Host first" + "\r\n" + "Write-Host second", "first"+"\n"+"second", ""); } @Test(groups="Live") @@ -372,7 +369,7 @@ public void testExecPsBatchFileExit3() throws Exception { String scriptPath = "C:\\myscript-"+Identifiers.makeRandomId(4)+".bat"; machine.copyTo(new ByteArrayInputStream(script.getBytes()), scriptPath); - WinRmToolResponse response = machine.executePsScript("& '"+scriptPath+"'"); + WinRmToolResponse response = machine.executePsScript("& '" + scriptPath + "'"); String msg = "statusCode="+response.getStatusCode()+"; out="+response.getStdOut()+"; err="+response.getStdErr(); assertEquals(response.getStatusCode(), 3, msg); } @@ -464,7 +461,7 @@ public void testExecPsFilePsSingleLineWithInvalidCommand() throws Exception { @Test(groups="Live") public void testConfirmUseOfErrorActionPreferenceDoesNotCauseErr() throws Exception { // Confirm that ErrorActionPreference=Stop does not itself cause a failure, and still get output on success. - assertExecPsSucceeds(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, "Write-Host myline"), "myline", ""); + assertExecPsSucceeds(PS_ERR_ACTION_PREF_EQ_STOP + "\r\n" + "Write-Host myline", "myline", ""); } /* @@ -493,11 +490,10 @@ public void testExecPsExit1() throws Exception { public void testExecFailingPsScript() throws Exception { // Single commands assertExecPsFails(INVALID_CMD); - assertExecPsFails(ImmutableList.of(INVALID_CMD)); - + // Multi-part commands - assertExecPsFails(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, "Write-Host myline", INVALID_CMD)); - assertExecPsFails(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, INVALID_CMD, "Write-Host myline")); + assertExecPsFails(Joiner.on("\r\n").join(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, "Write-Host myline", INVALID_CMD))); + assertExecPsFails(Joiner.on("\r\n").join(ImmutableList.of(PS_ERR_ACTION_PREF_EQ_STOP, INVALID_CMD, "Write-Host myline"))); } @Test(groups={"Live", "Acceptance"}) @@ -554,12 +550,12 @@ public Void call() throws Exception { private void assertExecFails(String cmd) { Stopwatch stopwatch = Stopwatch.createStarted(); - assertFailed(cmd, machine.executeScript(cmd), stopwatch); + assertFailed(cmd, machine.executeCommand(cmd), stopwatch); } private void assertExecFails(List cmds) { Stopwatch stopwatch = Stopwatch.createStarted(); - assertFailed(cmds, machine.executeScript(cmds), stopwatch); + assertFailed(cmds, machine.executeCommand(cmds), stopwatch); } private void assertExecPsFails(String cmd) { @@ -574,12 +570,12 @@ private void assertExecPsFails(List cmds) { private void assertExecSucceeds(String cmd, String stdout, String stderr) { Stopwatch stopwatch = Stopwatch.createStarted(); - assertSucceeded(cmd, machine.executeScript(cmd), stdout, stderr, stopwatch); + assertSucceeded(cmd, machine.executeCommand(cmd), stdout, stderr, stopwatch); } private void assertExecSucceeds(List cmds, String stdout, String stderr) { Stopwatch stopwatch = Stopwatch.createStarted(); - assertSucceeded(cmds, machine.executeScript(cmds), stdout, stderr, stopwatch); + assertSucceeded(cmds, machine.executeCommand(cmds), stdout, stderr, stopwatch); } private void assertExecPsSucceeds(String cmd, String stdout, String stderr) { diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/NativeWindowsScriptRunner.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/NaiveWindowsScriptRunner.java similarity index 72% rename from brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/NativeWindowsScriptRunner.java rename to brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/NaiveWindowsScriptRunner.java index 26fde11d0c..faef2847f7 100644 --- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/lifecycle/NativeWindowsScriptRunner.java +++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/NaiveWindowsScriptRunner.java @@ -16,14 +16,20 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.brooklyn.entity.software.base.lifecycle; +package org.apache.brooklyn.location.winrm; +import java.io.InputStream; import java.util.Map; -public interface NativeWindowsScriptRunner { +/** Marks something which can run scripts. Called "Naive" because it hides too much of the complexity, + * about script execution and other command-related tasks (put, etc). + */ +public interface NaiveWindowsScriptRunner { /** Runs a command and returns the result code */ int executeNativeCommand(Map flags, String windowsCommand, String summaryForLogging); int executePsCommand(Map flags, String powerShellCommand, String summaryForLogging); - Integer executeNativeOrPsCommand(Map flags, String regularCommand, String powershellCommand, String phase, Boolean allowNoOp); + Integer executeNativeOrPsCommand(Map flags, String regularCommand, String powershellCommand, String summaryForLogging, Boolean allowNoOp); + + int copyTo(InputStream source, String destination); } diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java index 8a2a7865ef..478ec122af 100644 --- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java +++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/location/winrm/WinRmMachineLocation.java @@ -18,9 +18,11 @@ */ package org.apache.brooklyn.location.winrm; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.util.List; @@ -29,6 +31,7 @@ import javax.annotation.Nullable; +import com.google.common.base.Function; import org.apache.brooklyn.api.location.MachineDetails; import org.apache.brooklyn.api.location.MachineLocation; import org.apache.brooklyn.api.location.OsDetails; @@ -44,16 +47,15 @@ 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.text.Strings; 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; @@ -63,6 +65,8 @@ import com.google.common.net.HostAndPort; import com.google.common.reflect.TypeToken; +import static com.google.common.base.Preconditions.checkNotNull; + public class WinRmMachineLocation extends AbstractLocation implements MachineLocation { private static final Logger LOG = LoggerFactory.getLogger(WinRmMachineLocation.class); @@ -167,7 +171,7 @@ public void init() { } } } - + public String getUser() { return config().get(USER); } @@ -212,17 +216,50 @@ public Set getPrivateAddresses() { return (result == null) ? ImmutableSet.of() : ImmutableSet.copyOf(result); } + /** + * @deprecated since 0.9.0; use {@link #executeCommand(String)} + */ + @Deprecated public WinRmToolResponse executeScript(String script) { - return executeScript(ImmutableList.of(script)); + return executeCommand(script); } + /** + * @deprecated since 0.9.0; use {@link #executeCommand(List)} + */ + @Deprecated public WinRmToolResponse executeScript(List script) { - return executeScript(ImmutableMap.of(), script); + return executeCommand(script); } - + + /** + * @deprecated since 0.9.0; use {@link #executeCommand(Map, List)} + */ + @Deprecated public WinRmToolResponse executeScript(Map props, List script) { + return executeCommand(props, script); + } + + /** + * @since 0.9.0 (previously was {@code executeScript(String)} + */ + public WinRmToolResponse executeCommand(String script) { + return executeCommand(ImmutableList.of(script)); + } + + /** + * @since 0.9.0 (previously was {@code executeScript(List)} + */ + public WinRmToolResponse executeCommand(List script) { + return executeCommand(ImmutableMap.of(), script); + } + + /** + * @since 0.9.0 (previously was {@code executeScript(Map, List)} + */ + public WinRmToolResponse executeCommand(Map props, List script) { WinRmTool tool = newWinRmTool(props); - return tool.executeScript(script); + return tool.executeCommand(script); } public WinRmToolResponse executePsScript(String psScript) { @@ -232,7 +269,7 @@ public WinRmToolResponse executePsScript(String psScript) { public WinRmToolResponse executePsScript(List psScript) { return executePsScript(ImmutableMap.of(), psScript); } - + public WinRmToolResponse executePsScript(Map props, List psScript) { WinRmTool tool = newWinRmTool(props); return tool.executePs(psScript); @@ -244,22 +281,22 @@ protected WinRmTool newWinRmTool(Map props) { ConfigBag args = new ConfigBag(); for (Map.Entry entry: config().getBag().getAllConfig().entrySet()) { - + boolean include = false; String key = entry.getKey(); if (key.startsWith(WinRmTool.BROOKLYN_CONFIG_KEY_PREFIX)) { key = Strings.removeFromStart(key, WinRmTool.BROOKLYN_CONFIG_KEY_PREFIX); include = true; } - + if (key.startsWith(WINRM_TOOL_CLASS_PROPERTIES_PREFIX)) { key = Strings.removeFromStart(key, WINRM_TOOL_CLASS_PROPERTIES_PREFIX); include = true; } - + if (ALL_WINRM_CONFIG_KEY_NAMES.contains(entry.getKey())) { // key should be included, and does not need to be changed - + // TODO make this config-setting mechanism more universal // currently e.g. it will not admit a tool-specific property. // thinking either we know about the tool here, @@ -267,13 +304,13 @@ protected WinRmTool newWinRmTool(Map props) { // (require use of BROOKLYN_CONFIG_KEY_PREFIX) include = true; } - - + + if (include) { args.putStringKey(key, entry.getValue()); } } - + args.putAll(props); args.configure(SshTool.PROP_HOST, getAddress().getHostAddress()); @@ -292,6 +329,14 @@ protected WinRmTool newWinRmTool(Map props) { } } + private void writeToStream(ByteArrayOutputStream stream, String string) { + try { + stream.write(string.getBytes()); + } catch (IOException e) { + LOG.warn("Problem populating one of the std streams for task of entity ", e); + } + } + public int copyTo(File source, String destination) { FileInputStream sourceStream = null; try { @@ -309,13 +354,17 @@ public int copyTo(File source, String destination) { public int copyTo(InputStream source, String destination) { return copyTo(ImmutableMap.of(), source, destination); } - + public int copyTo(Map props, InputStream source, String destination) { WinRmTool tool = newWinRmTool(props); WinRmToolResponse response = tool.copyToServer(source, destination); return response.getStatusCode(); } + private T getRequiredConfig(ConfigKey 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( diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmScriptTool.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmScriptTool.java new file mode 100644 index 0000000000..316c3c411f --- /dev/null +++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmScriptTool.java @@ -0,0 +1,115 @@ +/* + * 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.base.Joiner; +import com.google.common.collect.ImmutableMap; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.location.winrm.NaiveWindowsScriptRunner; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.internal.ssh.ShellTool; +import org.apache.brooklyn.util.os.Os; +import org.apache.brooklyn.util.text.Identifiers; +import org.apache.brooklyn.util.text.Strings; +import org.apache.brooklyn.util.time.Time; + +import java.io.ByteArrayInputStream; +import java.util.List; +import java.util.Map; + +import static org.apache.brooklyn.core.config.ConfigKeys.newConfigKeyWithDefault; +import static org.apache.brooklyn.util.core.internal.ssh.ShellAbstractTool.getOptionalVal; + +/** + * Implemented like SshCliTool + */ +// TODO Use a general interface for SshTool and WinRmScriptTool with +public class WinRmScriptTool { + public static final ConfigKey PROP_SCRIPT_DIR = newConfigKeyWithDefault(ShellTool.PROP_SCRIPT_DIR, "The directory where the script should be uploaded. It is an env variable.", "temp"); + public static final ConfigKey PROP_SUMMARY = ShellTool.PROP_SUMMARY; + + private static final String SCRIPT_TEMP_DIR_VARIABLE = "BROOKLYN_TEMP_SCRIPT_DIR"; + + private String scriptNameWithoutExtension; + private String scriptDir; + private String summary; + private NaiveWindowsScriptRunner scriptRunner; + + public WinRmScriptTool(Map props, NaiveWindowsScriptRunner scriptRunner) { + this.scriptDir = getOptionalVal(props, PROP_SCRIPT_DIR); + + String summary = getOptionalVal(props, PROP_SUMMARY); + if (summary != null) { + summary = Strings.makeValidFilename(summary); + if (summary.length() > 30) + summary = summary.substring(0, 30); + } + + this.summary = summary; + this.scriptNameWithoutExtension = "brooklyn-" + + Time.makeDateStampString() + "-" + Identifiers.makeRandomId(4) + + (Strings.isBlank(summary) ? "" : "-" + summary); + + this.scriptRunner = scriptRunner; + } + + public static String psStringExpressionToBatchVariable(String psString, String batchVariable) { + return "for /f \"delims=\" %%i in ('powershell -noprofile \\'Write-Host \""+ psString +"\"\\') do @set "+batchVariable+ "=%i"; + } + + private String psExtension(String filename) { + return filename + ".ps1"; + } + + private String batchExtension(String filename) { + return filename + ".bat"; + } + + public int execPsScript(List psCommands) { + return execPsScript(ImmutableMap.of(), psCommands); + } + + public int execPsScript(Map flags, List commands) { + byte[] scriptBytes = toScript(commands).getBytes(); + String scriptFilename = psExtension(scriptNameWithoutExtension); + if (flags.containsKey("scriptFilename")) + flags.put("scriptFilename", scriptFilename); + String scriptPath = Os.mergePathsWin("$env:"+scriptDir, scriptFilename); + scriptRunner.copyTo(new ByteArrayInputStream(scriptBytes), scriptPath); + return scriptRunner.executeNativeOrPsCommand(MutableMap.of(), null, "& " + scriptPath, summary, false); + } + + public int execNativeScript(List commands) { + return execNativeScript(ImmutableMap.of(), commands); + } + + public int execNativeScript(Map flags, List commands) { + byte[] scriptBytes = toScript(commands).getBytes(); + String scriptFilename = batchExtension(scriptNameWithoutExtension); + if (flags.containsKey("scriptFilename")) + flags.put("scriptFilename", scriptFilename); + scriptRunner.copyTo(new ByteArrayInputStream(scriptBytes), Os.mergePathsWin("$env:"+scriptDir, scriptFilename)); + String scriptPath = Os.mergePathsWin("%"+scriptDir+"%", scriptFilename); + return scriptRunner.executeNativeOrPsCommand(MutableMap.of(), scriptPath, null, summary, false); + } + + private String toScript(List commands) { + return Joiner.on("\r\n").join(commands); + } +} diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java index 6d2fb06de4..6f10733948 100644 --- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java +++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/WinRmTool.java @@ -66,8 +66,43 @@ public interface WinRmTool { "Size of file chunks (in bytes) to be used when copying a file to the remote server", 1024); + /** + * Executes a Native Windows command. + * It is creating a new Shell on the destination host each time it is being called. + * @param command + * @since 0.2 + */ + WinRmToolResponse executeCommand(String command); + + /** + * Executes a Power Shell command. + * It is creating a new Shell on the destination host each time it is being called. + * @param psCommand + * @since 0.2 + */ + WinRmToolResponse executePsCommand(String psCommand); + + /** + * Execute a list of Windows Native commands as one command. + * The method translates the list of commands to a single String command with a "\r\n" delimiter and a terminating one. + * @param commands + * @deprecated since 0.2; Use the {@link #executeCommand(String)} instead and transform your commands list explicitly + */ + @Deprecated WinRmToolResponse executeScript(List commands); + /** + * @since 0.9.0 + */ + WinRmToolResponse executeCommand(List commands); + + /** + * Execute a list of Power Shell commands as one command. + * The method translates the list of commands to a single String command with a "\r\n" delimiter and a terminating one. + * @param commands + * @deprecated since 0.2; Use the {@link #executePsCommand(String)} instead and transform your commands list explicitly + */ + @Deprecated WinRmToolResponse executePs(List commands); WinRmToolResponse copyToServer(InputStream source, String destination); diff --git a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java index 0bdfa14a56..55fb7ae60b 100644 --- a/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java +++ b/brooklyn-server/software/winrm/src/main/java/org/apache/brooklyn/util/core/internal/winrm/pywinrm/Winrm4jTool.java @@ -82,12 +82,30 @@ public Winrm4jTool(ConfigBag config) { execRetryDelay = getRequiredConfig(config, PROP_EXEC_RETRY_DELAY); logCredentials = getRequiredConfig(config, LOG_CREDENTIALS); } - + @Override - public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeScript(final List commands) { + public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeCommand(final String command) { return exec(new Function() { @Override public WinRmToolResponse apply(io.cloudsoft.winrm4j.winrm.WinRmTool tool) { - return tool.executeScript(commands); + return tool.executeCommand(command); + } + }); + } + + @Override + public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executePsCommand(final String psCommand) { + return exec(new Function() { + @Override public WinRmToolResponse apply(io.cloudsoft.winrm4j.winrm.WinRmTool tool) { + return tool.executePs(psCommand); + } + }); + } + + @Override + public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeCommand(final List commands) { + return exec(new Function() { + @Override public WinRmToolResponse apply(io.cloudsoft.winrm4j.winrm.WinRmTool tool) { + return tool.executeCommand(commands); } }); } @@ -101,6 +119,12 @@ public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executePs( }); } + @Override + @Deprecated + public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse executeScript(final List commands) { + return executeCommand(commands); + } + @Override public org.apache.brooklyn.util.core.internal.winrm.WinRmToolResponse copyToServer(InputStream source, String destination) { executePs(ImmutableList.of("rm -ErrorAction SilentlyContinue " + destination)); diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/os/Os.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/os/Os.java index 2202c7b868..150a0bdc37 100644 --- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/os/Os.java +++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/os/Os.java @@ -204,6 +204,17 @@ public static String mergePathsUnix(String ...items) { return Urls.mergePaths(items); } + public static String mergePathsWin(String... items) { + char separatorChar = '\\'; + StringBuilder result = new StringBuilder(); + for (String item: items) { + if (Strings.isEmpty(item)) continue; + if (result.length() > 0 && !isSeparator(result.codePointAt(result.length()-1))) result.append(separatorChar); + result.append(item); + } + return result.toString(); + } + /** merges paths using forward slash as the "local OS file separator", because it is recognised on windows, * making paths more consistent and avoiding problems with backslashes being escaped. * empty segments are omitted. */