http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/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 deleted file mode 100644 index ed3ed2a..0000000 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java +++ /dev/null @@ -1,2863 +0,0 @@ -/* - * 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 brooklyn.location.jclouds; - -import static brooklyn.util.JavaGroovyEquivalents.elvis; -import static brooklyn.util.JavaGroovyEquivalents.groovyTruth; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.jclouds.compute.options.RunScriptOptions.Builder.overrideLoginCredentials; -import static org.jclouds.scriptbuilder.domain.Statements.exec; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.security.KeyPair; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nullable; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.management.AccessController; -import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; -import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; -import org.jclouds.compute.ComputeService; -import org.jclouds.compute.RunNodesException; -import org.jclouds.compute.config.AdminAccessConfiguration; -import org.jclouds.compute.domain.ComputeMetadata; -import org.jclouds.compute.domain.ExecResponse; -import org.jclouds.compute.domain.Hardware; -import org.jclouds.compute.domain.Image; -import org.jclouds.compute.domain.NodeMetadata; -import org.jclouds.compute.domain.NodeMetadata.Status; -import org.jclouds.compute.domain.NodeMetadataBuilder; -import org.jclouds.compute.domain.OperatingSystem; -import org.jclouds.compute.domain.OsFamily; -import org.jclouds.compute.domain.Template; -import org.jclouds.compute.domain.TemplateBuilder; -import org.jclouds.compute.domain.TemplateBuilderSpec; -import org.jclouds.compute.functions.Sha512Crypt; -import org.jclouds.compute.options.TemplateOptions; -import org.jclouds.domain.Credentials; -import org.jclouds.domain.LocationScope; -import org.jclouds.domain.LoginCredentials; -import org.jclouds.ec2.compute.options.EC2TemplateOptions; -import org.jclouds.googlecomputeengine.compute.options.GoogleComputeEngineTemplateOptions; -import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions; -import org.jclouds.rest.AuthorizationException; -import org.jclouds.scriptbuilder.domain.LiteralStatement; -import org.jclouds.scriptbuilder.domain.Statement; -import org.jclouds.scriptbuilder.domain.StatementList; -import org.jclouds.scriptbuilder.domain.Statements; -import org.jclouds.scriptbuilder.functions.InitAdminAccess; -import org.jclouds.scriptbuilder.statements.login.AdminAccess; -import org.jclouds.scriptbuilder.statements.login.ReplaceShadowPasswordEntry; -import org.jclouds.scriptbuilder.statements.ssh.AuthorizeRSAPublicKeys; -import org.jclouds.softlayer.compute.options.SoftLayerTemplateOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.base.Objects; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.base.Splitter; -import com.google.common.base.Stopwatch; -import com.google.common.base.Supplier; -import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.collect.Sets.SetView; -import com.google.common.io.Files; -import com.google.common.net.HostAndPort; -import com.google.common.primitives.Ints; - -import brooklyn.config.ConfigKey; -import brooklyn.config.ConfigKey.HasConfigKey; -import brooklyn.config.ConfigUtils; -import brooklyn.entity.basic.Sanitizer; -import brooklyn.entity.rebind.persister.LocationWithObjectStore; -import brooklyn.entity.rebind.persister.PersistenceObjectStore; -import brooklyn.entity.rebind.persister.jclouds.JcloudsBlobStoreBasedObjectStore; -import brooklyn.location.LocationSpec; -import brooklyn.location.MachineLocation; -import brooklyn.location.MachineLocationCustomizer; -import brooklyn.location.MachineManagementMixins.MachineMetadata; -import brooklyn.location.MachineManagementMixins.RichMachineProvisioningLocation; -import brooklyn.location.MachineManagementMixins.SuspendsMachines; -import brooklyn.location.NoMachinesAvailableException; -import brooklyn.location.access.PortForwardManager; -import brooklyn.location.access.PortMapping; -import brooklyn.location.basic.AbstractLocation; -import brooklyn.location.basic.BasicMachineMetadata; -import brooklyn.location.basic.LocationConfigKeys; -import brooklyn.location.basic.LocationConfigUtils; -import brooklyn.location.basic.LocationConfigUtils.OsCredential; -import brooklyn.location.basic.SshMachineLocation; -import brooklyn.location.basic.WinRmMachineLocation; -import brooklyn.location.cloud.AbstractCloudMachineProvisioningLocation; -import brooklyn.location.cloud.AvailabilityZoneExtension; -import brooklyn.location.cloud.names.AbstractCloudMachineNamer; -import brooklyn.location.cloud.names.CloudMachineNamer; -import brooklyn.location.jclouds.JcloudsPredicates.NodeInLocation; -import brooklyn.location.jclouds.networking.JcloudsPortForwarderExtension; -import brooklyn.location.jclouds.templates.PortableTemplateBuilder; -import brooklyn.location.jclouds.zone.AwsAvailabilityZoneExtension; -import brooklyn.util.ResourceUtils; -import brooklyn.util.collections.MutableList; -import brooklyn.util.collections.MutableMap; -import brooklyn.util.collections.MutableSet; -import brooklyn.util.config.ConfigBag; -import brooklyn.util.crypto.SecureKeys; -import brooklyn.util.exceptions.CompoundRuntimeException; -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.exceptions.ReferenceWithError; -import brooklyn.util.flags.MethodCoercions; -import brooklyn.util.flags.SetFromFlag; -import brooklyn.util.flags.TypeCoercions; -import brooklyn.util.guava.Maybe; -import brooklyn.util.internal.ssh.ShellTool; -import brooklyn.util.internal.ssh.SshTool; -import brooklyn.util.javalang.Enums; -import brooklyn.util.javalang.Reflections; -import brooklyn.util.net.Cidr; -import brooklyn.util.net.Networking; -import brooklyn.util.net.Protocol; -import brooklyn.util.os.Os; -import brooklyn.util.repeat.Repeater; -import brooklyn.util.ssh.BashCommands; -import brooklyn.util.ssh.IptablesCommands; -import brooklyn.util.ssh.IptablesCommands.Chain; -import brooklyn.util.ssh.IptablesCommands.Policy; -import brooklyn.util.stream.Streams; -import brooklyn.util.text.ByteSizeStrings; -import brooklyn.util.text.Identifiers; -import brooklyn.util.text.KeyValueParser; -import brooklyn.util.text.Strings; -import brooklyn.util.text.TemplateProcessor; -import brooklyn.util.time.Duration; -import brooklyn.util.time.Time; -import io.cloudsoft.winrm4j.pywinrm.Session; -import io.cloudsoft.winrm4j.pywinrm.WinRMFactory; - -/** - * For provisioning and managing VMs in a particular provider/region, using jclouds. - * Configuration flags are defined in {@link JcloudsLocationConfig}. - */ -@SuppressWarnings("serial") -public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation implements - JcloudsLocationConfig, RichMachineProvisioningLocation<MachineLocation>, - LocationWithObjectStore, SuspendsMachines { - - // TODO After converting from Groovy to Java, this is now very bad code! It relies entirely on putting - // things into and taking them out of maps; it's not type-safe, and it's thus very error-prone. - // In Groovy, that's considered ok but not in Java. - - // TODO test (and fix) ability to set config keys from flags - - // TODO we say config is inherited, but it isn't the case for many "deep" / jclouds properties - // e.g. when we pass getRawLocalConfigBag() in and decorate it with additional flags - // (inheritance only works when we call getConfig in this class) - - public static final Logger LOG = LoggerFactory.getLogger(JcloudsLocation.class); - - public static final String ROOT_USERNAME = "root"; - /** these userNames are known to be the preferred/required logins in some common/default images - * where root@ is not allowed to log in */ - public static final List<String> ROOT_ALIASES = ImmutableList.of("ubuntu", "ec2-user"); - public static final List<String> COMMON_USER_NAMES_TO_TRY = ImmutableList.<String>builder().add(ROOT_USERNAME).addAll(ROOT_ALIASES).add("admin").build(); - - private static final Pattern LIST_PATTERN = Pattern.compile("^\\[(.*)\\]$"); - private static final Pattern INTEGER_PATTERN = Pattern.compile("^\\d*$"); - - private static final int NOTES_MAX_LENGTH = 1000; - - private final AtomicBoolean loggedSshKeysHint = new AtomicBoolean(false); - private final AtomicBoolean listedAvailableTemplatesOnNoSuchTemplate = new AtomicBoolean(false); - - private final Map<String,Map<String, ? extends Object>> tagMapping = Maps.newLinkedHashMap(); - - @SetFromFlag // so it's persisted - private final Map<MachineLocation,String> vmInstanceIds = Maps.newLinkedHashMap(); - - static { Networking.init(); } - - public JcloudsLocation() { - super(); - } - - /** typically wants at least ACCESS_IDENTITY and ACCESS_CREDENTIAL */ - public JcloudsLocation(Map<?,?> conf) { - super(conf); - } - - @Override - @Deprecated - public JcloudsLocation configure(Map<?,?> properties) { - super.configure(properties); - - if (config().getLocalBag().containsKey("providerLocationId")) { - LOG.warn("Using deprecated 'providerLocationId' key in "+this); - if (!config().getLocalBag().containsKey(CLOUD_REGION_ID)) - config().addToLocalBag(MutableMap.of(CLOUD_REGION_ID.getName(), (String)config().getLocalBag().getStringKey("providerLocationId"))); - } - - if (isDisplayNameAutoGenerated() || !groovyTruth(getDisplayName())) { - setDisplayName(elvis(getProvider(), "unknown") + - (groovyTruth(getRegion()) ? ":"+getRegion() : "") + - (groovyTruth(getEndpoint()) ? ":"+getEndpoint() : "")); - } - - setCreationString(config().getLocalBag()); - - if (getConfig(MACHINE_CREATION_SEMAPHORE) == null) { - Integer maxConcurrent = getConfig(MAX_CONCURRENT_MACHINE_CREATIONS); - if (maxConcurrent == null || maxConcurrent < 1) { - throw new IllegalStateException(MAX_CONCURRENT_MACHINE_CREATIONS.getName() + " must be >= 1, but was "+maxConcurrent); - } - config().set(MACHINE_CREATION_SEMAPHORE, new Semaphore(maxConcurrent, true)); - } - return this; - } - - @Override - public void init() { - super.init(); - if ("aws-ec2".equals(getProvider())) { - addExtension(AvailabilityZoneExtension.class, new AwsAvailabilityZoneExtension(getManagementContext(), this)); - } - } - - @Override - public JcloudsLocation newSubLocation(Map<?,?> newFlags) { - return newSubLocation(getClass(), newFlags); - } - - @Override - public JcloudsLocation newSubLocation(Class<? extends AbstractCloudMachineProvisioningLocation> type, Map<?,?> newFlags) { - // TODO should be able to use ConfigBag.newInstanceExtending; would require moving stuff around to api etc - return (JcloudsLocation) getManagementContext().getLocationManager().createLocation(LocationSpec.create(type) - .parent(this) - .configure(config().getLocalBag().getAllConfig()) // FIXME Should this just be inherited? - .configure(MACHINE_CREATION_SEMAPHORE, getMachineCreationSemaphore()) - .configure(newFlags)); - } - - @Override - public String toString() { - Object identity = getIdentity(); - String configDescription = config().getLocalBag().getDescription(); - if (configDescription!=null && configDescription.startsWith(getClass().getSimpleName())) - return configDescription; - return getClass().getSimpleName()+"["+getDisplayName()+":"+(identity != null ? identity : null)+ - (configDescription!=null ? "/"+configDescription : "") + "@" + getId() + "]"; - } - - @Override - public String toVerboseString() { - return Objects.toStringHelper(this).omitNullValues() - .add("id", getId()).add("name", getDisplayName()).add("identity", getIdentity()) - .add("description", config().getLocalBag().getDescription()).add("provider", getProvider()) - .add("region", getRegion()).add("endpoint", getEndpoint()) - .toString(); - } - - public String getProvider() { - return getConfig(CLOUD_PROVIDER); - } - - public String getIdentity() { - return getConfig(ACCESS_IDENTITY); - } - - public String getCredential() { - return getConfig(ACCESS_CREDENTIAL); - } - - /** returns the location ID used by the provider, if set, e.g. us-west-1 */ - public String getRegion() { - return getConfig(CLOUD_REGION_ID); - } - - public String getEndpoint() { - return (String) config().getBag().getWithDeprecation(CLOUD_ENDPOINT, JCLOUDS_KEY_ENDPOINT); - } - - public String getUser(ConfigBag config) { - return (String) config.getWithDeprecation(USER, JCLOUDS_KEY_USERNAME); - } - - public boolean isWindows(Template template, ConfigBag config) { - return isWindows(template.getImage(), config); - } - - /** - * Whether VMs provisioned from this image will be Windows. Assume windows if the image - * 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 && os.getFamily() != OsFamily.UNRECOGNIZED) - ? (OsFamily.WINDOWS == os.getFamily()) - : (OsFamily.WINDOWS == confFamily); - } - - /** - * Whether the given VM is Windows. - * - * @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 && os.getFamily() != OsFamily.UNRECOGNIZED) - ? (OsFamily.WINDOWS == os.getFamily()) - : (OsFamily.WINDOWS == confFamily); - } - - public boolean isLocationFirewalldEnabled(SshMachineLocation location) { - int result = location.execCommands("checking if firewalld is active", - ImmutableList.of(IptablesCommands.firewalldServiceIsActive())); - if (result == 0) { - return true; - } - - return false; - } - - protected Semaphore getMachineCreationSemaphore() { - return checkNotNull(getConfig(MACHINE_CREATION_SEMAPHORE), MACHINE_CREATION_SEMAPHORE.getName()); - } - - protected CloudMachineNamer getCloudMachineNamer(ConfigBag config) { - String namerClass = config.get(LocationConfigKeys.CLOUD_MACHINE_NAMER_CLASS); - if (Strings.isNonBlank(namerClass)) { - Optional<CloudMachineNamer> cloudNamer = Reflections.invokeConstructorWithArgs(getManagementContext().getCatalogClassLoader(), namerClass); - if (cloudNamer.isPresent()) { - return cloudNamer.get(); - } else { - throw new IllegalStateException("Failed to create CloudMachineNamer "+namerClass+" for location "+this); - } - } else { - return new JcloudsMachineNamer(); - } - } - - protected Collection<JcloudsLocationCustomizer> getCustomizers(ConfigBag setup) { - @SuppressWarnings("deprecation") - JcloudsLocationCustomizer customizer = setup.get(JCLOUDS_LOCATION_CUSTOMIZER); - Collection<JcloudsLocationCustomizer> customizers = setup.get(JCLOUDS_LOCATION_CUSTOMIZERS); - @SuppressWarnings("deprecation") - String customizerType = setup.get(JCLOUDS_LOCATION_CUSTOMIZER_TYPE); - @SuppressWarnings("deprecation") - String customizersSupplierType = setup.get(JCLOUDS_LOCATION_CUSTOMIZERS_SUPPLIER_TYPE); - - ClassLoader catalogClassLoader = getManagementContext().getCatalogClassLoader(); - List<JcloudsLocationCustomizer> result = new ArrayList<JcloudsLocationCustomizer>(); - if (customizer != null) result.add(customizer); - if (customizers != null) result.addAll(customizers); - if (Strings.isNonBlank(customizerType)) { - Optional<JcloudsLocationCustomizer> customizerByType = Reflections.invokeConstructorWithArgs(catalogClassLoader, customizerType, setup); - if (customizerByType.isPresent()) { - result.add(customizerByType.get()); - } else { - customizerByType = Reflections.invokeConstructorWithArgs(catalogClassLoader, customizerType); - if (customizerByType.isPresent()) { - result.add(customizerByType.get()); - } else { - throw new IllegalStateException("Failed to create JcloudsLocationCustomizer "+customizersSupplierType+" for location "+this); - } - } - } - if (Strings.isNonBlank(customizersSupplierType)) { - Optional<Supplier<Collection<JcloudsLocationCustomizer>>> supplier = Reflections.invokeConstructorWithArgs(catalogClassLoader, customizersSupplierType, setup); - if (supplier.isPresent()) { - result.addAll(supplier.get().get()); - } else { - supplier = Reflections.invokeConstructorWithArgs(catalogClassLoader, customizersSupplierType); - if (supplier.isPresent()) { - result.addAll(supplier.get().get()); - } else { - throw new IllegalStateException("Failed to create JcloudsLocationCustomizer supplier "+customizersSupplierType+" for location "+this); - } - } - } - return result; - } - - protected Collection<MachineLocationCustomizer> getMachineCustomizers(ConfigBag setup) { - Collection<MachineLocationCustomizer> customizers = setup.get(MACHINE_LOCATION_CUSTOMIZERS); - return (customizers == null ? ImmutableList.<MachineLocationCustomizer>of() : customizers); - } - - public void setDefaultImageId(String val) { - config().set(DEFAULT_IMAGE_ID, val); - } - - // TODO remove tagMapping, or promote it - // (i think i favour removing it, letting the config come in from the entity) - - public void setTagMapping(Map<String,Map<String, ? extends Object>> val) { - tagMapping.clear(); - tagMapping.putAll(val); - } - - // TODO Decide on semantics. If I give "TomcatServer" and "Ubuntu", then must I get back an image that matches both? - // Currently, just takes first match that it finds... - @Override - public Map<String,Object> getProvisioningFlags(Collection<String> tags) { - Map<String,Object> result = Maps.newLinkedHashMap(); - Collection<String> unmatchedTags = Lists.newArrayList(); - for (String it : tags) { - if (groovyTruth(tagMapping.get(it)) && !groovyTruth(result)) { - result.putAll(tagMapping.get(it)); - } else { - unmatchedTags.add(it); - } - } - if (unmatchedTags.size() > 0) { - LOG.debug("Location {}, failed to match provisioning tags {}", this, unmatchedTags); - } - return result; - } - - public static final Set<ConfigKey<?>> getAllSupportedProperties() { - Set<String> configsOnClass = Sets.newLinkedHashSet( - Iterables.transform(ConfigUtils.getStaticKeysOnClass(JcloudsLocation.class), - new Function<HasConfigKey<?>,String>() { - @Override @Nullable - public String apply(@Nullable HasConfigKey<?> input) { - return input.getConfigKey().getName(); - } - })); - Set<ConfigKey<?>> configKeysInList = ImmutableSet.<ConfigKey<?>>builder() - .addAll(SUPPORTED_TEMPLATE_BUILDER_PROPERTIES.keySet()) - .addAll(SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES.keySet()) - .build(); - Set<String> configsInList = Sets.newLinkedHashSet( - Iterables.transform(configKeysInList, - new Function<ConfigKey<?>,String>() { - @Override @Nullable - public String apply(@Nullable ConfigKey<?> input) { - return input.getName(); - } - })); - - SetView<String> extrasInList = Sets.difference(configsInList, configsOnClass); - // notInList is normal - if (!extrasInList.isEmpty()) - LOG.warn("JcloudsLocation supported properties differs from config defined on class: " + extrasInList); - return Collections.unmodifiableSet(configKeysInList); - } - - public ComputeService getComputeService() { - return getComputeService(MutableMap.of()); - } - public ComputeService getComputeService(Map<?,?> flags) { - ConfigBag conf = (flags==null || flags.isEmpty()) - ? config().getBag() - : ConfigBag.newInstanceExtending(config().getBag(), flags); - return getConfig(COMPUTE_SERVICE_REGISTRY).findComputeService(conf, true); - } - - /** @deprecated since 0.7.0 use {@link #listMachines()} */ @Deprecated - public Set<? extends ComputeMetadata> listNodes() { - return listNodes(MutableMap.of()); - } - /** @deprecated since 0.7.0 use {@link #listMachines()}. - * (no support for custom compute service flags; if that is needed, we'll have to introduce a new method, - * but it seems there are no usages) */ @Deprecated - public Set<? extends ComputeMetadata> listNodes(Map<?,?> flags) { - return getComputeService(flags).listNodes(); - } - - @Override - public Map<String, MachineMetadata> listMachines() { - Set<? extends ComputeMetadata> nodes = - getRegion()!=null ? getComputeService().listNodesDetailsMatching(new NodeInLocation(getRegion(), true)) - : getComputeService().listNodes(); - Map<String,MachineMetadata> result = new LinkedHashMap<String, MachineMetadata>(); - - for (ComputeMetadata node: nodes) - result.put(node.getId(), getMachineMetadata(node)); - - return result; - } - - protected MachineMetadata getMachineMetadata(ComputeMetadata node) { - if (node==null) - return null; - return new BasicMachineMetadata(node.getId(), node.getName(), - ((node instanceof NodeMetadata) ? Iterators.tryFind( ((NodeMetadata)node).getPublicAddresses().iterator(), Predicates.alwaysTrue() ).orNull() : null), - ((node instanceof NodeMetadata) ? ((NodeMetadata)node).getStatus()==Status.RUNNING : null), - node); - } - - @Override - public MachineMetadata getMachineMetadata(MachineLocation l) { - if (l instanceof JcloudsSshMachineLocation) { - return getMachineMetadata( ((JcloudsSshMachineLocation)l).node ); - } - return null; - } - - @Override - public void killMachine(String cloudServiceId) { - getComputeService().destroyNode(cloudServiceId); - } - - @Override - public void killMachine(MachineLocation l) { - MachineMetadata m = getMachineMetadata(l); - if (m==null) throw new NoSuchElementException("Machine "+l+" is not known at "+this); - killMachine(m.getId()); - } - - /** attaches a string describing where something is being created - * (provider, region/location and/or endpoint, callerContext) */ - protected void setCreationString(ConfigBag config) { - config.setDescription(elvis(config.get(CLOUD_PROVIDER), "unknown")+ - (config.containsKey(CLOUD_REGION_ID) ? ":"+config.get(CLOUD_REGION_ID) : "")+ - (config.containsKey(CLOUD_ENDPOINT) ? ":"+config.get(CLOUD_ENDPOINT) : "")+ - (config.containsKey(CALLER_CONTEXT) ? "@"+config.get(CALLER_CONTEXT) : "")); - } - - // ----------------- obtaining a new machine ------------------------ - public MachineLocation obtain() throws NoMachinesAvailableException { - return obtain(MutableMap.of()); - } - public MachineLocation obtain(TemplateBuilder tb) throws NoMachinesAvailableException { - return obtain(MutableMap.of(), tb); - } - public MachineLocation obtain(Map<?,?> flags, TemplateBuilder tb) throws NoMachinesAvailableException { - return obtain(MutableMap.builder().putAll(flags).put(TEMPLATE_BUILDER, tb).build()); - } - - /** core method for obtaining a VM using jclouds; - * Map should contain CLOUD_PROVIDER and CLOUD_ENDPOINT or CLOUD_REGION, depending on the cloud, - * as well as ACCESS_IDENTITY and ACCESS_CREDENTIAL, - * plus any further properties to specify e.g. images, hardware profiles, accessing user - * (for initial login, and a user potentially to create for subsequent ie normal access) */ - @Override - public MachineLocation obtain(Map<?,?> flags) throws NoMachinesAvailableException { - ConfigBag setup = ConfigBag.newInstanceExtending(config().getBag(), flags); - Integer attempts = setup.get(MACHINE_CREATE_ATTEMPTS); - List<Exception> exceptions = Lists.newArrayList(); - if (attempts == null || attempts < 1) attempts = 1; - for (int i = 1; i <= attempts; i++) { - try { - return obtainOnce(setup); - } catch (RuntimeException e) { - LOG.warn("Attempt #{}/{} to obtain machine threw error: {}", new Object[]{i, attempts, e}); - exceptions.add(e); - } - } - String msg = String.format("Failed to get VM after %d attempt%s.", attempts, attempts == 1 ? "" : "s"); - - Exception cause = (exceptions.size() == 1) - ? exceptions.get(0) - : new CompoundRuntimeException(msg + " - " - + "First cause is "+exceptions.get(0)+" (listed in primary trace); " - + "plus " + (exceptions.size()-1) + " more (e.g. the last is "+exceptions.get(exceptions.size()-1)+")", - exceptions.get(0), exceptions); - - if (exceptions.get(exceptions.size()-1) instanceof NoMachinesAvailableException) { - throw new NoMachinesAvailableException(msg, cause); - } else { - throw Exceptions.propagate(cause); - } - } - - protected MachineLocation obtainOnce(ConfigBag setup) throws NoMachinesAvailableException { - AccessController.Response access = getManagementContext().getAccessController().canProvisionLocation(this); - if (!access.isAllowed()) { - throw new IllegalStateException("Access controller forbids provisioning in "+this+": "+access.getMsg()); - } - - setCreationString(setup); - boolean waitForSshable = !"false".equalsIgnoreCase(setup.get(WAIT_FOR_SSHABLE)); - boolean waitForWinRmable = !"false".equalsIgnoreCase(setup.get(WAIT_FOR_WINRM_AVAILABLE)); - boolean usePortForwarding = setup.get(USE_PORT_FORWARDING); - boolean skipJcloudsSshing = Boolean.FALSE.equals(setup.get(USE_JCLOUDS_SSH_INIT)) || usePortForwarding; - JcloudsPortForwarderExtension portForwarder = setup.get(PORT_FORWARDER); - if (usePortForwarding) checkNotNull(portForwarder, "portForwarder, when use-port-forwarding enabled"); - - final ComputeService computeService = getConfig(COMPUTE_SERVICE_REGISTRY).findComputeService(setup, true); - CloudMachineNamer cloudMachineNamer = getCloudMachineNamer(setup); - String groupId = elvis(setup.get(GROUP_ID), cloudMachineNamer.generateNewGroupId(setup)); - NodeMetadata node = null; - JcloudsMachineLocation machineLocation = null; - - try { - LOG.info("Creating VM "+setup.getDescription()+" in "+this); - - Semaphore machineCreationSemaphore = getMachineCreationSemaphore(); - boolean acquired = machineCreationSemaphore.tryAcquire(0, TimeUnit.SECONDS); - if (!acquired) { - LOG.info("Waiting in {} for machine-creation permit ({} other queuing requests already)", new Object[] {this, machineCreationSemaphore.getQueueLength()}); - Stopwatch blockStopwatch = Stopwatch.createStarted(); - machineCreationSemaphore.acquire(); - LOG.info("Acquired in {} machine-creation permit, after waiting {}", this, Time.makeTimeStringRounded(blockStopwatch)); - } else { - LOG.debug("Acquired in {} machine-creation permit immediately", this); - } - - Stopwatch provisioningStopwatch = Stopwatch.createStarted(); - Duration templateTimestamp, provisionTimestamp, usableTimestamp, customizedTimestamp; - - LoginCredentials userCredentials = null; - Set<? extends NodeMetadata> nodes; - Template template; - try { - // Setup the template - template = buildTemplate(computeService, setup); - boolean expectWindows = isWindows(template, setup); - if (!skipJcloudsSshing) { - if (expectWindows) { - // TODO Was this too early to look at template.getImage? e.g. customizeTemplate could subsequently modify it. - LOG.warn("Ignoring invalid configuration for Windows provisioning of "+template.getImage()+": "+USE_JCLOUDS_SSH_INIT.getName()+" should be false"); - skipJcloudsSshing = true; - } else if (waitForSshable) { - userCredentials = initTemplateForCreateUser(template, setup); - } - } - - templateTimestamp = Duration.of(provisioningStopwatch); - // "Name" metadata seems to set the display name; at least in AWS - // TODO it would be nice if this salt comes from the location's ID (but we don't know that yet as the ssh machine location isn't created yet) - // TODO in softlayer we want to control the suffix of the hostname which is 3 random hex digits - template.getOptions().getUserMetadata().put("Name", cloudMachineNamer.generateNewMachineUniqueNameFromGroupId(setup, groupId)); - - if (setup.get(JcloudsLocationConfig.INCLUDE_BROOKLYN_USER_METADATA)) { - template.getOptions().getUserMetadata().put("brooklyn-user", System.getProperty("user.name")); - - Object context = setup.get(CALLER_CONTEXT); - if (context instanceof Entity) { - Entity entity = (Entity)context; - template.getOptions().getUserMetadata().put("brooklyn-app-id", entity.getApplicationId()); - template.getOptions().getUserMetadata().put("brooklyn-app-name", entity.getApplication().getDisplayName()); - template.getOptions().getUserMetadata().put("brooklyn-entity-id", entity.getId()); - template.getOptions().getUserMetadata().put("brooklyn-entity-name", entity.getDisplayName()); - template.getOptions().getUserMetadata().put("brooklyn-server-creation-date", Time.makeDateSimpleStampString()); - } - } - - customizeTemplate(setup, computeService, template); - - LOG.debug("jclouds using template {} / options {} to provision machine in {}", - new Object[] {template, template.getOptions(), setup.getDescription()}); - - if (!setup.getUnusedConfig().isEmpty()) - LOG.debug("NOTE: unused flags passed to obtain VM in "+setup.getDescription()+": "+ - setup.getUnusedConfig()); - - nodes = computeService.createNodesInGroup(groupId, 1, template); - provisionTimestamp = Duration.of(provisioningStopwatch); - } finally { - machineCreationSemaphore.release(); - } - - node = Iterables.getOnlyElement(nodes, null); - LOG.debug("jclouds created {} for {}", node, setup.getDescription()); - if (node == null) - 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, - node.getLoginPort(), - Optional.<Integer>absent(), - Protocol.TCP, - Cidr.UNIVERSAL)); - } else { - sshHostAndPortOverride = Optional.absent(); - } - - if (skipJcloudsSshing) { - boolean waitForConnectable = (windows) ? waitForWinRmable : waitForSshable; - if (waitForConnectable) { - if (windows) { - // TODO Does jclouds support any windows user setup? - waitForWinRmAvailable(computeService, node, sshHostAndPortOverride, node.getCredentials(), setup); - } else { - waitForSshable(computeService, node, sshHostAndPortOverride, node.getCredentials(), setup); - } - userCredentials = createUser(computeService, node, sshHostAndPortOverride, setup); - } - } - - // Figure out which login-credentials to use - LoginCredentials customCredentials = setup.get(CUSTOM_CREDENTIALS); - if (customCredentials != null) { - userCredentials = customCredentials; - //set userName and other data, from these credentials - Object oldUsername = setup.put(USER, customCredentials.getUser()); - LOG.debug("node {} username {} / {} (customCredentials)", new Object[] { node, customCredentials.getUser(), oldUsername }); - if (customCredentials.getOptionalPassword().isPresent()) setup.put(PASSWORD, customCredentials.getOptionalPassword().get()); - if (customCredentials.getOptionalPrivateKey().isPresent()) setup.put(PRIVATE_KEY_DATA, customCredentials.getOptionalPrivateKey().get()); - } - if (userCredentials == null) { - userCredentials = extractVmCredentials(setup, node); - } - if (userCredentials != null) { - node = NodeMetadataBuilder.fromNodeMetadata(node).credentials(userCredentials).build(); - } else { - // only happens if something broke above... - userCredentials = LoginCredentials.fromCredentials(node.getCredentials()); - } - // store the credentials, in case they have changed - setup.putIfNotNull(JcloudsLocationConfig.PASSWORD, userCredentials.getOptionalPassword().orNull()); - setup.putIfNotNull(JcloudsLocationConfig.PRIVATE_KEY_DATA, userCredentials.getOptionalPrivateKey().orNull()); - - // Wait for the VM to be reachable over SSH - if (waitForSshable && !windows) { - waitForSshable(computeService, node, sshHostAndPortOverride, userCredentials, setup); - } else { - LOG.debug("Skipping ssh check for {} ({}) due to config waitForSshable=false", node, setup.getDescription()); - } - usableTimestamp = Duration.of(provisioningStopwatch); - -// JcloudsSshMachineLocation jcloudsSshMachineLocation = null; -// WinRmMachineLocation winRmMachineLocation = null; - // Create a JcloudsSshMachineLocation, and register it - if (windows) { - machineLocation = registerWinRmMachineLocation(computeService, node, userCredentials, sshHostAndPortOverride, setup); - } else { - machineLocation = registerJcloudsSshMachineLocation(computeService, node, userCredentials, sshHostAndPortOverride, setup); - if (template!=null && machineLocation.getTemplate()==null) { - ((JcloudsSshMachineLocation)machineLocation).template = template; - } - } - - if (usePortForwarding && sshHostAndPortOverride.isPresent()) { - // Now that we have the sshMachineLocation, we can associate the port-forwarding address with it. - PortForwardManager portForwardManager = setup.get(PORT_FORWARDING_MANAGER); - if (portForwardManager != null) { - portForwardManager.associate(node.getId(), sshHostAndPortOverride.get(), machineLocation, node.getLoginPort()); - } else { - LOG.warn("No port-forward manager for {} so could not associate {} -> {} for {}", - new Object[] {this, node.getLoginPort(), sshHostAndPortOverride, machineLocation}); - } - } - - if ("docker".equals(this.getProvider())) { - if (windows) { - throw new UnsupportedOperationException("Docker not supported on Windows"); - } - Map<Integer, Integer> portMappings = JcloudsUtil.dockerPortMappingsFor(this, node.getId()); - PortForwardManager portForwardManager = setup.get(PORT_FORWARDING_MANAGER); - if (portForwardManager != null) { - for(Integer containerPort : portMappings.keySet()) { - Integer hostPort = portMappings.get(containerPort); - String dockerHost = ((JcloudsSshMachineLocation)machineLocation).getSshHostAndPort().getHostText(); - portForwardManager.associate(node.getId(), HostAndPort.fromParts(dockerHost, hostPort), machineLocation, containerPort); - } - } else { - LOG.warn("No port-forward manager for {} so could not associate docker port-mappings for {}", - this, machineLocation); - } - } - - List<String> customisationForLogging = new ArrayList<String>(); - // Apply same securityGroups rules to iptables, if iptables is running on the node - if (waitForSshable) { - - String setupScript = setup.get(JcloudsLocationConfig.CUSTOM_MACHINE_SETUP_SCRIPT_URL); - List<String> setupScripts = setup.get(JcloudsLocationConfig.CUSTOM_MACHINE_SETUP_SCRIPT_URL_LIST); - Collection<String> allScripts = new MutableList<String>().appendIfNotNull(setupScript).appendAll(setupScripts); - for (String setupScriptItem : allScripts) { - if (Strings.isNonBlank(setupScriptItem)) { - customisationForLogging.add("custom setup script " + setupScriptItem); - - String setupVarsString = setup.get(JcloudsLocationConfig.CUSTOM_MACHINE_SETUP_SCRIPT_VARS); - Map<String, String> substitutions = (setupVarsString != null) - ? Splitter.on(",").withKeyValueSeparator(":").split(setupVarsString) - : ImmutableMap.<String, String>of(); - 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")))); - } else { - ((SshMachineLocation)machineLocation).execCommands("Customizing node " + this, ImmutableList.of(script)); - } - } - } - - if (setup.get(JcloudsLocationConfig.MAP_DEV_RANDOM_TO_DEV_URANDOM)) { - if (windows) { - LOG.warn("Ignoring flag MAP_DEV_RANDOM_TO_DEV_URANDOM on Windows location {}", machineLocation); - } else { - customisationForLogging.add("point /dev/random to urandom"); - - ((SshMachineLocation)machineLocation).execCommands("using urandom instead of random", - Arrays.asList("sudo mv /dev/random /dev/random-real", "sudo ln -s /dev/urandom /dev/random")); - } - } - - - if (setup.get(GENERATE_HOSTNAME)) { - if (windows) { - // TODO: Generate Windows Hostname - LOG.warn("Ignoring flag GENERATE_HOSTNAME on Windows location {}", machineLocation); - } else { - customisationForLogging.add("configure hostname"); - - ((SshMachineLocation)machineLocation).execCommands("Generate hostname " + node.getName(), - Arrays.asList("sudo hostname " + node.getName(), - "sudo sed -i \"s/HOSTNAME=.*/HOSTNAME=" + node.getName() + "/g\" /etc/sysconfig/network", - "sudo bash -c \"echo 127.0.0.1 `hostname` >> /etc/hosts\"") - ); - } - } - - if (setup.get(OPEN_IPTABLES)) { - if (windows) { - LOG.warn("Ignoring DEPRECATED flag OPEN_IPTABLES on Windows location {}", machineLocation); - } else { - LOG.warn("Using DEPRECATED flag OPEN_IPTABLES (will not be supported in future versions) for {} at {}", machineLocation, this); - - @SuppressWarnings("unchecked") - Iterable<Integer> inboundPorts = (Iterable<Integer>) setup.get(INBOUND_PORTS); - - if (inboundPorts == null || Iterables.isEmpty(inboundPorts)) { - LOG.info("No ports to open in iptables (no inbound ports) for {} at {}", machineLocation, this); - } else { - customisationForLogging.add("open iptables"); - - List<String> iptablesRules = Lists.newArrayList(); - - if (isLocationFirewalldEnabled((SshMachineLocation)machineLocation)) { - for (Integer port : inboundPorts) { - iptablesRules.add(IptablesCommands.addFirewalldRule(Chain.INPUT, Protocol.TCP, port, Policy.ACCEPT)); - } - } else { - iptablesRules = createIptablesRulesForNetworkInterface(inboundPorts); - iptablesRules.add(IptablesCommands.saveIptablesRules()); - } - List<String> batch = Lists.newArrayList(); - // Some entities, such as Riak (erlang based) have a huge range of ports, which leads to a script that - // is too large to run (fails with a broken pipe). Batch the rules into batches of 50 - for (String rule : iptablesRules) { - batch.add(rule); - if (batch.size() == 50) { - ((SshMachineLocation)machineLocation).execCommands("Inserting iptables rules, 50 command batch", batch); - batch.clear(); - } - } - if (batch.size() > 0) { - ((SshMachineLocation)machineLocation).execCommands("Inserting iptables rules", batch); - } - ((SshMachineLocation)machineLocation).execCommands("List iptables rules", ImmutableList.of(IptablesCommands.listIptablesRule())); - } - } - } - - if (setup.get(STOP_IPTABLES)) { - if (windows) { - LOG.warn("Ignoring DEPRECATED flag OPEN_IPTABLES on Windows location {}", machineLocation); - } else { - LOG.warn("Using DEPRECATED flag STOP_IPTABLES (will not be supported in future versions) for {} at {}", machineLocation, this); - - customisationForLogging.add("stop iptables"); - - List<String> cmds = ImmutableList.<String>of(); - if (isLocationFirewalldEnabled((SshMachineLocation)machineLocation)) { - cmds = ImmutableList.of(IptablesCommands.firewalldServiceStop(), IptablesCommands.firewalldServiceStatus()); - } else { - cmds = ImmutableList.of(IptablesCommands.iptablesServiceStop(), IptablesCommands.iptablesServiceStatus()); - } - ((SshMachineLocation)machineLocation).execCommands("Stopping iptables", cmds); - } - } - - List<String> extraKeyUrlsToAuth = setup.get(EXTRA_PUBLIC_KEY_URLS_TO_AUTH); - if (extraKeyUrlsToAuth!=null && !extraKeyUrlsToAuth.isEmpty()) { - if (windows) { - LOG.warn("Ignoring flag EXTRA_PUBLIC_KEY_URLS_TO_AUTH on Windows location", machineLocation); - } else { - List<String> extraKeyDataToAuth = MutableList.of(); - for (String keyUrl : extraKeyUrlsToAuth) { - extraKeyDataToAuth.add(ResourceUtils.create().getResourceAsString(keyUrl)); - } - ((SshMachineLocation)machineLocation).execCommands("Authorizing ssh keys", - ImmutableList.of(new AuthorizeRSAPublicKeys(extraKeyDataToAuth).render(org.jclouds.scriptbuilder.domain.OsFamily.UNIX))); - } - } - - } else { - // Otherwise we have deliberately not waited to be ssh'able, so don't try now to - // ssh to exec these commands! - } - - // Apply any optional app-specific customization. - for (JcloudsLocationCustomizer customizer : getCustomizers(setup)) { - customizer.customize(this, computeService, machineLocation); - } - for (MachineLocationCustomizer customizer : getMachineCustomizers(setup)) { - customizer.customize(machineLocation); - } - - customizedTimestamp = Duration.of(provisioningStopwatch); - - try { - String logMessage = "Finished VM "+setup.getDescription()+" creation:" - + " "+machineLocation.getUser()+"@"+machineLocation.getAddress()+":"+machineLocation.getPort() - + (Boolean.TRUE.equals(setup.get(LOG_CREDENTIALS)) - ? "password=" + userCredentials.getOptionalPassword().or("<absent>") - + " && key=" + userCredentials.getOptionalPrivateKey().or("<absent>") - : "") - + " ready after "+Duration.of(provisioningStopwatch).toStringRounded() - + " ("+template+" template built in "+Duration.of(templateTimestamp).toStringRounded()+";" - + " "+node+" provisioned in "+Duration.of(provisionTimestamp).subtract(templateTimestamp).toStringRounded()+";" - + " "+machineLocation+" connection usable in "+Duration.of(usableTimestamp).subtract(provisionTimestamp).toStringRounded()+";" - + " and os customized in "+Duration.of(customizedTimestamp).subtract(usableTimestamp).toStringRounded()+" - "+Joiner.on(", ").join(customisationForLogging)+")"; - LOG.info(logMessage); - } catch (Exception e){ - // TODO Remove try-catch! @Nakomis: why did you add it? What exception happened during logging? - Exceptions.propagateIfFatal(e); - LOG.warn("Problem generating log message summarising completion of jclouds machine provisioning "+machineLocation+" by "+this, e); - } - - return machineLocation; - - } catch (Exception e) { - if (e instanceof RunNodesException && ((RunNodesException)e).getNodeErrors().size() > 0) { - node = Iterables.get(((RunNodesException)e).getNodeErrors().keySet(), 0); - } - // sometimes AWS nodes come up busted (eg ssh not allowed); just throw it back (and maybe try for another one) - boolean destroyNode = (node != null) && Boolean.TRUE.equals(setup.get(DESTROY_ON_FAILURE)); - - if (e.toString().contains("VPCResourceNotSpecified")) { - LOG.error("Detected that your EC2 account is a legacy 'classic' account, but the recommended instance type requires VPC. " - + "You can specify the 'eu-central-1' region to avoid this problem, or you can specify a classic-compatible instance type, " - + "or you can specify a subnet to use with 'networkName' " - + "(taking care that the subnet auto-assigns public IP's and allows ingress on all ports, " - + "as Brooklyn does not currently configure security groups for non-default VPC's; " - + "or setting up Brooklyn to be in the subnet or have a jump host or other subnet access configuration). " - + "For more information on VPC vs classic see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-vpc.html."); - } - - LOG.error("Failed to start VM for {}{}: {}", - new Object[] {setup.getDescription(), (destroyNode ? " (destroying "+node+")" : ""), e.getMessage()}); - LOG.debug(Throwables.getStackTraceAsString(e)); - - if (destroyNode) { - if (machineLocation != null) { - releaseSafely(machineLocation); - } else { - releaseNodeSafely(node); - } - } - - throw Exceptions.propagate(e); - } - } - - // ------------- suspend and resume ------------------------------------ - - /** - * Suspends the given location. - * <p> - * Note that this method does <b>not</b> call the lifecycle methods of any - * {@link #getCustomizers(ConfigBag) customizers} attached to this location. - */ - @Override - public void suspendMachine(MachineLocation rawLocation) { - String instanceId = vmInstanceIds.remove(rawLocation); - if (instanceId == null) { - LOG.info("Attempt to suspend unknown machine " + rawLocation + " in " + this); - throw new IllegalArgumentException("Unknown machine " + rawLocation); - } - LOG.info("Suspending machine {} in {}, instance id {}", new Object[]{rawLocation, this, instanceId}); - Exception toThrow = null; - try { - getComputeService().suspendNode(instanceId); - } catch (Exception e) { - toThrow = e; - LOG.error("Problem suspending machine " + rawLocation + " in " + this + ", instance id " + instanceId, e); - } - removeChild(rawLocation); - if (toThrow != null) { - throw Exceptions.propagate(toThrow); - } - } - - // ------------- constructing the template, etc ------------------------ - - private static interface CustomizeTemplateBuilder { - void apply(TemplateBuilder tb, ConfigBag props, Object v); - } - - public static interface CustomizeTemplateOptions { - void apply(TemplateOptions tb, ConfigBag props, Object v); - } - - /** properties which cause customization of the TemplateBuilder */ - public static final Map<ConfigKey<?>,CustomizeTemplateBuilder> SUPPORTED_TEMPLATE_BUILDER_PROPERTIES = ImmutableMap.<ConfigKey<?>,CustomizeTemplateBuilder>builder() - .put(OS_64_BIT, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - Boolean os64Bit = TypeCoercions.coerce(v, Boolean.class); - if (os64Bit!=null) - tb.os64Bit(os64Bit); - }}) - .put(MIN_RAM, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.minRam( (int)(ByteSizeStrings.parse(Strings.toString(v), "mb")/1000/1000) ); - }}) - .put(MIN_CORES, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.minCores(TypeCoercions.coerce(v, Double.class)); - }}) - .put(MIN_DISK, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.minDisk( (int)(ByteSizeStrings.parse(Strings.toString(v), "gb")/1000/1000/1000) ); - }}) - .put(HARDWARE_ID, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.hardwareId(((CharSequence)v).toString()); - }}) - .put(IMAGE_ID, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.imageId(((CharSequence)v).toString()); - }}) - .put(IMAGE_DESCRIPTION_REGEX, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.imageDescriptionMatches(((CharSequence)v).toString()); - }}) - .put(IMAGE_NAME_REGEX, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.imageNameMatches(((CharSequence)v).toString()); - }}) - .put(OS_FAMILY, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - Maybe<OsFamily> osFamily = Enums.valueOfIgnoreCase(OsFamily.class, v.toString()); - if (osFamily.isAbsent()) - throw new IllegalArgumentException("Invalid "+OS_FAMILY+" value "+v); - tb.osFamily(osFamily.get()); - }}) - .put(OS_VERSION_REGEX, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.osVersionMatches( ((CharSequence)v).toString() ); - }}) - .put(TEMPLATE_SPEC, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - tb.from(TemplateBuilderSpec.parse(((CharSequence)v).toString())); - }}) - .put(DEFAULT_IMAGE_ID, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - /* done in the code, but included here so that it is in the map */ - }}) - .put(TEMPLATE_BUILDER, new CustomizeTemplateBuilder() { - public void apply(TemplateBuilder tb, ConfigBag props, Object v) { - /* done in the code, but included here so that it is in the map */ - }}) - .build(); - - /** properties which cause customization of the TemplateOptions */ - public static final Map<ConfigKey<?>,CustomizeTemplateOptions> SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES = ImmutableMap.<ConfigKey<?>,CustomizeTemplateOptions>builder() - .put(SECURITY_GROUPS, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof EC2TemplateOptions) { - String[] securityGroups = toStringArray(v); - ((EC2TemplateOptions)t).securityGroups(securityGroups); - } else if (t instanceof NovaTemplateOptions) { - String[] securityGroups = toStringArray(v); - ((NovaTemplateOptions)t).securityGroups(securityGroups); - } else if (t instanceof SoftLayerTemplateOptions) { - String[] securityGroups = toStringArray(v); - ((SoftLayerTemplateOptions)t).securityGroups(securityGroups); - } else if (t instanceof GoogleComputeEngineTemplateOptions) { - String[] securityGroups = toStringArray(v); - ((GoogleComputeEngineTemplateOptions)t).securityGroups(securityGroups); - } else { - LOG.info("ignoring securityGroups({}) in VM creation because not supported for cloud/type ({})", v, t.getClass()); - } - }}) - .put(INBOUND_PORTS, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - int[] inboundPorts = toIntArray(v); - if (LOG.isDebugEnabled()) LOG.debug("opening inbound ports {} for cloud/type {}", Arrays.toString(inboundPorts), t.getClass()); - t.inboundPorts(inboundPorts); - }}) - .put(USER_METADATA_STRING, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof EC2TemplateOptions) { - // See AWS docs: http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/UsingConfig_WinAMI.html#user-data-execution - if (v==null) return; - String data = v.toString(); - if (!(data.startsWith("<script>") || data.startsWith("<powershell>"))) { - data = "<script> " + data + " </script>"; - } - ((EC2TemplateOptions)t).userData(data.getBytes()); - } else if (t instanceof SoftLayerTemplateOptions) { - ((SoftLayerTemplateOptions)t).userData(Strings.toString(v)); - } else { - // Try reflection: userData(String), or guestCustomizationScript(String); - // the latter is used by vCloud Director. - Class<? extends TemplateOptions> clazz = t.getClass(); - Method userDataMethod = null; - try { - userDataMethod = clazz.getMethod("userData", String.class); - } catch (SecurityException e) { - LOG.info("Problem reflectively inspecting methods of "+t.getClass()+" for setting userData", e); - } catch (NoSuchMethodException e) { - try { - // For vCloud Director - userDataMethod = clazz.getMethod("guestCustomizationScript", String.class); - } catch (NoSuchMethodException e2) { - // expected on various other clouds - } - } - if (userDataMethod != null) { - try { - userDataMethod.invoke(t, Strings.toString(v)); - } catch (InvocationTargetException e) { - LOG.info("Problem invoking "+userDataMethod.getName()+" of "+t.getClass()+", for setting userData (rethrowing)", e); - throw Exceptions.propagate(e); - } catch (IllegalAccessException 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()); - } - } - }}) - .put(USER_DATA_UUENCODED, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof EC2TemplateOptions) { - byte[] bytes = toByteArray(v); - ((EC2TemplateOptions)t).userData(bytes); - } else if (t instanceof SoftLayerTemplateOptions) { - ((SoftLayerTemplateOptions)t).userData(Strings.toString(v)); - } else { - LOG.info("ignoring userData({}) in VM creation because not supported for cloud/type ({})", v, t.getClass()); - } - }}) - .put(STRING_TAGS, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - List<String> tags = toListOfStrings(v); - if (LOG.isDebugEnabled()) LOG.debug("setting VM tags {} for {}", tags, t); - t.tags(tags); - }}) - .put(USER_METADATA_MAP, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (v != null) { - t.userMetadata(toMapStringString(v)); - } - }}) - .put(EXTRA_PUBLIC_KEY_DATA_TO_AUTH, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - t.authorizePublicKey(((CharSequence)v).toString()); - }}) - .put(RUN_AS_ROOT, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - t.runAsRoot((Boolean)v); - }}) - .put(LOGIN_USER, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (v != null) { - t.overrideLoginUser(((CharSequence)v).toString()); - } - }}) - .put(LOGIN_USER_PASSWORD, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (v != null) { - t.overrideLoginPassword(((CharSequence)v).toString()); - } - }}) - .put(LOGIN_USER_PRIVATE_KEY_FILE, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (v != null) { - String privateKeyFileName = ((CharSequence)v).toString(); - String privateKey; - try { - privateKey = Files.toString(new File(Os.tidyPath(privateKeyFileName)), Charsets.UTF_8); - } catch (IOException e) { - LOG.error(privateKeyFileName + "not found", e); - throw Exceptions.propagate(e); - } - t.overrideLoginPrivateKey(privateKey); - } - }}) - .put(LOGIN_USER_PRIVATE_KEY_DATA, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (v != null) { - t.overrideLoginPrivateKey(((CharSequence)v).toString()); - } - }}) - .put(KEY_PAIR, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof EC2TemplateOptions) { - ((EC2TemplateOptions)t).keyPair(((CharSequence)v).toString()); - } else if (t instanceof NovaTemplateOptions) { - ((NovaTemplateOptions)t).keyPairName(((CharSequence)v).toString()); - } else if (t instanceof CloudStackTemplateOptions) { - ((CloudStackTemplateOptions) t).keyPair(((CharSequence) v).toString()); - } else { - LOG.info("ignoring keyPair({}) in VM creation because not supported for cloud/type ({})", v, t); - } - }}) - .put(AUTO_GENERATE_KEYPAIRS, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof NovaTemplateOptions) { - ((NovaTemplateOptions)t).generateKeyPair((Boolean)v); - } else if (t instanceof CloudStackTemplateOptions) { - ((CloudStackTemplateOptions) t).generateKeyPair((Boolean) v); - } else { - LOG.info("ignoring auto-generate-keypairs({}) in VM creation because not supported for cloud/type ({})", v, t); - } - }}) - .put(AUTO_CREATE_FLOATING_IPS, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof NovaTemplateOptions) { - ((NovaTemplateOptions)t).autoAssignFloatingIp((Boolean)v); - } else { - LOG.info("ignoring auto-generate-floating-ips({}) in VM creation because not supported for cloud/type ({})", v, t); - } - }}) - .put(AUTO_ASSIGN_FLOATING_IP, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof NovaTemplateOptions) { - ((NovaTemplateOptions)t).autoAssignFloatingIp((Boolean)v); - } else if (t instanceof CloudStackTemplateOptions) { - ((CloudStackTemplateOptions)t).setupStaticNat((Boolean)v); - } else { - LOG.info("ignoring auto-assign-floating-ip({}) in VM creation because not supported for cloud/type ({})", v, t); - } - }}) - .put(NETWORK_NAME, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof AWSEC2TemplateOptions) { - // subnet ID is the sensible interpretation of network name in EC2 - ((AWSEC2TemplateOptions)t).subnetId((String)v); - - } else { - if (t instanceof GoogleComputeEngineTemplateOptions) { - // no warning needed - // we think this is the only jclouds endpoint which supports this option - - } else if (t instanceof SoftLayerTemplateOptions) { - LOG.warn("networkName is not be supported in SoftLayer; use `templateOptions` with `primaryNetworkComponentNetworkVlanId` or `primaryNetworkBackendComponentNetworkVlanId`"); - } else if (!(t instanceof CloudStackTemplateOptions) && !(t instanceof NovaTemplateOptions)) { - LOG.warn("networkName is experimental in many jclouds endpoints may not be supported in this cloud"); - // NB, from @andreaturli -// Cloudstack uses custom securityGroupIds and networkIds not the generic networks -// Openstack Nova uses securityGroupNames which is marked as @deprecated (suggests to use groups which is maybe even more confusing) -// Azure supports the custom networkSecurityGroupName - } - - t.networks((String)v); - } - }}) - .put(DOMAIN_NAME, new CustomizeTemplateOptions() { - public void apply(TemplateOptions t, ConfigBag props, Object v) { - if (t instanceof SoftLayerTemplateOptions) { - ((SoftLayerTemplateOptions)t).domainName(TypeCoercions.coerce(v, String.class)); - } else { - LOG.info("ignoring domain-name({}) in VM creation because not supported for cloud/type ({})", v, t); - } - }}) - .put(TEMPLATE_OPTIONS, new CustomizeTemplateOptions() { - @Override - public void apply(TemplateOptions options, ConfigBag config, Object v) { - if (v == null) return; - @SuppressWarnings("unchecked") Map<String, Object> optionsMap = (Map<String, Object>) v; - if (optionsMap.isEmpty()) return; - - Class<? extends TemplateOptions> clazz = options.getClass(); - for(final Map.Entry<String, Object> option : optionsMap.entrySet()) { - Maybe<?> result = MethodCoercions.tryFindAndInvokeBestMatchingMethod(options, option.getKey(), option.getValue()); - if(result.isAbsent()) { - LOG.warn("Ignoring request to set template option {} because this is not supported by {}", new Object[] { option.getKey(), clazz.getCanonicalName() }); - } - } - }}) - .build(); - - /** hook whereby template customizations can be made for various clouds */ - protected void customizeTemplate(ConfigBag setup, ComputeService computeService, Template template) { - for (JcloudsLocationCustomizer customizer : getCustomizers(setup)) { - customizer.customize(this, computeService, template); - customizer.customize(this, computeService, template.getOptions()); - } - - // these things are nice on softlayer - if (template.getOptions() instanceof SoftLayerTemplateOptions) { - SoftLayerTemplateOptions slT = ((SoftLayerTemplateOptions)template.getOptions()); - if (Strings.isBlank(slT.getDomainName()) || "jclouds.org".equals(slT.getDomainName())) { - // set a quasi-sensible domain name if none was provided (better than the default, jclouds.org) - // NB: things like brooklyn.local are disallowed - slT.domainName("local.brooklyncentral.org"); - } - // convert user metadata to tags and notes because user metadata is otherwise ignored - Map<String, String> md = slT.getUserMetadata(); - if (md!=null && !md.isEmpty()) { - Set<String> tags = MutableSet.copyOf(slT.getTags()); - for (Map.Entry<String,String> entry: md.entrySet()) { - tags.add(AbstractCloudMachineNamer.sanitize(entry.getKey())+":"+AbstractCloudMachineNamer.sanitize(entry.getValue())); - } - slT.tags(tags); - - if (!md.containsKey("notes")) { - String notes = "User Metadata\n=============\n\n * " + Joiner.on("\n * ").withKeyValueSeparator(": ").join(md); - if (notes.length() > NOTES_MAX_LENGTH) { - String truncatedMsg = "...\n<truncated - notes total length is " + notes.length() + " characters>"; - notes = notes.substring(0, NOTES_MAX_LENGTH - truncatedMsg.length()) + truncatedMsg; - } - md.put("notes", notes); - } - } - } - } - - /** returns the jclouds Template which describes the image to be built, for the given config and compute service */ - public Template buildTemplate(ComputeService computeService, ConfigBag config) { - TemplateBuilder templateBuilder = (TemplateBuilder) config.get(TEMPLATE_BUILDER); - if (templateBuilder==null) { - templateBuilder = new PortableTemplateBuilder<PortableTemplateBuilder<?>>(); - } else { - LOG.debug("jclouds using templateBuilder {} as custom base for provisioning in {} for {}", new Object[] { - templateBuilder, this, config.getDescription()}); - } - if (templateBuilder instanceof PortableTemplateBuilder<?>) { - if (((PortableTemplateBuilder<?>)templateBuilder).imageChooser()==null) { - Function<Iterable<? extends Image>, Image> chooser = config.get(JcloudsLocationConfig.IMAGE_CHOOSER); - chooser = BrooklynImageChooser.cloneFor(chooser, computeService); - templateBuilder.imageChooser(chooser); - } else { - // an image chooser is already set, so do nothing - } - } else { - // template builder supplied, and we cannot check image chooser status; warn, for now - LOG.warn("Cannot check imageChooser status for {} due to manually supplied black-box TemplateBuilder; " - + "it is recommended to use a PortableTemplateBuilder if you supply a TemplateBuilder", config.getDescription()); - } - - if (!Strings.isEmpty(config.get(CLOUD_REGION_ID))) { - templateBuilder.locationId(config.get(CLOUD_REGION_ID)); - } - - // Apply the template builder and options properties - for (Map.Entry<ConfigKey<?>, CustomizeTemplateBuilder> entry : SUPPORTED_TEMPLATE_BUILDER_PROPERTIES.entrySet()) { - ConfigKey<?> name = entry.getKey(); - CustomizeTemplateBuilder code = entry.getValue(); - if (config.containsKey(name)) - code.apply(templateBuilder, config, config.get(name)); - } - - if (templateBuilder instanceof PortableTemplateBuilder) { - ((PortableTemplateBuilder<?>)templateBuilder).attachComputeService(computeService); - // do the default last, and only if nothing else specified (guaranteed to be a PTB if nothing else specified) - if (groovyTruth(config.get(DEFAULT_IMAGE_ID))) { - if (((PortableTemplateBuilder<?>)templateBuilder).isBlank()) { - templateBuilder.imageId(config.get(DEFAULT_IMAGE_ID).toString()); - } - } - } - - // Then apply any optional app-specific customization. - for (JcloudsLocationCustomizer customizer : getCustomizers(config)) { - customizer.customize(this, computeService, templateBuilder); - } - - LOG.debug("jclouds using templateBuilder {} for provisioning in {} for {}", new Object[] { - templateBuilder, this, config.getDescription()}); - - // Finally try to build the template - Template template; - Image image; - try { - template = templateBuilder.build(); - if (template==null) throw new NullPointerException("No template found (templateBuilder.build returned null)"); - image = template.getImage(); - LOG.debug("jclouds found template "+template+" (image "+image+") for provisioning in "+this+" for "+config.getDescription()); - if (image==null) throw new NullPointerException("Template does not contain an image (templateBuilder.build returned invalid template)"); - } catch (AuthorizationException e) { - LOG.warn("Error resolving template: not authorized (rethrowing: "+e+")"); - throw new IllegalStateException("Not authorized to access cloud "+this+" to resolve "+templateBuilder, e); - } catch (Exception e) { - try { - IOException ioe = Exceptions.getFirstThrowableOfType(e, IOException.class); - if (ioe != null) { - LOG.warn("IOException found...", ioe); - throw ioe; - } - if (listedAvailableTemplatesOnNoSuchTemplate.compareAndSet(false, true)) { - // delay subsequent log.warns (put in synch block) so the "Loading..." message is obvious - LOG.warn("Unable to match required VM template constraints "+templateBuilder+" when trying to provision VM in "+this+" (rethrowing): "+e); - logAvailableTemplates(config); - } - } catch (Exception e2) { - LOG.warn("Error loading available images to report (following original error matching template which will be rethrown): "+e2, e2); - throw new IllegalStateException("Unable to access cloud "+this+" to resolve "+templateBuilder+": "+e, e); - } - throw new IllegalStateException("Unable to match required VM template constraints "+templateBuilder+" when trying to provision VM in "+this+"; " - + "see list of images in log. Root cause: "+e, e); - } - TemplateOptions options = template.getOptions(); - - boolean windows = isWindows(template, config); - if (windows) { - if (!(config.containsKey(JcloudsLocationConfig.USER_METADATA_STRING) || config.containsKey(JcloudsLocationConfig.USER_METADATA_MAP))) { - config.put(JcloudsLocationConfig.USER_METADATA_STRING, WinRmMachineLocation.getDefaultUserMetadataString()); - } - } - - for (Map.Entry<ConfigKey<?>, CustomizeTemplateOptions> entry : SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES.entrySet()) { - ConfigKey<?> key = entry.getKey(); - CustomizeTemplateOptions code = entry.getValue(); - if (config.containsKey(key)) - code.apply(options, config, config.get(key)); - } - - return template; - } - - protected void logAvailableTemplates(ConfigBag config) { - LOG.info("Loading available images at "+this+" for reference..."); - ConfigBag m1 = ConfigBag.newInstanceCopying(config); - if (m1.containsKey(IMAGE_ID)) { - // if caller specified an image ID, remove that, but don't apply default filters - m1.remove(IMAGE_ID); - // TODO use key - m1.putStringKey("anyOwner", true); - } - ComputeService computeServiceLessRestrictive = getConfig(COMPUTE_SERVICE_REGISTRY).findComputeService(m1, true); - Set<? extends Image> imgs = computeServiceLessRestrictive.listImages(); - LOG.info(""+imgs.size()+" available images at "+this); - for (Image img: imgs) { - LOG.info(" Image: "+img); - } - - Set<? extends Hardware> profiles = computeServiceLessRestrictive.listHardwareProfiles(); - LOG.info(""+profiles.size()+" available profiles at "+this); - for (Hardware profile: profiles) { - LOG.info(" Profile: "+profile); - } - - Set<? extends org.jclouds.domain.Location> assignableLocations = computeServiceLessRestrictive.listAssignableLocations(); - LOG.info(""+assignableLocations.size()+" available locations at "+this); - for (org.jclouds.domain.Location assignableLocation: assignableLocations) { - LOG.info(" Location: "+assignableLocation); - } - } - - protected SshMachineLocation createTemporarySshMachineLocation(HostAndPort hostAndPort, LoginCredentials creds, ConfigBag config) { - Optional<String> initialPassword = creds.getOptionalPassword(); - Optional<String> initialPrivateKey = creds.getOptionalPrivateKey(); - String initialUser = creds.getUser(); - - Map<String,Object> sshProps = Maps.newLinkedHashMap(config.getAllConfig()); - sshProps.put("user", initialUser); - sshProps.put("address", hostAndPort.getHostText()); - sshProps.put("port", hostAndPort.getPort()); - sshProps.put(AbstractLocation.TEMPORARY_LOCATION.getName(), true); - if (initialPassword.isPresent()) sshProps.put("password", initialPassword.get()); - if (initialPrivateKey.isPresent()) sshProps.put("privateKeyData", initialPrivateKey.get()); - if (initialPrivateKey.isPresent()) sshProps.put("privateKeyData", initialPrivateKey.get()); - - if (isManaged()) { - return getManagementContext().getLocationManager().createLocation(sshProps, SshMachineLocation.class); - } else { - return new SshMachineLocation(sshProps); - } - } - - /** - * Create the user immediately - executing ssh commands as required. - */ - protected LoginCredentials createUser(ComputeService computeService, NodeMetadata node, Optional<HostAndPort> hostAndPortOverride, ConfigBag config) { - Image image = (node.getImageId() != null) ? computeService.getImage(node.getImageId()) : null; - UserCreation userCreation = createUserStatements(image, config); - - if (!userCreation.statements.isEmpty()) { - // If unsure of OS family, default to unix for rendering statements. - org.jclouds.scriptbuilder.domain.OsFamily scriptOsFamily; - if (isWindows(node, config)) { - scriptOsFamily = org.jclouds.scriptbuilder.domain.OsFamily.WINDOWS; - } else { - scriptOsFamily = org.jclouds.scriptbuilder.domain.OsFamily.UNIX; - } - - boolean windows = isWindows(node, config); - - if (windows) { - LOG.warn("Unable to execute statements on WinRM in JcloudsLocation; skipping for "+node+": "+userCreation.statements); - - } else { - List<String> commands = Lists.newArrayList(); - for (Statement statement : userCreation.statements) { - InitAdminAccess initAdminAccess = new InitAdminAccess(new AdminAccessConfiguration.Default()); - initAdminAccess.visit(statement); - commands.add(statement.render(scriptOsFamily)); - } - - LoginCredentials initialCredentials = node.getCredentials(); - Optional<String> initialPassword = initialCredentials.getOptionalPassword(); - Optional<String> initialPrivateKey = initialCredentials.getOptionalPrivateKey(); - String initialUser = initialCredentials.getUser(); - String address = hostAndPortOverride.isPresent() ? hostAndPortOverride.get().getHostText() : JcloudsUtil.getFirstReachableAddress(computeService.getContext(), node); - int port = hostAndPortOverride.isPresent() ? hostAndPortOverri
<TRUNCATED>
