http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java deleted file mode 100644 index e5696c2..0000000 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/AbstractPortableTemplateBuilder.java +++ /dev/null @@ -1,527 +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.templates; - -import java.util.ArrayList; -import java.util.List; - -import org.jclouds.compute.domain.Hardware; -import org.jclouds.compute.domain.Image; -import org.jclouds.compute.domain.OsFamily; -import org.jclouds.compute.domain.Template; -import org.jclouds.compute.domain.TemplateBuilder; -import org.jclouds.compute.options.TemplateOptions; - -import com.google.common.base.Function; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableList; - -public abstract class AbstractPortableTemplateBuilder<T extends AbstractPortableTemplateBuilder<?>> implements TemplateBuilder { - - /** list of commands supplied by user, excluding options */ - protected List<Function<TemplateBuilder,TemplateBuilder>> commands = new ArrayList<Function<TemplateBuilder,TemplateBuilder>>(); - - private Hardware hardware; - private Image image; - private Template template; - private String locationId; - private String imageId; - private String hardwareId; - private OsFamily os; - private String osNameRegex; - private String osDescriptionRegex; - private String osVersionRegex; - private String osArchitectureRegex; - private String hypervisorRegex; - private Boolean is64bit; - private String imageNameRegex; - private String imageDescriptionRegex; - private String imageVersionRegex; - private Double minCores; - private Integer minRam; - private Double minDisk; - private Predicate<Image> imageCondition; - private Function<Iterable<? extends Image>, Image> imageChooserFunction; - /** this is the last options instance set by a call to options(TemplateOptions) */ - private TemplateOptions options; - /** these are extra options that we want _added_, in order, on top of the last options set */ - private List<TemplateOptions> additionalOptions = new ArrayList<TemplateOptions>(); - - @Override - public T any() { - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.any(); }}); - return (T)this; - } - - @Override - public T fromHardware(final Hardware hardware) { - this.hardware = hardware; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.fromHardware(hardware); }}); - return (T)this; - } - - public Hardware getHardware() { - return hardware; - } - - @Override - public T fromImage(final Image image) { - this.image = image; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.fromImage(image); }}); - return (T)this; - } - - public Image getImage() { - return image; - } - - @Override - public T fromTemplate(final Template template) { - this.template = template; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.fromTemplate(template); }}); - return (T)this; - } - - public Template getTemplate() { - return template; - } - - @Override - public T smallest() { - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.smallest(); }}); - return (T)this; - } - - @Override - public T fastest() { - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.fastest(); }}); - return (T)this; - } - - @Override - public T biggest() { - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.biggest(); }}); - return (T)this; - } - - @Override - public T locationId(final String locationId) { - this.locationId = locationId; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.locationId(locationId); }}); - return (T)this; - } - - public String getLocationId() { - return locationId; - } - - @Override - public T imageId(final String imageId) { - this.imageId = imageId; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.imageId(imageId); }}); - return (T)this; - } - - public String getImageId() { - return imageId; - } - - @Override - public T hardwareId(final String hardwareId) { - this.hardwareId = hardwareId; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.hardwareId(hardwareId); }}); - return (T)this; - } - - public String getHardwareId() { - return hardwareId; - } - - @Override - public T osNameMatches(final String osNameRegex) { - this.osNameRegex = osNameRegex; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.osNameMatches(osNameRegex); }}); - return (T)this; - } - - public String getOsNameMatchesRegex() { - return osNameRegex; - } - - @Override - public T osDescriptionMatches(final String osDescriptionRegex) { - this.osDescriptionRegex = osDescriptionRegex; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.osDescriptionMatches(osDescriptionRegex); }}); - return (T)this; - } - - public String getOsDescriptionMatchesRegex() { - return osDescriptionRegex; - } - - @Override - public T osVersionMatches(final String osVersionRegex) { - this.osVersionRegex = osVersionRegex; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.osVersionMatches(osVersionRegex); }}); - return (T)this; - } - - public String getOsVersionMatchesRegex() { - return osVersionRegex; - } - - @Override - public T osArchMatches(final String osArchitectureRegex) { - this.osArchitectureRegex = osArchitectureRegex; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.osArchMatches(osArchitectureRegex); }}); - return (T)this; - } - - public String getOsArchitectureMatchesRegex() { - return osArchitectureRegex; - } - - @Override - public T os64Bit(final boolean is64bit) { - this.is64bit = is64bit; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.os64Bit(is64bit); }}); - return (T)this; - } - - public Boolean getIs64bit() { - return is64bit; - } - - @Override - public T osFamily(final OsFamily os) { - this.os = os; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.osFamily(os); }}); - return (T)this; - } - - public OsFamily getOsFamily() { - return os; - } - - @Override - public T hypervisorMatches(final String hypervisorRegex) { - this.hypervisorRegex = hypervisorRegex; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.hypervisorMatches(hypervisorRegex); }}); - return (T)this; - } - - public String getHypervisorMatchesRegex() { - return hypervisorRegex; - } - - @Override - public T imageNameMatches(final String imageNameRegex) { - this.imageNameRegex = imageNameRegex; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.imageNameMatches(imageNameRegex); }}); - return (T)this; - } - - public String getImageNameMatchesRegex() { - return imageNameRegex; - } - - @Override - public T imageDescriptionMatches(final String imageDescriptionRegex) { - this.imageDescriptionRegex = imageDescriptionRegex; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.imageDescriptionMatches(imageDescriptionRegex); }}); - return (T)this; - } - - public String getImageDescriptionMatchesRegex() { - return imageDescriptionRegex; - } - - @Override - public T imageVersionMatches(final String imageVersionRegex) { - this.imageVersionRegex = imageVersionRegex; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.imageVersionMatches(imageVersionRegex); }}); - return (T)this; - } - - public String getImageVersionMatchesRegex() { - return imageVersionRegex; - } - - @Override - public T imageMatches(final Predicate<Image> condition) { - this.imageCondition = condition; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.imageMatches(condition); }}); - return (T)this; - } - - public Predicate<Image> getImageMatchesCondition() { - return imageCondition; - } - - @Override - public T minCores(final double minCores) { - this.minCores = minCores; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.minCores(minCores); }}); - return (T)this; - } - - public Double getMinCores() { - return minCores; - } - - @Override - public T minRam(final int megabytes) { - this.minRam = megabytes; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.minRam(megabytes); }}); - return (T)this; - } - - /** megabytes */ - public Integer getMinRam() { - return minRam; - } - - @Override - public T minDisk(final double gigabytes) { - this.minDisk = gigabytes; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.minDisk(gigabytes); }}); - return (T)this; - } - - /** megabytes */ - public Double getMinDisk() { - return minDisk; - } - - public T imageChooser(final Function<Iterable<? extends Image>, Image> imageChooserFunction) { - this.imageChooserFunction = imageChooserFunction; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.imageChooser(imageChooserFunction); }}); - return (T)this; - } - - public Function<Iterable<? extends Image>, Image> imageChooser() { - return imageChooserFunction; - } - - /** clears everything set in this template, including any default from the compute service */ - // not sure this is that useful, as the default is only applied if there are no changes - public T blank() { - hardware = null; - image = null; - template = null; - hypervisorRegex = null; - os = null; - locationId = null; - imageId = null; - hardwareId = null; - osNameRegex = null; - osDescriptionRegex = null; - osVersionRegex = null; - osArchitectureRegex = null; - is64bit = null; - imageNameRegex = null; - imageDescriptionRegex = null; - imageVersionRegex = null; - imageCondition = null; - minCores = null; - minRam = null; - options = null; - additionalOptions.clear(); - - // clear all fields, and commands - commands.clear(); - // then add a command to clear osName + Version + 64bit - osFamily(null); - osVersionMatches(null); - // no way to turn off 64-bitness, but it won't usually be turned on -// os64bit(null); - // set _something_ to prevent the default from applying - minRam(1); - - return (T)this; - } - - /** true if the templateBuilder spec is blank (ignoring customization options e.g. tags for the resulting instance) */ - public boolean isBlank() { - if (commands.isEmpty()) return true; - //also "blank" if we've blanked it - if (commands.size()==1 && (minRam!=null && minRam==1)) return true; - return false; - } - - @Override - public T options(final TemplateOptions options) { - this.options = options; - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.options(options); }}); - return (T)this; - } - - /** sets customization options; may be null if not set. use addOptions(new TemplateOptions()) to set new ones. */ - public TemplateOptions getOptions() { - return options; - } - - /** adds customization options; if options have already been set, this will additively set selected options - * (but not all, see addTemplateOptions for more info) - */ - public T addOptions(final TemplateOptions options) { - this.additionalOptions.add(options); - commands.add(new Function<TemplateBuilder,TemplateBuilder>() { - public TemplateBuilder apply(TemplateBuilder b) { return b.options(options); }}); - return (T)this; - } - - public List<TemplateOptions> getAdditionalOptions() { - return ImmutableList.copyOf(additionalOptions); - } - - @Override - public int hashCode() { - return Objects.hashCode( - hypervisorRegex, - os, - locationId, - hardwareId, - imageId, - imageDescriptionRegex, - imageNameRegex, - imageVersionRegex, - // might not be implement hashCode, so ignore -// imageCondition, -// imageChooserFunction, - is64bit, - locationId, - osArchitectureRegex, - osDescriptionRegex, - osNameRegex, - osVersionRegex, - minCores, - minRam, - minDisk, - options, - additionalOptions, - // might not implement hashCode, so ignore -// template, - 0); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - AbstractPortableTemplateBuilder other = (AbstractPortableTemplateBuilder) obj; - if (!Objects.equal(additionalOptions, other.additionalOptions)) return false; - if (!Objects.equal(commands, other.commands)) return false; - if (!Objects.equal(locationId, other.locationId)) return false; - if (!Objects.equal(hardware, other.hardware)) return false; - if (!Objects.equal(hardwareId, other.hardwareId)) return false; - if (!Objects.equal(image, other.image)) return false; - if (!Objects.equal(imageId, other.imageId)) return false; - if (!Objects.equal(imageDescriptionRegex, other.imageDescriptionRegex)) return false; - if (!Objects.equal(imageNameRegex, other.imageNameRegex)) return false; - if (!Objects.equal(imageVersionRegex, other.imageVersionRegex)) return false; - if (!Objects.equal(imageCondition, other.imageCondition)) return false; - if (!Objects.equal(imageChooserFunction, other.imageChooserFunction)) return false; - if (!Objects.equal(os, other.os)) return false; - if (!Objects.equal(osArchitectureRegex, other.osArchitectureRegex)) return false; - if (!Objects.equal(osDescriptionRegex, other.osDescriptionRegex)) return false; - if (!Objects.equal(osNameRegex, other.osNameRegex)) return false; - if (!Objects.equal(osVersionRegex, other.osVersionRegex)) return false; - if (!Objects.equal(is64bit, other.is64bit)) return false; - if (!Objects.equal(hypervisorRegex, other.hypervisorRegex)) return false; - if (!Objects.equal(minCores, other.minCores)) return false; - if (!Objects.equal(minRam, other.minRam)) return false; - if (!Objects.equal(minDisk, other.minDisk)) return false; - if (!Objects.equal(options, other.options)) return false; - if (!Objects.equal(template, other.template)) return false; - return true; - } - - @Override - public String toString() { - return getClass().getSimpleName()+"["+makeNonTrivialArgumentsString()+"]"; - } - - protected String makeNonTrivialArgumentsString() { - String s = - (hardware != null ? "hardware=" + hardware + ", " : "") - + (image != null ? "image=" + image + ", " : "") - + (template != null ? "template=" + template + ", " : "") - + (hypervisorRegex != null ? "hypervisorRegex=" - + hypervisorRegex + ", " : "") - + (os != null ? "os=" + os + ", " : "") - + (locationId != null ? "locationId=" + locationId + ", " : "") - + (imageId != null ? "imageId=" + imageId + ", " : "") - + (hardwareId != null ? "hardwareId=" + hardwareId + ", " : "") - + (osNameRegex != null ? "osNameRegex=" + osNameRegex + ", " - : "") - + (osDescriptionRegex != null ? "osDescriptionRegex=" - + osDescriptionRegex + ", " : "") - + (osVersionRegex != null ? "osVersionRegex=" + osVersionRegex - + ", " : "") - + (osArchitectureRegex != null ? "osArchictectureRegex=" - + osArchitectureRegex + ", " : "") - + (is64bit != null ? "is64bit=" + is64bit + ", " : "") - + (imageNameRegex != null ? "imageNameRegex=" + imageNameRegex - + ", " : "") - + (imageDescriptionRegex != null ? "imageDescriptionRegex=" - + imageDescriptionRegex + ", " : "") - + (imageVersionRegex != null ? "imageVersionRegex=" - + imageVersionRegex + ", " : "") - + (imageCondition != null ? "imageCondition=" + imageCondition - + ", " : "") - + (imageChooserFunction != null ? "imageChooserFunction=" + imageChooserFunction - + ", " : "") - + (minCores != null ? "minCores=" + minCores + ", " : "") - + (minRam != null ? "minRam=" + minRam + ", " : "") - + (minDisk != null ? "minDisk=" + minDisk + ", " : ""); - if (s.endsWith(", ")) s = s.substring(0, s.length()-2); - return s; - } - - -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java deleted file mode 100644 index 3baa003..0000000 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/templates/PortableTemplateBuilder.java +++ /dev/null @@ -1,145 +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.templates; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -import org.jclouds.compute.ComputeService; -import org.jclouds.compute.domain.Template; -import org.jclouds.compute.domain.TemplateBuilder; -import org.jclouds.compute.domain.TemplateBuilderSpec; -import org.jclouds.compute.options.TemplateOptions; - -import com.google.common.base.Function; -import com.google.common.collect.ImmutableList; -import com.google.common.primitives.Ints; - - -public class PortableTemplateBuilder<T extends PortableTemplateBuilder<?>> extends AbstractPortableTemplateBuilder<T> { - - ComputeService svc; - List<TemplateOptions> additionalOptionalOptions = new ArrayList<TemplateOptions>(); - - @Override - public synchronized Template build() { - if (svc!=null) return newJcloudsTemplate(svc); - throw new IllegalStateException("Cannot build a portable template until a compute service is attached"); - } - - public synchronized ComputeService attachComputeService(ComputeService svc) { - ComputeService old = this.svc; - this.svc = svc; - return old; - } - - public TemplateBuilder newJcloudsTemplateBuilder(ComputeService svc) { - TemplateBuilder tb = svc.templateBuilder(); - for (Function<TemplateBuilder,TemplateBuilder> c: commands) { - tb = c.apply(tb); - } - - tb.options(computeAggregatedOptions(true)); - - return tb; - } - - public Template newJcloudsTemplate(ComputeService svc) { - return newJcloudsTemplateBuilder(svc).build(); - } - - /** Adds template options which are used for building, but not for matching/filtering. - * (eg tags added here will be set on any machine created by this template, - * but will not be required when matching this template to existing machines) */ - @SuppressWarnings("unchecked") - public T addOptionalOptions(TemplateOptions options) { - additionalOptionalOptions.add(options); - return (T)this; - } - - protected TemplateOptions computeAggregatedOptions(boolean includeOptional) { - TemplateOptions result; - if (getOptions()!=null) result = getOptions().clone(); - else result = new TemplateOptions(); - if (includeOptional) - for (TemplateOptions moreOptions: getAdditionalOptionalOptions()) result = addTemplateOptions(result, moreOptions); - for (TemplateOptions moreOptions: getAdditionalOptions()) result = addTemplateOptions(result, moreOptions); - return result; - } - - public List<TemplateOptions> getAdditionalOptionalOptions() { - return ImmutableList.copyOf(additionalOptionalOptions); - } - - /** like TemplateOptions.copyTo but additive wrt arrays, collections, and maps, - * putting moreOptions in on top of / at the end of options. - * currently applies to inboundPorts, tags, and userMetadata. */ - public static TemplateOptions addTemplateOptions(TemplateOptions options, TemplateOptions moreOptions) { - TemplateOptions result = options.clone(); - moreOptions.copyTo(result); - - Set<String> tags = new LinkedHashSet<String>(options.getTags()); - tags.addAll(moreOptions.getTags()); - result.tags(tags); - - Map<String,String> userMetadata = new LinkedHashMap<String,String>(options.getUserMetadata()); - userMetadata.putAll(moreOptions.getUserMetadata()); - result.userMetadata(userMetadata); - - Set<Integer> inboundPorts = new TreeSet<Integer>(); - for (int port: options.getInboundPorts()) inboundPorts.add(port); - for (int port: moreOptions.getInboundPorts()) inboundPorts.add(port); - int[] inboundPortsArray = new int[inboundPorts.size()]; - int i=0; - for (Iterator<Integer> portI=inboundPorts.iterator(); portI.hasNext();) { - inboundPortsArray[i++] = portI.next(); - } - result.inboundPorts(inboundPortsArray); - - return result; - } - - protected String makeNonTrivialArgumentsString() { - String s = super.makeNonTrivialArgumentsString(); - TemplateOptions aggr = computeAggregatedOptions(false); - if (aggr.getInboundPorts().length>0) s = "ports="+Ints.asList(aggr.getInboundPorts())+(s!=null && s.length()>0 ? ", "+s : ""); - if (!aggr.getUserMetadata().isEmpty()) s = "metadata="+aggr.getUserMetadata()+(s!=null && s.length()>0 ? ", "+s : ""); - if (!aggr.getTags().isEmpty()) s = "tags="+aggr.getTags()+(s!=null && s.length()>0 ? ", "+s : ""); - return s; - } - - @Override - public TemplateBuilder from(TemplateBuilderSpec spec) { - TemplateOptions options = new TemplateOptions(); - addOptionalOptions(options); - TemplateBuilder result = spec.copyTo(this, options); - return result; - } - - @Override - public TemplateBuilder from(String spec) { - return from(TemplateBuilderSpec.parse(spec)); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtension.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtension.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtension.java deleted file mode 100644 index 20bfb4c..0000000 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/zone/AwsAvailabilityZoneExtension.java +++ /dev/null @@ -1,74 +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.zone; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.util.List; -import java.util.Set; - -import org.apache.brooklyn.api.management.ManagementContext; -import org.jclouds.aws.ec2.AWSEC2Api; -import org.jclouds.ec2.domain.AvailabilityZoneInfo; - -import brooklyn.location.Location; -import brooklyn.location.cloud.AbstractAvailabilityZoneExtension; -import brooklyn.location.cloud.AvailabilityZoneExtension; -import brooklyn.location.jclouds.JcloudsLocation; - -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; - -public class AwsAvailabilityZoneExtension extends AbstractAvailabilityZoneExtension implements AvailabilityZoneExtension { - - private final JcloudsLocation loc; - - public AwsAvailabilityZoneExtension(ManagementContext managementContext, JcloudsLocation loc) { - super(managementContext); - this.loc = checkNotNull(loc, "loc"); - checkArgument(loc.getProvider().equals("aws-ec2"), "provider not aws-ec2 (%s)", loc.getProvider()); - } - - @Override - protected List<Location> doGetAllSubLocations() { - List<Location> result = Lists.newArrayList(); - Set<AvailabilityZoneInfo> zones = getAvailabilityZones(); - for (AvailabilityZoneInfo zone : zones) { - result.add(newSubLocation(loc, zone)); - } - return result; - } - - @Override - protected boolean isNameMatch(Location loc, Predicate<? super String> namePredicate) { - return namePredicate.apply(((JcloudsLocation)loc).getRegion()); - } - - protected Set<AvailabilityZoneInfo> getAvailabilityZones() { - String regionName = loc.getRegion(); - AWSEC2Api ec2Client = loc.getComputeService().getContext().unwrapApi(AWSEC2Api.class); - return ec2Client.getAvailabilityZoneAndRegionApi().get().describeAvailabilityZonesInRegion(regionName); - } - - protected JcloudsLocation newSubLocation(Location parent, AvailabilityZoneInfo zone) { - return loc.newSubLocation(ImmutableMap.of(JcloudsLocation.CLOUD_REGION_ID, zone.getZone())); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java b/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java index f0d1140..bd02f60 100644 --- a/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java +++ b/locations/jclouds/src/main/java/brooklyn/policy/os/AdvertiseWinrmLoginPolicy.java @@ -28,8 +28,8 @@ import org.slf4j.LoggerFactory; import brooklyn.entity.basic.AbstractEntity; import brooklyn.event.basic.Sensors; -import brooklyn.location.Location; -import brooklyn.location.basic.WinRmMachineLocation; +import org.apache.brooklyn.location.Location; +import org.apache.brooklyn.location.basic.WinRmMachineLocation; import brooklyn.policy.basic.AbstractPolicy; import com.google.common.annotations.Beta; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java index 2459187..4e2f68c 100644 --- a/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java +++ b/locations/jclouds/src/main/java/brooklyn/policy/os/CreateUserPolicy.java @@ -37,8 +37,8 @@ import brooklyn.entity.basic.AbstractEntity; import brooklyn.entity.basic.ConfigKeys; import brooklyn.entity.basic.EntityInternal; import brooklyn.event.basic.Sensors; -import brooklyn.location.Location; -import brooklyn.location.basic.SshMachineLocation; +import org.apache.brooklyn.location.Location; +import org.apache.brooklyn.location.basic.SshMachineLocation; import brooklyn.policy.basic.AbstractPolicy; import brooklyn.util.flags.SetFromFlag; import brooklyn.util.internal.ssh.SshTool; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsSubnetSshMachineLocation.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsSubnetSshMachineLocation.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsSubnetSshMachineLocation.java new file mode 100644 index 0000000..c21810d --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/AbstractJcloudsSubnetSshMachineLocation.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import java.util.Map; + +import org.jclouds.compute.domain.NodeMetadata; + +import org.apache.brooklyn.location.basic.SupportsPortForwarding.RequiresPortForwarding; + +public abstract class AbstractJcloudsSubnetSshMachineLocation extends JcloudsSshMachineLocation implements RequiresPortForwarding { + + public AbstractJcloudsSubnetSshMachineLocation() { + } + + /** @deprecated since 0.6.0 use no-arg constructor, as per parent */ + @Deprecated + public AbstractJcloudsSubnetSshMachineLocation(Map flags, JcloudsLocation parent, NodeMetadata node) { + super(flags, parent, node); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java new file mode 100644 index 0000000..e73d5f7 --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.domain.TemplateBuilder; +import org.jclouds.compute.options.TemplateOptions; + +import com.google.common.annotations.Beta; + + +/** + * A default no-op implementation, which can be extended to override the appropriate methods. + * + * Sub-classing will give the user some protection against future API changes - note that + * {@link JcloudsLocationCustomizer} is marked {@link Beta}. + * + * @author aled + */ +public class BasicJcloudsLocationCustomizer implements JcloudsLocationCustomizer { + + @Override + public void customize(JcloudsLocation location, ComputeService computeService, TemplateBuilder templateBuilder) { + // no-op + } + + @Override + public void customize(JcloudsLocation location, ComputeService computeService, Template template) { + // no-op + } + + @Override + public void customize(JcloudsLocation location, ComputeService computeService, TemplateOptions templateOptions) { + // no-op + } + + @Override + public void customize(JcloudsLocation location, ComputeService computeService, JcloudsMachineLocation machine) { + if (machine instanceof JcloudsSshMachineLocation) { + customize(location, computeService, (JcloudsSshMachineLocation)machine); + } else { + // no-op + } + } + + @Override + public void preRelease(JcloudsMachineLocation machine) { + if (machine instanceof JcloudsSshMachineLocation) { + preRelease((JcloudsSshMachineLocation)machine); + } else { + // no-op + } + } + + @Override + public void postRelease(JcloudsMachineLocation machine) { + if (machine instanceof JcloudsSshMachineLocation) { + postRelease((JcloudsSshMachineLocation)machine); + } else { + // no-op + } + } + + @Override + @Deprecated + public void customize(JcloudsLocation location, ComputeService computeService, JcloudsSshMachineLocation machine) { + // no-op + } + + @Override + @Deprecated + public void preRelease(JcloudsSshMachineLocation machine) { + // no-op + } + + @Override + @Deprecated + public void postRelease(JcloudsSshMachineLocation machine) { + // no-op + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynImageChooser.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynImageChooser.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynImageChooser.java new file mode 100644 index 0000000..62cf003 --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynImageChooser.java @@ -0,0 +1,367 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.OperatingSystem; +import org.jclouds.compute.domain.OsFamily; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.util.collections.MutableList; + +import com.google.common.annotations.Beta; +import com.google.common.base.Function; +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.Ordering; +import com.google.common.math.DoubleMath; + +@Beta +/** NB: subclasses must implement {@link #clone()} */ +public class BrooklynImageChooser implements Cloneable { + + private static final Logger log = LoggerFactory.getLogger(BrooklynImageChooser.class); + + protected ComputeService computeService; + protected String cloudProviderName; + + protected static int compare(double left, double right) { + return DoubleMath.fuzzyCompare(left, right, 0.00000001); + } + + protected static boolean imageNameContains(Image img, String pattern) { + if (img.getName()==null) return false; + return img.getName().contains(pattern); + } + + protected static boolean imageNameContainsCaseInsensitive(Image img, String pattern) { + if (img.getName()==null) return false; + return img.getName().toLowerCase().contains(pattern.toLowerCase()); + } + + protected static boolean imageNameContainsWordCaseInsensitive(Image img, String pattern) { + if (img.getName()==null) return false; + return img.getName().toLowerCase().matches("(.*[^a-z])?"+pattern.toLowerCase()+"([^a-z].*)?"); + } + + public double punishmentForOldOsVersions(Image img, OsFamily family, double minVersion) { + OperatingSystem os = img.getOperatingSystem(); + if (os!=null && family.equals(os.getFamily())) { + String v = os.getVersion(); + if (v!=null) { + try { + double vd = Double.parseDouble(v); + // punish older versions, with a -log function (so 0.5 version behind is -log(1.5)=-0.5 and 2 versions behind is -log(3)=-1.2 + if (vd < minVersion) return -Math.log(1+(minVersion - vd)); + } catch (Exception e) { + /* ignore unparseable versions */ + } + } + } + return 0; + } + + public List<String> blackListedImageIds() { + return Arrays.asList( + // bad natty image - causes 403 on attempts to apt-get; https://bugs.launchpad.net/ubuntu/+bug/987182 + "us-east-1/ami-1cb30875", + // wrong login user advertised, causes "Error Invalid packet: indicated length 1349281121 too large" + // from sshj due to message coming back "Plea"(se log in as another user), according to https://github.com/jclouds/legacy-jclouds/issues/748 + "us-east-1/ami-08faa660" + ); + } + + public List<String> whilelistedImageIds() { + return Arrays.asList( + // these are the ones we recommend in brooklyn.properties, but now autodetection should be more reliable +// "us-east-1/ami-d0f89fb9", +// "us-west-1/ami-fe002cbb", +// "us-west-2/ami-70f96e40", +// "eu-west-1/ami-ce7b6fba", +// "sa-east-1/ami-a3da00be", +// "ap-southeast-1/ami-64084736", +// "ap-southeast-2/ami-04ea7a3e", +// "ap-northeast-1/ami-fe6ceeff" + ); + } + + public double score(Image img) { + double score = 0; + + if (blackListedImageIds().contains(img.getId())) + score -= 50; + + if (whilelistedImageIds().contains(img.getId())) + // NB: this should be less than deprecated punishment to catch deprecation of whitelisted items + score += 20; + + score += punishmentForDeprecation(img); + + + // prefer these guys, in stock brooklyn provisioning + score += punishmentForOldOsVersions(img, OsFamily.UBUNTU, 11); + score += punishmentForOldOsVersions(img, OsFamily.CENTOS, 6); + + OperatingSystem os = img.getOperatingSystem(); + if (os!=null) { + if (os.getFamily()!=null) { + // preference for these open, popular OS (but only wrt versions above) + if (os.getFamily().equals(OsFamily.CENTOS)) score += 2; + else if (os.getFamily().equals(OsFamily.UBUNTU)) { + score += 2; + + // prefer these LTS releases slightly above others (including above CentOS) + // (but note in AWS Virginia, at least, version is empty for the 14.04 images for some reason, as of Aug 2014) + if ("14.04".equals(os.getVersion())) score += 0.2; + else if ("12.04".equals(os.getVersion())) score += 0.1; + + // NB some 13.10 images take 20m+ before they are sshable on AWS + // with "vesafb: module verification error" showing in the AWS system log + } + + // slight preference for these + else if (os.getFamily().equals(OsFamily.RHEL)) score += 1; + else if (os.getFamily().equals(OsFamily.AMZN_LINUX)) score += 1; + else if (os.getFamily().equals(OsFamily.DEBIAN)) score += 1; + + // prefer to take our chances with unknown / unlabelled linux than something explicitly windows + else if (os.getFamily().equals(OsFamily.WINDOWS)) score -= 1; + + if ("softlayer".equals(cloudProviderName)) { + // on softlayer, prefer images where family is part of the image id + // (this is the only way to identiy official images; but in other clouds + // it can cause not-so-good images to get selected!) + if (img.getId().toLowerCase().indexOf(os.getFamily().toString().toLowerCase()) >= 0) + score += 0.5; + } + } + // prefer 64-bit + if (os.is64Bit()) score += 0.5; + } + + // TODO prefer known providerIds + + if (log.isTraceEnabled()) + log.trace("initial score "+score+" for "+img); + + return score; + } + + protected double punishmentForDeprecation(Image img) { + // google deprecation strategy + // userMetadata={deprecatedState=DEPRECATED}} + String deprecated = img.getUserMetadata().get("deprecatedState"); + if (deprecated!=null) { + if ("deprecated".equalsIgnoreCase(deprecated)) + return -30; + log.warn("Unrecognised 'deprecatedState' value '"+deprecated+"' when scoring "+img+"; ignoring that metadata"); + } + + // common strategies + if (imageNameContainsWordCaseInsensitive(img, "deprecated")) return -30; + if (imageNameContainsWordCaseInsensitive(img, "alpha")) return -10; + if (imageNameContainsWordCaseInsensitive(img, "beta")) return -5; + if (imageNameContainsWordCaseInsensitive(img, "testing")) return -5; + if (imageNameContainsWordCaseInsensitive(img, "rc")) return -3; + + // no indication this is deprecated + return 0; + } + + public BrooklynImageChooser clone() { + return new BrooklynImageChooser(); + } + + protected void use(ComputeService service) { + if (this.computeService!=null && !this.computeService.equals(service)) + throw new IllegalStateException("ImageChooser must be cloned to set a compute service"); + this.computeService = service; + if (computeService!=null) { + cloudProviderName = computeService.getContext().unwrap().getId(); + } + } + + public BrooklynImageChooser cloneFor(ComputeService service) { + BrooklynImageChooser result = clone(); + result.use(service); + return result; + } + + public static class OrderingScoredWithoutDefaults extends Ordering<Image> implements ComputeServiceAwareChooser<OrderingScoredWithoutDefaults> { + private BrooklynImageChooser chooser; + public OrderingScoredWithoutDefaults(BrooklynImageChooser chooser) { + this.chooser = chooser; + } + public int compare(Image left, Image right) { + return BrooklynImageChooser.compare(chooser.score(left), chooser.score(right)); + } + @Override + public OrderingScoredWithoutDefaults cloneFor(ComputeService service) { + return new OrderingScoredWithoutDefaults(chooser.cloneFor(service)); + } + } + + public Ordering<Image> orderingScoredWithoutDefaults() { + return new OrderingScoredWithoutDefaults(this); + } + + /** @deprecated since 0.7.0 kept in case persisted */ + @Deprecated + public Ordering<Image> orderingScoredWithoutDefaultsDeprecated() { + return new Ordering<Image>() { + @Override + public int compare(Image left, Image right) { + return BrooklynImageChooser.compare(score(left), score(right)); + } + }; + } + + public static class OrderingWithDefaults extends Ordering<Image> implements ComputeServiceAwareChooser<OrderingWithDefaults> { + Ordering<Image> primaryOrdering; + public OrderingWithDefaults(final Ordering<Image> primaryOrdering) { + this.primaryOrdering = primaryOrdering; + } + @Override + public int compare(Image left, Image right) { + return ComparisonChain.start() + .compare(left, right, primaryOrdering) + // fall back to default strategy otherwise, except preferring *non*-null values + // TODO use AlphaNum string comparator + .compare(left.getName(), right.getName(), Ordering.<String> natural().nullsFirst()) + .compare(left.getVersion(), right.getVersion(), Ordering.<String> natural().nullsFirst()) + .compare(left.getDescription(), right.getDescription(), Ordering.<String> natural().nullsFirst()) + .compare(left.getOperatingSystem().getName(), right.getOperatingSystem().getName(), Ordering.<String> natural().nullsFirst()) + .compare(left.getOperatingSystem().getVersion(), right.getOperatingSystem().getVersion(), Ordering.<String> natural().nullsFirst()) + .compare(left.getOperatingSystem().getDescription(), right.getOperatingSystem().getDescription(), Ordering.<String> natural().nullsFirst()) + .compare(left.getOperatingSystem().getArch(), right.getOperatingSystem().getArch(), Ordering.<String> natural().nullsFirst()).result(); + } + @Override + public OrderingWithDefaults cloneFor(ComputeService service) { + if (primaryOrdering instanceof ComputeServiceAwareChooser) { + return new OrderingWithDefaults( BrooklynImageChooser.cloneFor(primaryOrdering, service) ); + } + return this; + } + } + + public static Ordering<Image> orderingWithDefaults(final Ordering<Image> primaryOrdering) { + return new OrderingWithDefaults(primaryOrdering); + } + + /** @deprecated since 0.7.0 kept in case persisted */ + @Deprecated + public static Ordering<Image> orderingWithDefaultsDeprecated(final Ordering<Image> primaryOrdering) { + return new Ordering<Image>() { + @Override + public int compare(Image left, Image right) { + return ComparisonChain.start() + .compare(left, right, primaryOrdering) + // fall back to default strategy otherwise, except preferring *non*-null values + // TODO use AlphaNum string comparator + .compare(left.getName(), right.getName(), Ordering.<String> natural().nullsFirst()) + .compare(left.getVersion(), right.getVersion(), Ordering.<String> natural().nullsFirst()) + .compare(left.getDescription(), right.getDescription(), Ordering.<String> natural().nullsFirst()) + .compare(left.getOperatingSystem().getName(), right.getOperatingSystem().getName(), Ordering.<String> natural().nullsFirst()) + .compare(left.getOperatingSystem().getVersion(), right.getOperatingSystem().getVersion(), Ordering.<String> natural().nullsFirst()) + .compare(left.getOperatingSystem().getDescription(), right.getOperatingSystem().getDescription(), Ordering.<String> natural().nullsFirst()) + .compare(left.getOperatingSystem().getArch(), right.getOperatingSystem().getArch(), Ordering.<String> natural().nullsFirst()).result(); + } + }; + } + + public static class ImageChooserFromOrdering implements Function<Iterable<? extends Image>, Image>, ComputeServiceAwareChooser<ImageChooserFromOrdering> { + final Ordering<Image> ordering; + public ImageChooserFromOrdering(final Ordering<Image> ordering) { this.ordering = ordering; } + @Override + public Image apply(Iterable<? extends Image> input) { + List<? extends Image> maxImages = multiMax(ordering, input); + return maxImages.get(maxImages.size() - 1); + } + @Override + public ImageChooserFromOrdering cloneFor(ComputeService service) { + if (ordering instanceof ComputeServiceAwareChooser) { + return new ImageChooserFromOrdering( BrooklynImageChooser.cloneFor(ordering, service) ); + } + return this; + } + } + + public static Function<Iterable<? extends Image>, Image> imageChooserFromOrdering(final Ordering<Image> ordering) { + return new ImageChooserFromOrdering(ordering); + } + + /** @deprecated since 0.7.0 kept in case persisted */ + @Deprecated + public static Function<Iterable<? extends Image>, Image> imageChooserFromOrderingDeprecated(final Ordering<Image> ordering) { + return new Function<Iterable<? extends Image>, Image>() { + @Override + public Image apply(Iterable<? extends Image> input) { + List<? extends Image> maxImages = multiMax(ordering, input); + return maxImages.get(maxImages.size() - 1); + } + }; + } + + protected interface ComputeServiceAwareChooser<T> { + public T cloneFor(ComputeService service); + } + + /** Attempts to clone the given item for use with the given {@link ComputeService}, if + * the item is {@link ComputeServiceAwareChooser}; otherwise it returns the item unchanged */ + @SuppressWarnings("unchecked") + public static <T> T cloneFor(T item, ComputeService service) { + if (item instanceof ComputeServiceAwareChooser) { + return ((ComputeServiceAwareChooser<T>)item).cloneFor(service); + } + return item; + } + + // from jclouds + static <T, E extends T> List<E> multiMax(Comparator<T> ordering, Iterable<E> iterable) { + Iterator<E> iterator = iterable.iterator(); + List<E> maxes = MutableList.of(iterator.next()); + E maxSoFar = maxes.get(0); + while (iterator.hasNext()) { + E current = iterator.next(); + int comparison = ordering.compare(maxSoFar, current); + if (comparison == 0) { + maxes.add(current); + } else if (comparison < 0) { + maxes = MutableList.of(current); + maxSoFar = current; + } + } + return maxes; + } + + public Ordering<Image> ordering() { + return orderingWithDefaults(orderingScoredWithoutDefaults()); + } + + public Function<Iterable<? extends Image>,Image> chooser() { + return imageChooserFromOrdering(ordering()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java new file mode 100644 index 0000000..e857e93 --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BrooklynMachinePool.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import static org.apache.brooklyn.location.jclouds.pool.MachinePoolPredicates.matching; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.management.Task; +import org.jclouds.compute.domain.NodeMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.trait.Startable; +import org.apache.brooklyn.location.MachineLocation; +import org.apache.brooklyn.location.basic.SshMachineLocation; +import org.apache.brooklyn.location.jclouds.pool.MachinePool; +import org.apache.brooklyn.location.jclouds.pool.MachineSet; +import org.apache.brooklyn.location.jclouds.pool.ReusableMachineTemplate; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.task.BasicExecutionContext; + +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; + +/** + * @deprecated since 0.6.0; never used in production setting, and thus of dubious value; best avoided as unlikely to be supported in future versions + */ +@Deprecated +public class BrooklynMachinePool extends MachinePool { + + private static final Logger log = LoggerFactory.getLogger(BrooklynMachinePool.class); + + protected final JcloudsLocation location; + final List<Task<?>> activeTasks = new ArrayList<Task<?>>(); + final String providerLocationId; + + public BrooklynMachinePool(JcloudsLocation l) { + super(l.getComputeService()); + providerLocationId = l.getRegion(); + this.location = l; + } + + /** claims a machine with the indicated spec, creating if necessary */ + public SshMachineLocation obtain(ReusableMachineTemplate t) { + MachineSet previous = unclaimed(matching(t)); + + while (true) { + NodeMetadata m = claim(1, t).iterator().next(); + // TODO ideally shouldn't have to rebind + SshMachineLocation result = null; + try { + result = toSshMachineLocation( m ); + } catch (Exception e) { + if (previous.contains(m)) { + log.debug("attempt to bind to previous existing machine "+m+" failed (will blacklist and retry another): "+e); + } else { + log.warn("attempt to bind to machine "+m+" failed: "+e); + throw Throwables.propagate(e); + } + } + if (result!=null) return result; + if (previous.contains(m)) { + log.debug("could not bind to previous existing machine "+m+"; blacklisting and trying a new one"); + addToBlacklist(new MachineSet(m)); + } else { + throw new IllegalStateException("cannot bind/connect to newly created machine; error in configuration"); + } + } + } + + protected MachineSet filterForAllowedMachines(MachineSet input) { + MachineSet result = super.filterForAllowedMachines(input); + if (providerLocationId!=null) { + result = result.filtered(matching( new ReusableMachineTemplate().locationId(providerLocationId).strict(false) )); + } + return result; + } + + /** returns an SshMachineLocation, if one can be created and accessed; returns null if it cannot be created */ + protected SshMachineLocation toSshMachineLocation(NodeMetadata m) { + try { + JcloudsSshMachineLocation sshM = location.rebindMachine(m); + if (sshM.execCommands("check-reachable", Arrays.asList("whoami")) != 0) { + log.warn("cannot bind to machine "+m); + return null; + } + return sshM; + } catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public MachineSet create(int count, ReusableMachineTemplate template) { + List<NodeMetadata> nodes = new ArrayList<NodeMetadata>(); + for (int i=0; i<count; i++) { + // TODO this in parallel + JcloudsSshMachineLocation m; + try { + MachineLocation machineLocation = location.obtain(MutableMap.of("callerContext", ""+this+"("+template+")"), template); + // Class has been deprecated since 0.6.0, and prior to that, obtain would have returned a JcloudsSshMachineLocation + if (machineLocation instanceof JcloudsSshMachineLocation) { + m = (JcloudsSshMachineLocation) machineLocation; + } else { + throw new UnsupportedOperationException("Cannot create WinRmMachineLocation"); + } + } catch (Exception e) { + throw Throwables.propagate(e); + } + nodes.add(m.getNode()); + } + MachineSet result = new MachineSet(nodes); + registerNewNodes(result, template); + return result; + } + + public boolean unclaim(SshMachineLocation location) { + init(); + if (location instanceof JcloudsSshMachineLocation) + return unclaim(new MachineSet( ((JcloudsSshMachineLocation)location).getNode()) ) > 0; + return false; + } + public boolean destroy(SshMachineLocation location) { + init(); + if (location instanceof JcloudsSshMachineLocation) + return destroy(new MachineSet( ((JcloudsSshMachineLocation)location).getNode()) ) > 0; + return false; + } + + // TODO we need to remove stale tasks somewhere + protected <T> Task<T> addTask(Task<T> t) { + synchronized (activeTasks) { activeTasks.add(t); } + return t; + } + + public List<Task<?>> getActiveTasks() { + List<Task<?>> result; + synchronized (activeTasks) { result = ImmutableList.<Task<?>>copyOf(activeTasks); } + return result; + } + + public void blockUntilTasksEnded() { + while (true) { + boolean allDone = true; + List<Task<?>> tt = getActiveTasks(); + for (Task<?> t: tt) { + if (!t.isDone()) { + allDone = false; + if (log.isDebugEnabled()) log.debug("Pool "+this+", blocking for completion of: "+t); + t.blockUntilEnded(); + } + } + synchronized (activeTasks) { + List<Task> newTT = new ArrayList<Task>(getActiveTasks()); + newTT.removeAll(tt); + if (allDone && tt.isEmpty()) { + //task list has stabilized, and there are no active tasks; clear and exit + if (log.isDebugEnabled()) log.debug("Pool "+this+", all known tasks have completed, clearing list"); + activeTasks.clear(); + break; + } + if (log.isDebugEnabled()) log.debug("Pool "+this+", all previously known tasks have completed, but there are new tasks ("+newTT+") checking them"); + } + } + } + + /** starts the given template; for use only within a task (e.g. application's start effector). + * returns a child task of the current task. + * <p> + * throws exception if not in a task. (you will have to claim, then invoke the effectors manually.) */ + public Task<?> start(final ReusableMachineTemplate template, final List<? extends Startable> entities) { + BasicExecutionContext ctx = BasicExecutionContext.getCurrentExecutionContext(); + if (ctx==null) throw new IllegalStateException("Pool.start is only permitted within a task (effector)"); + final AtomicReference<Task<?>> t = new AtomicReference<Task<?>>(); + synchronized (t) { + t.set(ctx.submit(new Runnable() { + public void run() { + synchronized (t) { + if (log.isDebugEnabled()) log.debug("Pool "+this+", task "+t.get()+" claiming a "+template); + SshMachineLocation m = obtain(template); + if (log.isDebugEnabled()) log.debug("Pool "+this+", task "+t.get()+" got "+m+"; starting "+entities); + for (Startable entity: entities) + addTask( ((Entity)entity).invoke(Startable.START, MutableMap.of("locations", Arrays.asList(m))) ); + } + } + })); + } + addTask(t.get()); + return t.get(); + } + + /** @see #start(ReusableMachineTemplate, List) */ + public Task<?> start(ReusableMachineTemplate template, Startable ...entities) { + return start(template, Arrays.asList(entities)); + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java new file mode 100644 index 0000000..a835e2c --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistry.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import org.jclouds.compute.ComputeService; + +import brooklyn.util.config.ConfigBag; + +public interface ComputeServiceRegistry { + + public ComputeService findComputeService(ConfigBag conf, boolean allowReuse); +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java new file mode 100644 index 0000000..f3f39bb --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ComputeServiceRegistryImpl.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import static brooklyn.util.JavaGroovyEquivalents.groovyTruth; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_AMI_QUERY; +import static org.jclouds.aws.ec2.reference.AWSEC2Constants.PROPERTY_EC2_CC_AMI_QUERY; + +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.brooklyn.location.cloud.CloudLocationConfig; +import org.jclouds.Constants; +import org.jclouds.ContextBuilder; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.ec2.reference.EC2Constants; +import org.jclouds.encryption.bouncycastle.config.BouncyCastleCryptoModule; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.sshj.config.SshjSshClientModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.basic.Sanitizer; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.config.ConfigBag; +import brooklyn.util.time.Duration; + +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.inject.Module; + +public class ComputeServiceRegistryImpl implements ComputeServiceRegistry, JcloudsLocationConfig { + + private static final Logger LOG = LoggerFactory.getLogger(ComputeServiceRegistryImpl.class); + + public static final ComputeServiceRegistryImpl INSTANCE = new ComputeServiceRegistryImpl(); + + protected ComputeServiceRegistryImpl() { + } + + protected final Map<Map<?,?>,ComputeService> cachedComputeServices = new ConcurrentHashMap<Map<?,?>,ComputeService>(); + + protected final Object createComputeServicesMutex = new Object(); + + @Override + public ComputeService findComputeService(ConfigBag conf, boolean allowReuse) { + String provider = checkNotNull(conf.get(CLOUD_PROVIDER), "provider must not be null"); + String identity = checkNotNull(conf.get(CloudLocationConfig.ACCESS_IDENTITY), "identity must not be null"); + String credential = checkNotNull(conf.get(CloudLocationConfig.ACCESS_CREDENTIAL), "credential must not be null"); + + Properties properties = new Properties(); + properties.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, Boolean.toString(true)); + properties.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, Boolean.toString(true)); + properties.setProperty("jclouds.ssh.max-retries", conf.getStringKey("jclouds.ssh.max-retries") != null ? + conf.getStringKey("jclouds.ssh.max-retries").toString() : "50"); + // Enable aws-ec2 lazy image fetching, if given a specific imageId; otherwise customize for specific owners; or all as a last resort + // See https://issues.apache.org/jira/browse/WHIRR-416 + if ("aws-ec2".equals(provider)) { + // TODO convert AWS-only flags to config keys + if (groovyTruth(conf.get(IMAGE_ID))) { + properties.setProperty(PROPERTY_EC2_AMI_QUERY, ""); + properties.setProperty(PROPERTY_EC2_CC_AMI_QUERY, ""); + } else if (groovyTruth(conf.getStringKey("imageOwner"))) { + properties.setProperty(PROPERTY_EC2_AMI_QUERY, "owner-id="+conf.getStringKey("imageOwner")+";state=available;image-type=machine"); + } else if (groovyTruth(conf.getStringKey("anyOwner"))) { + // set `anyOwner: true` to override the default query (which is restricted to certain owners as per below), + // allowing the AMI query to bind to any machine + // (note however, we sometimes pick defaults in JcloudsLocationFactory); + // (and be careful, this can give a LOT of data back, taking several minutes, + // and requiring extra memory allocated on the command-line) + properties.setProperty(PROPERTY_EC2_AMI_QUERY, "state=available;image-type=machine"); + /* + * by default the following filters are applied: + * Filter.1.Name=owner-id&Filter.1.Value.1=137112412989& + * Filter.1.Value.2=063491364108& + * Filter.1.Value.3=099720109477& + * Filter.1.Value.4=411009282317& + * Filter.2.Name=state&Filter.2.Value.1=available& + * Filter.3.Name=image-type&Filter.3.Value.1=machine& + */ + } + + // occasionally can get com.google.common.util.concurrent.UncheckedExecutionException: java.lang.RuntimeException: + // security group eu-central-1/jclouds#brooklyn-bxza-alex-eu-central-shoul-u2jy-nginx-ielm is not available after creating + // the default timeout was 500ms so let's raise it in case that helps + properties.setProperty(EC2Constants.PROPERTY_EC2_TIMEOUT_SECURITYGROUP_PRESENT, ""+Duration.seconds(30).toMilliseconds()); + } + + // FIXME Deprecated mechanism, should have a ConfigKey for overrides + Map<String, Object> extra = Maps.filterKeys(conf.getAllConfig(), Predicates.containsPattern("^jclouds\\.")); + if (extra.size() > 0) { + LOG.warn("Jclouds using deprecated property overrides: "+Sanitizer.sanitize(extra)); + } + properties.putAll(extra); + + String endpoint = conf.get(CloudLocationConfig.CLOUD_ENDPOINT); + if (!groovyTruth(endpoint)) endpoint = getDeprecatedProperty(conf, Constants.PROPERTY_ENDPOINT); + if (groovyTruth(endpoint)) properties.setProperty(Constants.PROPERTY_ENDPOINT, endpoint); + + Map<?,?> cacheKey = MutableMap.builder() + .putAll(properties) + .put("provider", provider) + .put("identity", identity) + .put("credential", credential) + .putIfNotNull("endpoint", endpoint) + .build() + .asUnmodifiable(); + + if (allowReuse) { + ComputeService result = cachedComputeServices.get(cacheKey); + if (result!=null) { + LOG.trace("jclouds ComputeService cache hit for compute service, for "+Sanitizer.sanitize(properties)); + return result; + } + LOG.debug("jclouds ComputeService cache miss for compute service, creating, for "+Sanitizer.sanitize(properties)); + } + + Iterable<Module> modules = getCommonModules(); + + // Synchronizing to avoid deadlock from sun.reflect.annotation.AnnotationType. + // See https://github.com/brooklyncentral/brooklyn/issues/974 + ComputeServiceContext computeServiceContext; + synchronized (createComputeServicesMutex) { + computeServiceContext = ContextBuilder.newBuilder(provider) + .modules(modules) + .credentials(identity, credential) + .overrides(properties) + .build(ComputeServiceContext.class); + } + final ComputeService computeService = computeServiceContext.getComputeService(); + if (allowReuse) { + synchronized (cachedComputeServices) { + ComputeService result = cachedComputeServices.get(cacheKey); + if (result != null) { + LOG.debug("jclouds ComputeService cache recovery for compute service, for "+Sanitizer.sanitize(cacheKey)); + //keep the old one, discard the new one + computeService.getContext().close(); + return result; + } + LOG.debug("jclouds ComputeService created "+computeService+", adding to cache, for "+Sanitizer.sanitize(properties)); + cachedComputeServices.put(cacheKey, computeService); + } + } + return computeService; + } + + /** returns the jclouds modules we typically install */ + protected ImmutableSet<Module> getCommonModules() { + return ImmutableSet.<Module> of( + new SshjSshClientModule(), + new SLF4JLoggingModule(), + new BouncyCastleCryptoModule()); + } + + protected String getDeprecatedProperty(ConfigBag conf, String key) { + if (conf.containsKey(key)) { + LOG.warn("Jclouds using deprecated brooklyn-jclouds property "+key+": "+Sanitizer.sanitize(conf.getAllConfig())); + return (String) conf.getStringKey(key); + } else { + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e2c57058/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java new file mode 100644 index 0000000..5aa026d --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsByonLocationResolver.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.location.jclouds; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.brooklyn.api.management.ManagementContext; +import org.apache.brooklyn.location.LocationRegistry; +import org.apache.brooklyn.api.management.ManagementContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.brooklyn.location.LocationResolver; +import org.apache.brooklyn.location.LocationSpec; +import org.apache.brooklyn.location.NoMachinesAvailableException; +import org.apache.brooklyn.location.basic.BasicLocationRegistry; +import org.apache.brooklyn.location.basic.FixedListMachineProvisioningLocation; +import org.apache.brooklyn.location.basic.LocationConfigKeys; +import org.apache.brooklyn.location.basic.LocationConfigUtils; +import org.apache.brooklyn.location.basic.LocationInternal; +import org.apache.brooklyn.location.basic.LocationPropertiesFromBrooklynProperties; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.config.ConfigBag; +import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.text.KeyValueParser; +import brooklyn.util.text.Strings; +import brooklyn.util.text.WildcardGlobs; +import brooklyn.util.text.WildcardGlobs.PhraseTreatment; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +/** + * Examples of valid specs: + * <ul> + * <li>byon:(hosts=myhost) + * <li>byon:(hosts="myhost, myhost2") + * <li>byon:(hosts="myhost, myhost2", name="my location name") + * </ul> + * + * @author aled + */ +@SuppressWarnings({"unchecked","rawtypes"}) +public class JcloudsByonLocationResolver implements LocationResolver { + + public static final Logger log = LoggerFactory.getLogger(JcloudsByonLocationResolver.class); + + public static final String BYON = "jcloudsByon"; + + private static final Pattern PATTERN = Pattern.compile("("+BYON+"|"+BYON.toUpperCase()+")" + ":" + "\\((.*)\\)$"); + + private ManagementContext managementContext; + + @Override + public void init(ManagementContext managementContext) { + this.managementContext = checkNotNull(managementContext, "managementContext"); + } + + // TODO Remove some duplication from JcloudsResolver; needs more careful review + @Override + public FixedListMachineProvisioningLocation<JcloudsSshMachineLocation> newLocationFromString(Map locationFlags, String spec, LocationRegistry registry) { + Map globalProperties = registry.getProperties(); + + Matcher matcher = PATTERN.matcher(spec); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid location '"+spec+"'; must specify something like jcloudsByon(provider=\"aws-ec2\",region=\"us-east-1\",hosts=\"i-f2014593,i-d1234567\")"); + } + + String argsPart = matcher.group(2); + Map<String, String> argsMap = KeyValueParser.parseMap(argsPart); + + // prefer args map over location flags + + String namedLocation = (String) locationFlags.get(LocationInternal.NAMED_SPEC_NAME.getName()); + + String providerOrApi = argsMap.containsKey("provider") ? argsMap.get("provider") : (String)locationFlags.get("provider"); + + String regionName = argsMap.containsKey("region") ? argsMap.get("region") : (String)locationFlags.get("region"); + + String endpoint = argsMap.containsKey("endpoint") ? argsMap.get("endpoint") : (String)locationFlags.get("endpoint"); + + String name = argsMap.containsKey("name") ? argsMap.get("name") : (String)locationFlags.get("name"); + + String user = argsMap.containsKey("user") ? argsMap.get("user") : (String)locationFlags.get("user"); + + String privateKeyFile = argsMap.containsKey("privateKeyFile") ? argsMap.get("privateKeyFile") : (String)locationFlags.get("privateKeyFile"); + + String hosts = argsMap.get("hosts"); + + if (Strings.isEmpty(providerOrApi)) { + throw new IllegalArgumentException("Invalid location '"+spec+"'; provider must be defined"); + } + if (hosts == null || hosts.isEmpty()) { + throw new IllegalArgumentException("Invalid location '"+spec+"'; at least one host must be defined"); + } + if (argsMap.containsKey("name") && (Strings.isEmpty(name))) { + throw new IllegalArgumentException("Invalid location '"+spec+"'; if name supplied then value must be non-empty"); + } + + // For everything in brooklyn.properties, only use things with correct prefix (and remove that prefix). + // But for everything passed in via locationFlags, pass those as-is. + // TODO Should revisit the locationFlags: where are these actually used? Reason accepting properties without + // full prefix is that the map's context is explicitly this location, rather than being generic properties. + Map allProperties = getAllProperties(registry, globalProperties); + Map jcloudsProperties = new JcloudsPropertiesFromBrooklynProperties().getJcloudsProperties(providerOrApi, regionName, namedLocation, allProperties); + jcloudsProperties.putAll(locationFlags); + jcloudsProperties.putAll(argsMap); + + String jcloudsSpec = "jclouds:"+providerOrApi + (regionName != null ? ":"+regionName : "") + (endpoint != null ? ":"+endpoint : ""); + JcloudsLocation jcloudsLocation = (JcloudsLocation) registry.resolve(jcloudsSpec, jcloudsProperties); + + List<String> hostIdentifiers = WildcardGlobs.getGlobsAfterBraceExpansion("{"+hosts+"}", + true /* numeric */, /* no quote support though */ PhraseTreatment.NOT_A_SPECIAL_CHAR, PhraseTreatment.NOT_A_SPECIAL_CHAR); + List<JcloudsSshMachineLocation> machines = Lists.newArrayList(); + + for (String hostIdentifier : hostIdentifiers) { + Map<?, ?> machineFlags = MutableMap.builder() + .put("id", hostIdentifier) + .putIfNotNull("user", user) + .putIfNotNull("privateKeyFile", privateKeyFile) + .build(); + try { + JcloudsSshMachineLocation machine = jcloudsLocation.rebindMachine(jcloudsLocation.config().getBag().putAll(machineFlags)); + machines.add(machine); + } catch (NoMachinesAvailableException e) { + log.warn("Error rebinding to jclouds machine "+hostIdentifier+" in "+jcloudsLocation, e); + Exceptions.propagate(e); + } + } + + ConfigBag flags = ConfigBag.newInstance(jcloudsProperties); + + flags.putStringKey("machines", machines); + flags.putIfNotNull(LocationConfigKeys.USER, user); + flags.putStringKeyIfNotNull("name", name); + + if (registry != null) + LocationPropertiesFromBrooklynProperties.setLocalTempDir(registry.getProperties(), flags); + + log.debug("Created Jclouds BYON location "+name+": "+machines); + + return managementContext.getLocationManager().createLocation(LocationSpec.create(FixedListMachineProvisioningLocation.class) + .configure(flags.getAllConfig()) + .configure(LocationConfigUtils.finalAndOriginalSpecs(spec, locationFlags, globalProperties, namedLocation))); + } + + private Map getAllProperties(LocationRegistry registry, Map<?,?> properties) { + Map<Object,Object> allProperties = Maps.newHashMap(); + if (registry!=null) allProperties.putAll(registry.getProperties()); + allProperties.putAll(properties); + return allProperties; + } + + @Override + public String getPrefix() { + return BYON; + } + + @Override + public boolean accepts(String spec, LocationRegistry registry) { + return BasicLocationRegistry.isResolverPrefixForSpec(this, spec, true); + } +} \ No newline at end of file
