From 00f5457d6f1a3adb7febd03cc57cc62f0082cc59 Mon Sep 17 00:00:00 2001 From: Valentin Aitken Date: Wed, 28 Sep 2016 00:05:52 +0300 Subject: [PATCH 1/3] Reduce decisions for HostAndPort which will be used in JcloudsLocation Use managementHostAndPort for all customizations made in - obtainOnce(ConfigBag) - registerMachineLocation(ConfigBag, NodeMetadata) Cleanup transformations --- .../location/jclouds/JcloudsLocation.java | 221 +++++++----------- .../location/jclouds/JcloudsUtil.java | 2 +- 2 files changed, 90 insertions(+), 133 deletions(-) diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java index 2fd12a8ec6..f521d8130c 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java @@ -133,6 +133,7 @@ import org.apache.brooklyn.util.time.Duration; import org.apache.brooklyn.util.time.Time; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.tuple.Pair; import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; import org.jclouds.compute.ComputeService; @@ -771,17 +772,19 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable sshHostAndPortOverride = Optional.absent(); } + String vmHostname = getPublicHostname(node, sshHostAndPortOverride, userCredentials, setup); LoginCredentials initialCredentials = node.getCredentials(); + final HostAndPort managementHostAndPort = resolveManagementHostAndPort(computeService, node, vmHostname, sshHostAndPortOverride, setup); if (skipJcloudsSshing) { boolean waitForConnectable = (windows) ? waitForWinRmable : waitForSshable; if (waitForConnectable) { if (windows) { // TODO Does jclouds support any windows user setup? - initialCredentials = waitForWinRmAvailable(computeService, node, sshHostAndPortOverride, setup); + initialCredentials = waitForWinRmAvailable(initialCredentials, managementHostAndPort, setup); } else { - initialCredentials = waitForSshable(computeService, node, sshHostAndPortOverride, setup); + initialCredentials = waitForSshable(computeService, node, managementHostAndPort, setup); } - userCredentials = createUser(computeService, node, sshHostAndPortOverride, initialCredentials, setup); + userCredentials = createUser(computeService, node, managementHostAndPort, initialCredentials, setup); } } @@ -816,7 +819,7 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable // Wait for the VM to be reachable over SSH if (waitForSshable && !windows) { - waitForSshable(computeService, node, sshHostAndPortOverride, ImmutableList.of(userCredentials), setup); + waitForSshable(computeService, node, managementHostAndPort, ImmutableList.of(userCredentials), setup); } else { LOG.debug("Skipping ssh check for {} ({}) due to config waitForSshable=false", node, setup.getDescription()); } @@ -829,9 +832,9 @@ protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailable // Create a JcloudsSshMachineLocation, and register it if (windows) { - machineLocation = registerWinRmMachineLocation(computeService, node, userCredentials, sshHostAndPortOverride, setup); + machineLocation = registerWinRmMachineLocation(computeService, node, managementHostAndPort, sshHostAndPortOverride, setup); } else { - machineLocation = registerJcloudsSshMachineLocation(computeService, node, Optional.fromNullable(template), userCredentials, sshHostAndPortOverride, setup); + machineLocation = registerJcloudsSshMachineLocation(computeService, node, Optional.fromNullable(template), userCredentials, managementHostAndPort, sshHostAndPortOverride, vmHostname, setup); } PortForwardManager portForwardManager = setup.get(PORT_FORWARDING_MANAGER); @@ -1893,7 +1896,7 @@ protected WinRmMachineLocation createTemporaryWinRmMachineLocation(HostAndPort h /** * Create the user immediately - executing ssh commands as required. */ - protected LoginCredentials createUser(ComputeService computeService, NodeMetadata node, Optional hostAndPortOverride, LoginCredentials initialCredentials, ConfigBag config) { + protected LoginCredentials createUser(ComputeService computeService, NodeMetadata node, HostAndPort managementHostAndPort, LoginCredentials initialCredentials, ConfigBag config) { Image image = (node.getImageId() != null) ? computeService.getImage(node.getImageId()) : null; UserCreation userCreation = createUserStatements(image, config); @@ -1910,7 +1913,6 @@ protected LoginCredentials createUser(ComputeService computeService, NodeMetadat if (windows) { LOG.warn("Unable to execute statements on WinRM in JcloudsLocation; skipping for "+node+": "+userCreation.statements); - } else { List commands = Lists.newArrayList(); for (Statement statement : userCreation.statements) { @@ -1920,9 +1922,7 @@ protected LoginCredentials createUser(ComputeService computeService, NodeMetadat } String initialUser = initialCredentials.getUser(); - String address = hostAndPortOverride.isPresent() ? hostAndPortOverride.get().getHostText() : getFirstReachableAddress(node, config); - int port = hostAndPortOverride.isPresent() ? hostAndPortOverride.get().getPort() : node.getLoginPort(); - + // TODO Retrying lots of times as workaround for vcloud-director. There the guest customizations // can cause the VM to reboot shortly after it was ssh'able. Map execProps = Maps.newLinkedHashMap(); @@ -1931,16 +1931,15 @@ protected LoginCredentials createUser(ComputeService computeService, NodeMetadat execProps.put(SshTool.PROP_SSH_TRIES_TIMEOUT.getName(), 10*60*1000); if (LOG.isDebugEnabled()) { - LOG.debug("VM {}: executing user creation/setup via {}@{}:{}; commands: {}", new Object[] { - config.getDescription(), initialUser, address, port, commands}); + LOG.debug("VM {}: executing user creation/setup via {}@{}; commands: {}", new Object[] { + config.getDescription(), initialUser, managementHostAndPort, commands}); } - HostAndPort hostAndPort = hostAndPortOverride.isPresent() ? hostAndPortOverride.get() : HostAndPort.fromParts(address, port); - SshMachineLocation sshLoc = createTemporarySshMachineLocation(hostAndPort, initialCredentials, config); + SshMachineLocation sshLoc = createTemporarySshMachineLocation(managementHostAndPort, initialCredentials, config); try { // BROOKLYN-188: for SUSE, need to specify the path (for groupadd, useradd, etc) Map env = ImmutableMap.of("PATH", sbinPath()); - + int exitcode = sshLoc.execScript(execProps, "create-user", commands, env); if (exitcode != 0) { @@ -1956,6 +1955,33 @@ protected LoginCredentials createUser(ComputeService computeService, NodeMetadat return userCreation.createdUserCredentials; } + protected HostAndPort resolveManagementHostAndPort(ComputeService computeService, NodeMetadata node, String vmHostname, Optional hostAndPortOverride, ConfigBag config) { + return resolveManagementHostAndPort(computeService, node, vmHostname, hostAndPortOverride, config, false); + } + + protected HostAndPort resolveManagementHostAndPort(ComputeService computeService, NodeMetadata node, String vmHostname, Optional hostAndPortOverride, ConfigBag config, boolean useOnlyVmHostNameAndHostAndPortOverride) { + String pollForFirstReachable = config.get(POLL_FOR_FIRST_REACHABLE_ADDRESS); + boolean pollForFirstReachableEnabled = !useOnlyVmHostNameAndHostAndPortOverride && !"false".equalsIgnoreCase(pollForFirstReachable); + + String managementAddress = hostAndPortOverride.isPresent() ? hostAndPortOverride.get().getHostText() : + (pollForFirstReachableEnabled ? getFirstReachableAddress(node, config) : vmHostname); + int managementPort = hostAndPortOverride.isPresent() ? hostAndPortOverride.get().getPort() : node.getLoginPort(); + try { + Networking.getInetAddressWithFixedName(managementAddress); + // fine, it resolves + } catch (Exception e) { + // occurs if an unresolvable hostname is given as vmHostname, and the machine only has private IP addresses but they are reachable + // TODO cleanup use of getPublicHostname so its semantics are clearer, returning reachable hostname or ip, and + // do this check/fix there instead of here! + Exceptions.propagateIfFatal(e); + LOG.debug("Could not resolve reported address '"+managementAddress+"' for "+vmHostname+" ("+config.getDescription()+"/"+node+"), requesting reachable address"); + if (computeService==null) throw Exceptions.propagate(e); + // this has sometimes already been done in waitForReachable (unless skipped) but easy enough to do again + managementAddress = getFirstReachableAddress(node, config); + } + return hostAndPortOverride.isPresent() ? hostAndPortOverride.get() : HostAndPort.fromParts(managementAddress, managementPort); + } + /** * Setup the TemplateOptions to create the user. */ @@ -2268,11 +2294,13 @@ public JcloudsMachineLocation registerMachine(ConfigBag setup) throws NoMachines protected JcloudsMachineLocation registerMachineLocation(ConfigBag setup, NodeMetadata node) { ComputeService computeService = getComputeService(setup); + String vmHostname = getPublicHostname(node, Optional.absent(), Suppliers.ofInstance(null), setup); + HostAndPort managementHostAndPort = resolveManagementHostAndPort(computeService, node, vmHostname, Optional.absent(), setup, true); if (isWindows(node, setup)) { - return registerWinRmMachineLocation(computeService, node, null, Optional.absent(), setup); + return registerWinRmMachineLocation(computeService, node, managementHostAndPort, Optional.absent(), setup); } else { try { - return registerJcloudsSshMachineLocation(computeService, node, Optional.