Windows support fixes (for vCloud Director) - WinRmMachineLocation respects host:port - Adds JcloudsLocation config option osFamilyOverride. If set, then assumes it is a Windows VM even if the metadata from the cloud says âunrecognisedâ. - Fixes WinRM login over NAT
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/22638627 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/22638627 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/22638627 Branch: refs/heads/master Commit: 22638627997fc410409872ab9dee39a581d86258 Parents: 51e7e0b Author: Aled Sage <[email protected]> Authored: Fri Jun 12 16:45:50 2015 +0100 Committer: Aled Sage <[email protected]> Committed: Fri Jun 12 16:45:50 2015 +0100 ---------------------------------------------------------------------- .../location/basic/WinRmMachineLocation.java | 15 +-- .../location/jclouds/JcloudsLocation.java | 108 +++++++++++++------ .../location/jclouds/JcloudsLocationConfig.java | 3 + .../location/jclouds/JcloudsLocationTest.java | 2 +- 4 files changed, 86 insertions(+), 42 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/22638627/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java b/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java index cb59102..36c0360 100644 --- a/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java +++ b/core/src/main/java/brooklyn/location/basic/WinRmMachineLocation.java @@ -53,6 +53,7 @@ import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +import com.google.common.net.HostAndPort; public class WinRmMachineLocation extends AbstractLocation implements MachineLocation { @@ -113,6 +114,12 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc return (address != null) ? address.getHostAddress() : null; } + @Nullable + protected String getHostAndPort() { + String host = getHostname(); + return (host == null) ? null : host + ":" + config().get(WINRM_PORT); + } + @Override public Set<String> getPublicAddresses() { InetAddress address = getAddress(); @@ -150,7 +157,7 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc } protected WinRmToolResponse executeScriptNoRetry(List<String> script) { - WinRmTool winRmTool = WinRmTool.connect(getHostname(), getUser(), getPassword()); + WinRmTool winRmTool = WinRmTool.connect(getHostAndPort(), getUser(), getPassword()); WinRmToolResponse response = winRmTool.executeScript(script); return response; } @@ -183,7 +190,7 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc } public WinRmToolResponse executePsScriptNoRetry(List<String> psScript) { - WinRmTool winRmTool = WinRmTool.connect(getHostname(), getUser(), getPassword()); + WinRmTool winRmTool = WinRmTool.connect(getHostAndPort(), getUser(), getPassword()); WinRmToolResponse response = winRmTool.executePs(psScript); return response; } @@ -231,10 +238,6 @@ public class WinRmMachineLocation extends AbstractLocation implements MachineLoc @Override public void init() { super.init(); - - getRequiredConfig(ADDRESS); - getRequiredConfig(USER); - getRequiredConfig(PASSWORD); } public String getUser() { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/22638627/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java index 7d3d50e..15ea553 100644 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java +++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java @@ -328,17 +328,25 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im /** * Whether VMs provisioned from this image will be Windows. Assume windows if the image - * explicitly says so, or if the config explicitly says {@link JcloudsLocationConfig#OS_FAMILY} - * is Windows. + * explicitly says so, or if image does not tell us then fall back to whether the config + * explicitly says windows in {@link JcloudsLocationConfig#OS_FAMILY}. + * + * Will first look at {@link JcloudsLocationConfig#OS_FAMILY_OVERRIDE}, to check if that + * is set. If so, no further checks are done: the value is compared against {@link OsFamily#WINDOWS}. * * We believe the config (e.g. from brooklyn.properties) because for some clouds there is * insufficient meta-data so the Image might not tell us. Thus a user can work around it * by explicitly supplying configuration. */ public boolean isWindows(Image image, ConfigBag config) { + OsFamily override = config.get(OS_FAMILY_OVERRIDE); + if (override != null) return override == OsFamily.WINDOWS; + OsFamily confFamily = config.get(OS_FAMILY); OperatingSystem os = (image != null) ? image.getOperatingSystem() : null; - return (os != null) ? (OsFamily.WINDOWS == os.getFamily()) : (OsFamily.WINDOWS == confFamily); + return (os != null && os.getFamily() != OsFamily.UNRECOGNIZED) + ? (OsFamily.WINDOWS == os.getFamily()) + : (OsFamily.WINDOWS == confFamily); } /** @@ -347,9 +355,14 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im * @see {@link #isWindows(Image, ConfigBag)} */ public boolean isWindows(NodeMetadata node, ConfigBag config) { + OsFamily override = config.get(OS_FAMILY_OVERRIDE); + if (override != null) return override == OsFamily.WINDOWS; + OsFamily confFamily = config.get(OS_FAMILY); OperatingSystem os = (node != null) ? node.getOperatingSystem() : null; - return (os != null) ? (OsFamily.WINDOWS == os.getFamily()) : (OsFamily.WINDOWS == confFamily); + return (os != null && os.getFamily() != OsFamily.UNRECOGNIZED) + ? (OsFamily.WINDOWS == os.getFamily()) + : (OsFamily.WINDOWS == confFamily); } protected Semaphore getMachineCreationSemaphore() { @@ -682,14 +695,24 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im throw new IllegalStateException("No nodes returned by jclouds create-nodes in " + setup.getDescription()); boolean windows = isWindows(node, setup); - + if (windows) { + int newLoginPort = node.getLoginPort() == 22 ? 5985 : node.getLoginPort(); + String newLoginUser = "root".equals(node.getCredentials().getUser()) ? "Administrator" : node.getCredentials().getUser(); + LOG.debug("jclouds created Windows VM {}; transforming connection details: loginPort from {} to {}; loginUser from {} to {}", + new Object[] {node, node.getLoginPort(), newLoginPort, node.getCredentials().getUser(), newLoginUser}); + + node = NodeMetadataBuilder.fromNodeMetadata(node) + .loginPort(newLoginPort) + .credentials(LoginCredentials.builder(node.getCredentials()).user(newLoginUser).build()) + .build(); + } // FIXME How do we influence the node.getLoginPort, so it is set correctly for Windows? // Setup port-forwarding, if required Optional<HostAndPort> sshHostAndPortOverride; if (usePortForwarding) { sshHostAndPortOverride = Optional.of(portForwarder.openPortForwarding( node, - (windows ? 5985 : node.getLoginPort()), + node.getLoginPort(), Optional.<Integer>absent(), Protocol.TCP, Cidr.UNIVERSAL)); @@ -745,7 +768,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im // WinRmMachineLocation winRmMachineLocation = null; // Create a JcloudsSshMachineLocation, and register it if (windows) { - machineLocation = registerWinRmMachineLocation(computeService, node, setup); + machineLocation = registerWinRmMachineLocation(computeService, node, userCredentials, sshHostAndPortOverride, setup); } else { machineLocation = registerJcloudsSshMachineLocation(computeService, node, userCredentials, sshHostAndPortOverride, setup); if (template!=null && machineLocation.getTemplate()==null) { @@ -1075,11 +1098,13 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im } if (userDataMethod != null) { try { - userDataMethod.invoke(Strings.toString(v)); + userDataMethod.invoke(t, Strings.toString(v)); } catch (InvocationTargetException e) { - LOG.info("Problem invoking "+userDataMethod.getName()+" of "+t.getClass()+", for setting userData", e); + LOG.info("Problem invoking "+userDataMethod.getName()+" of "+t.getClass()+", for setting userData (rethrowing)", e); + throw Exceptions.propagate(e); } catch (IllegalAccessException e) { - LOG.info("Unable to reflectively invoke "+userDataMethod.getName()+" of "+t.getClass()+", for setting userData", e); + LOG.debug("Unable to reflectively invoke "+userDataMethod.getName()+" of "+t.getClass()+", for setting userData (rethrowing)", e); + throw Exceptions.propagate(e); } } else { LOG.info("ignoring userDataString({}) in VM creation because not supported for cloud/type ({})", v, t.getClass()); @@ -1446,12 +1471,10 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im if (!userCreation.statements.isEmpty()) { // If unsure of OS family, default to unix for rendering statements. org.jclouds.scriptbuilder.domain.OsFamily scriptOsFamily; - if (node.getOperatingSystem() == null) { - scriptOsFamily = org.jclouds.scriptbuilder.domain.OsFamily.UNIX; + if (isWindows(node, config)) { + scriptOsFamily = org.jclouds.scriptbuilder.domain.OsFamily.WINDOWS; } else { - scriptOsFamily = (node.getOperatingSystem().getFamily() == org.jclouds.compute.domain.OsFamily.WINDOWS) - ? org.jclouds.scriptbuilder.domain.OsFamily.WINDOWS - : org.jclouds.scriptbuilder.domain.OsFamily.UNIX; + scriptOsFamily = org.jclouds.scriptbuilder.domain.OsFamily.UNIX; } boolean windows = isWindows(node, config); @@ -1903,16 +1926,16 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im String vmHostname = getPublicHostname(node, sshHostAndPort, setup); JcloudsSshMachineLocation machine = createJcloudsSshMachineLocation(computeService, node, vmHostname, sshHostAndPort, setup); - registerJcloudsSshMachineLocation(node.getId(), machine); + registerJcloudsMachineLocation(node.getId(), machine); return machine; } @VisibleForTesting - protected void registerJcloudsSshMachineLocation(String nodeId, JcloudsSshMachineLocation machine) { + protected void registerJcloudsMachineLocation(String nodeId, JcloudsMachineLocation machine) { machine.setParent(this); vmInstanceIds.put(machine, nodeId); } - + /** @deprecated since 0.7.0 use variant which takes compute service; no longer called internally, * so marked final to force any overrides to switch to new syntax */ @Deprecated @@ -1997,17 +2020,18 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im } } - protected JcloudsWinRmMachineLocation registerWinRmMachineLocation(ComputeService computeService, NodeMetadata node, ConfigBag setup) { - // FIMXE: Need to write WinRM equivalent of getPublicHostname - String vmHostname = node.getPublicAddresses().iterator().next(); - - JcloudsWinRmMachineLocation winRmMachineLocation = createWinRmMachineLocation(computeService, node, vmHostname, setup); - winRmMachineLocation.setParent(this); - vmInstanceIds.put(winRmMachineLocation, node.getId()); - return winRmMachineLocation; + protected JcloudsWinRmMachineLocation registerWinRmMachineLocation(ComputeService computeService, NodeMetadata node, LoginCredentials initialCredentials, Optional<HostAndPort> sshHostAndPort, ConfigBag setup) { + if (initialCredentials==null) + initialCredentials = node.getCredentials(); + + String vmHostname = getPublicHostname(node, sshHostAndPort, setup); + + JcloudsWinRmMachineLocation machine = createWinRmMachineLocation(computeService, node, vmHostname, sshHostAndPort, setup); + registerJcloudsMachineLocation(node.getId(), machine); + return machine; } - protected JcloudsWinRmMachineLocation createWinRmMachineLocation(ComputeService computeService, NodeMetadata node, String vmHostname, ConfigBag setup) { + protected JcloudsWinRmMachineLocation createWinRmMachineLocation(ComputeService computeService, NodeMetadata node, String vmHostname, Optional<HostAndPort> sshHostAndPort, ConfigBag setup) { String nodeAvailabilityZone = extractAvailabilityZone(setup, node); String nodeRegion = extractRegion(setup, node); if (nodeRegion == null) { @@ -2015,11 +2039,14 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im nodeRegion = extractProvider(setup, node); } + String address = sshHostAndPort.isPresent() ? sshHostAndPort.get().getHostText() : vmHostname; + if (isManaged()) { return getManagementContext().getLocationManager().createLocation(LocationSpec.create(JcloudsWinRmMachineLocation.class) .configure("jcloudsParent", this) .configure("displayName", vmHostname) - .configure("address", vmHostname) + .configure("address", address) + .configure(WinRmMachineLocation.WINRM_PORT, sshHostAndPort.isPresent() ? sshHostAndPort.get().getPort() : node.getLoginPort()) .configure("user", getUser(setup)) .configure(WinRmMachineLocation.USER, setup.get(USER)) .configure(WinRmMachineLocation.PASSWORD, setup.get(PASSWORD)) @@ -2463,13 +2490,24 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im } } if (sshHostAndPort.isPresent() || inferredHostAndPort != null) { - HostAndPort hostAndPortToUse = sshHostAndPort.isPresent() ? sshHostAndPort.get() : inferredHostAndPort; - try { - return getPublicHostnameAws(hostAndPortToUse, setup); - } catch (Exception e) { - LOG.warn("Error querying aws-ec2 instance instance "+node.getId()+"@"+node.getLocation()+" over ssh for its hostname; falling back to first reachable IP", e); - // We've already found a reachable address so settle for that, rather than doing it again - if (inferredHostAndPort != null) return inferredHostAndPort.getHostText(); + if (isWindows(node, setup)) { + if (inferredHostAndPort != null) { + LOG.warn("Cannot querying aws-ec2 Windows instance "+node.getId()+"@"+node.getLocation()+" over ssh for its hostname; falling back to first reachable IP"); + return inferredHostAndPort.getHostText(); + } + } else { + HostAndPort hostAndPortToUse = sshHostAndPort.isPresent() ? sshHostAndPort.get() : inferredHostAndPort; + try { + return getPublicHostnameAws(hostAndPortToUse, setup); + } catch (Exception e) { + if (inferredHostAndPort != null) { + LOG.warn("Error querying aws-ec2 instance "+node.getId()+"@"+node.getLocation()+" over ssh for its hostname; falling back to first reachable IP", e); + // We've already found a reachable address so settle for that, rather than doing it again + return inferredHostAndPort.getHostText(); + } else { + LOG.warn("Error querying aws-ec2 instance "+node.getId()+"@"+node.getLocation()+" over ssh for its hostname; falling back to jclouds metadata for address", e); + } + } } } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/22638627/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java index 5e8780e..ab0007a 100644 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java +++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java @@ -251,6 +251,9 @@ public interface JcloudsLocationConfig extends CloudLocationConfig { public static final ConfigKey<String> OS_VERSION_REGEX = ConfigKeys.newStringConfigKey("osVersionRegex", "Regular expression for the OS version to load"); + public static final ConfigKey<OsFamily> OS_FAMILY_OVERRIDE = ConfigKeys.newConfigKey(OsFamily.class, "osFamilyOverride", + "OS family of VMs (ignores VM metadata from jclouds, and assumes this value)"); + public static final ConfigKey<ComputeServiceRegistry> COMPUTE_SERVICE_REGISTRY = ConfigKeys.newConfigKey( ComputeServiceRegistry.class, "jclouds.computeServiceRegistry", http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/22638627/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java index 62817c2..3411da3 100644 --- a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java +++ b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTest.java @@ -520,7 +520,7 @@ public class JcloudsLocationTest implements JcloudsLocationConfig { .configure("port", 22) .configure("user", "bob") .configure("jcloudsParent", this)); - registerJcloudsSshMachineLocation("bogus", result); + registerJcloudsMachineLocation("bogus", result); // explicitly invoke this customizer, to comply with tests for (JcloudsLocationCustomizer customizer : getCustomizers(config().getBag())) {
