Updated Branches: refs/heads/master 14fbe2d38 -> fea10aa14
JCLOUDS-205: Do not create duplicated groups in Abiquo Refactored the AbiquoComputeServiceAdapter to avoid creating more than one group with the same name, when creating multiple nodes concurrently. Project: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/commit/fea10aa1 Tree: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/tree/fea10aa1 Diff: http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/diff/fea10aa1 Branch: refs/heads/master Commit: fea10aa14e078ae185932afbc599e4aea15778e0 Parents: 14fbe2d Author: Ignasi Barrera <[email protected]> Authored: Mon Aug 5 14:35:15 2013 +0200 Committer: Ignasi Barrera <[email protected]> Committed: Fri Aug 30 17:39:17 2013 +0200 ---------------------------------------------------------------------- .../AbiquoComputeServiceContextModule.java | 5 +- .../compute/options/AbiquoTemplateOptions.java | 6 +- .../strategy/AbiquoComputeServiceAdapter.java | 54 +++---- .../CreateGroupBeforeCreatingNodes.java | 114 +++++++++++++++ .../VirtualApplianceCachingTemplate.java | 146 +++++++++++++++++++ .../compute/AbiquoComputeServiceLiveTest.java | 20 --- 6 files changed, 290 insertions(+), 55 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/blob/fea10aa1/abiquo/src/main/java/org/jclouds/abiquo/compute/config/AbiquoComputeServiceContextModule.java ---------------------------------------------------------------------- diff --git a/abiquo/src/main/java/org/jclouds/abiquo/compute/config/AbiquoComputeServiceContextModule.java b/abiquo/src/main/java/org/jclouds/abiquo/compute/config/AbiquoComputeServiceContextModule.java index eed2794..6174423 100644 --- a/abiquo/src/main/java/org/jclouds/abiquo/compute/config/AbiquoComputeServiceContextModule.java +++ b/abiquo/src/main/java/org/jclouds/abiquo/compute/config/AbiquoComputeServiceContextModule.java @@ -18,11 +18,12 @@ package org.jclouds.abiquo.compute.config; import org.jclouds.abiquo.compute.functions.DatacenterToLocation; import org.jclouds.abiquo.compute.functions.VirtualDatacenterToLocation; -import org.jclouds.abiquo.compute.functions.VirtualMachineTemplateToImage; import org.jclouds.abiquo.compute.functions.VirtualMachineTemplateInVirtualDatacenterToHardware; +import org.jclouds.abiquo.compute.functions.VirtualMachineTemplateToImage; import org.jclouds.abiquo.compute.functions.VirtualMachineToNodeMetadata; import org.jclouds.abiquo.compute.options.AbiquoTemplateOptions; import org.jclouds.abiquo.compute.strategy.AbiquoComputeServiceAdapter; +import org.jclouds.abiquo.compute.strategy.CreateGroupBeforeCreatingNodes; import org.jclouds.abiquo.domain.cloud.VirtualDatacenter; import org.jclouds.abiquo.domain.cloud.VirtualMachine; import org.jclouds.abiquo.domain.cloud.VirtualMachineTemplate; @@ -34,6 +35,7 @@ import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Image; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet; import org.jclouds.domain.Location; import org.jclouds.location.suppliers.ImplicitLocationSupplier; import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone; @@ -69,6 +71,7 @@ public class AbiquoComputeServiceContextModule }).to(VirtualDatacenterToLocation.class); bind(ImplicitLocationSupplier.class).to(OnlyLocationOrFirstZone.class).in(Scopes.SINGLETON); bind(TemplateOptions.class).to(AbiquoTemplateOptions.class); + bind(CreateNodesInGroupThenAddToSet.class).to(CreateGroupBeforeCreatingNodes.class); install(new LocationsFromComputeServiceAdapterModule<VirtualMachine, VirtualMachineTemplateInVirtualDatacenter, VirtualMachineTemplate, VirtualDatacenter>() { }); } http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/blob/fea10aa1/abiquo/src/main/java/org/jclouds/abiquo/compute/options/AbiquoTemplateOptions.java ---------------------------------------------------------------------- diff --git a/abiquo/src/main/java/org/jclouds/abiquo/compute/options/AbiquoTemplateOptions.java b/abiquo/src/main/java/org/jclouds/abiquo/compute/options/AbiquoTemplateOptions.java index aa20c43..1fd980b 100644 --- a/abiquo/src/main/java/org/jclouds/abiquo/compute/options/AbiquoTemplateOptions.java +++ b/abiquo/src/main/java/org/jclouds/abiquo/compute/options/AbiquoTemplateOptions.java @@ -19,10 +19,8 @@ package org.jclouds.abiquo.compute.options; import org.jclouds.compute.options.TemplateOptions; /** - * Contains options supported by the - * {@link ComputeService#createNodesInGroup(String, int, TemplateOptions)} and - * {@link ComputeService#createNodesInGroup(String, int, TemplateOptions)} - * operations on the <em>Abiquo</em> provider. + * Contains options supported by the ComputeService on the <em>Abiquo</em> + * provider. * * @author Ignasi Barrera */ http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/blob/fea10aa1/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/AbiquoComputeServiceAdapter.java ---------------------------------------------------------------------- diff --git a/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/AbiquoComputeServiceAdapter.java b/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/AbiquoComputeServiceAdapter.java index b82bb9a..1941cee 100644 --- a/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/AbiquoComputeServiceAdapter.java +++ b/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/AbiquoComputeServiceAdapter.java @@ -16,14 +16,15 @@ */ package org.jclouds.abiquo.compute.strategy; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.contains; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Iterables.tryFind; -import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -33,7 +34,6 @@ import javax.inject.Singleton; import org.jclouds.abiquo.AbiquoApi; import org.jclouds.abiquo.compute.options.AbiquoTemplateOptions; -import org.jclouds.abiquo.domain.cloud.VirtualAppliance; import org.jclouds.abiquo.domain.cloud.VirtualDatacenter; import org.jclouds.abiquo.domain.cloud.VirtualMachine; import org.jclouds.abiquo.domain.cloud.VirtualMachineTemplate; @@ -60,6 +60,7 @@ import org.jclouds.rest.ApiContext; import com.abiquo.server.core.cloud.VirtualMachineState; import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.Lists; @@ -70,6 +71,8 @@ import com.google.inject.Inject; * jclouds {@link ComputeService}. * * @author Ignasi Barrera + * + * @see CreateGroupBeforeCreatingNodes */ @Singleton public class AbiquoComputeServiceAdapter @@ -108,8 +111,15 @@ public class AbiquoComputeServiceAdapter } @Override - public NodeAndInitialCredentials<VirtualMachine> createNodeWithGroupEncodedIntoName(final String tag, + public NodeAndInitialCredentials<VirtualMachine> createNodeWithGroupEncodedIntoName(final String group, final String name, final Template template) { + checkArgument(template instanceof VirtualApplianceCachingTemplate, + "A VirtualApplianceCachingTemplate is required"); + return createNodeWithGroupEncodedIntoName(name, VirtualApplianceCachingTemplate.class.cast(template)); + } + + protected NodeAndInitialCredentials<VirtualMachine> createNodeWithGroupEncodedIntoName(final String name, + final VirtualApplianceCachingTemplate template) { AbiquoTemplateOptions options = template.getOptions().as(AbiquoTemplateOptions.class); Enterprise enterprise = adminService.getCurrentEnterprise(); @@ -120,27 +130,10 @@ public class AbiquoComputeServiceAdapter VirtualMachineTemplate virtualMachineTemplate = enterprise.getTemplateInRepository(datacenter, Integer.valueOf(template.getImage().getId())); - // Get the zone where the template will be deployed - VirtualDatacenter vdc = cloudService.getVirtualDatacenter(Integer.valueOf(template.getHardware().getLocation() - .getId())); - - // Load the virtual appliance or create it if it does not exist - VirtualAppliance vapp = find(vdc.listVirtualAppliances(), new Predicate<VirtualAppliance>() { - @Override - public boolean apply(VirtualAppliance input) { - return input.getName().equals(tag); - } - }, null); - - if (vapp == null) { - vapp = VirtualAppliance.builder(context, vdc).name(tag).build(); - vapp.save(); - } - Integer overrideCores = options.getOverrideCores(); Integer overrideRam = options.getOverrideRam(); - VirtualMachine vm = VirtualMachine.builder(context, vapp, virtualMachineTemplate) // + VirtualMachine vm = VirtualMachine.builder(context, template.getVirtualAppliance(), virtualMachineTemplate) // .nameLabel(name) // .cpu(overrideCores != null ? overrideCores : totalCores(template.getHardware())) // .ram(overrideRam != null ? overrideRam : template.getHardware().getRam()) // @@ -150,15 +143,17 @@ public class AbiquoComputeServiceAdapter vm.save(); // Once the virtual machine is created, override the default network - // settings if needed + // settings if needed. // If no public ip is available in the virtual datacenter, the virtual // machine will be assigned by default an ip address in the default - // private VLAN for the virtual datacenter - PublicIp publicIp = find(vdc.listPurchasedPublicIps(), IpPredicates.<PublicIp> notUsed(), null); - if (publicIp != null) { - List<Ip<?, ?>> ips = Lists.newArrayList(); - ips.add(publicIp); - vm.setNics(ips); + // private VLAN for the virtual datacenter. + Optional<PublicIp> publicIp = tryFind(template.getVirtualDatacenter().listPurchasedPublicIps(), + IpPredicates.<PublicIp> notUsed()); + if (publicIp.isPresent()) { + logger.debug(">> Found available public ip %s", publicIp.get().getIp()); + vm.setNics(Lists.<Ip<?, ?>> newArrayList(publicIp.get())); + } else { + logger.debug(">> No available public ip found. Using a private ip"); } // This is an async operation, but jclouds already waits until the node is @@ -199,8 +194,7 @@ public class AbiquoComputeServiceAdapter @Override public VirtualMachineTemplate getImage(final String id) { - Enterprise enterprise = adminService.getCurrentEnterprise(); - return find(enterprise.listTemplates(), new Predicate<VirtualMachineTemplate>() { + return find(listImages(), new Predicate<VirtualMachineTemplate>() { @Override public boolean apply(VirtualMachineTemplate input) { return input.getId().equals(id); http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/blob/fea10aa1/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/CreateGroupBeforeCreatingNodes.java ---------------------------------------------------------------------- diff --git a/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/CreateGroupBeforeCreatingNodes.java b/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/CreateGroupBeforeCreatingNodes.java new file mode 100644 index 0000000..3096c99 --- /dev/null +++ b/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/CreateGroupBeforeCreatingNodes.java @@ -0,0 +1,114 @@ +/* + * 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.jclouds.abiquo.compute.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.tryFind; + +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.abiquo.AbiquoApi; +import org.jclouds.abiquo.domain.cloud.VirtualAppliance; +import org.jclouds.abiquo.domain.cloud.VirtualDatacenter; +import org.jclouds.abiquo.features.services.CloudService; +import org.jclouds.compute.config.CustomizationResponse; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.functions.GroupNamingConvention; +import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName; +import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap; +import org.jclouds.compute.strategy.ListNodesStrategy; +import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet; +import org.jclouds.rest.ApiContext; + +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Multimap; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; + +/** + * Creates the group before concurrently creating the nodes, to avoid creating + * more than one group with the same name. + * + * @author Ignasi Barrera + */ +@Singleton +public class CreateGroupBeforeCreatingNodes extends CreateNodesWithGroupEncodedIntoNameThenAddToSet { + + protected final ApiContext<AbiquoApi> context; + + protected final CloudService cloudService; + + @Inject + protected CreateGroupBeforeCreatingNodes( + CreateNodeWithGroupEncodedIntoName addNodeWithGroupStrategy, + ListNodesStrategy listNodesStrategy, + GroupNamingConvention.Factory namingConvention, + @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, + CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory, + ApiContext<AbiquoApi> context, CloudService cloudService) { + super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor, + customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory); + this.context = checkNotNull(context, "context must not be null"); + this.cloudService = checkNotNull(cloudService, "cloudService must not be null"); + } + + @Override + public Map<?, ListenableFuture<Void>> execute(final String group, int count, Template template, + Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes, + Multimap<NodeMetadata, CustomizationResponse> customizationResponses) { + // Get the zone where the template will be deployed + Integer locationId = Integer.valueOf(template.getHardware().getLocation().getId()); + VirtualDatacenter vdc = cloudService.getVirtualDatacenter(locationId); + + // Check if it already exists a group with the given name + Iterable<VirtualAppliance> existingGroups = vdc.listVirtualAppliances(); + Optional<VirtualAppliance> vapp = tryFind(existingGroups, new Predicate<VirtualAppliance>() { + @Override + public boolean apply(VirtualAppliance input) { + return input.getName().equals(group); + } + }); + + // Create the group if still does not exist + VirtualAppliance newVapp = null; + if (!vapp.isPresent()) { + logger.debug(">> Creating group %s", group); + newVapp = VirtualAppliance.builder(context, vdc).name(group).build(); + newVapp.save(); + logger.debug("<< group(%s) created", newVapp.getId()); + } else { + logger.debug(">> Using existing group(%s)", vapp.get().getId()); + } + + VirtualApplianceCachingTemplate abiquoTemplate = VirtualApplianceCachingTemplate // + .from(template) // + .withVirtualDatacenter(vdc) // + .withVirtualAppliance(vapp.or(newVapp)) // + .build(); + + return super.execute(group, count, abiquoTemplate, goodNodes, badNodes, customizationResponses); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/blob/fea10aa1/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/VirtualApplianceCachingTemplate.java ---------------------------------------------------------------------- diff --git a/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/VirtualApplianceCachingTemplate.java b/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/VirtualApplianceCachingTemplate.java new file mode 100644 index 0000000..a90b856 --- /dev/null +++ b/abiquo/src/main/java/org/jclouds/abiquo/compute/strategy/VirtualApplianceCachingTemplate.java @@ -0,0 +1,146 @@ +/* + * 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.jclouds.abiquo.compute.strategy; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.jclouds.abiquo.domain.cloud.VirtualAppliance; +import org.jclouds.abiquo.domain.cloud.VirtualDatacenter; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Location; + +import com.google.common.base.Objects; + +/** + * A {@link Template} implementation that caches the {@link VirtualAppliance} + * and the {@link VirtualDatacenter} where the nodes will be deployed. + * <p> + * When deploying multiple nodes at the same time, all go to the same virtual + * appliance and virtual datacenter. Having both cached in the template saves a + * couple extra api calls for each deployed node. + * <p> + * This class is not public as it is intended to be used internally. + * + * @author Ignasi Barrera + * + * @see CreateGroupBeforeCreatingNodes + * @see AbiquoComputeServiceAdapter + */ +class VirtualApplianceCachingTemplate implements Template { + + private final Template delegate; + private final VirtualDatacenter virtualDatacenter; + private final VirtualAppliance virtualAppliance; + + private VirtualApplianceCachingTemplate(Template delegate, VirtualDatacenter virtualDatacenter, + VirtualAppliance virtualAppliance) { + this.delegate = checkNotNull(delegate, "delegate"); + this.virtualDatacenter = checkNotNull(virtualDatacenter, "virtualDatacenter"); + this.virtualAppliance = checkNotNull(virtualAppliance, "virtualAppliance"); + } + + public VirtualDatacenter getVirtualDatacenter() { + return virtualDatacenter; + } + + public VirtualAppliance getVirtualAppliance() { + return virtualAppliance; + } + + // Delegate methods + + @Override + public Image getImage() { + return delegate.getImage(); + } + + @Override + public Hardware getHardware() { + return delegate.getHardware(); + } + + @Override + public Location getLocation() { + return delegate.getLocation(); + } + + @Override + public TemplateOptions getOptions() { + return delegate.getOptions(); + } + + @Override + public VirtualApplianceCachingTemplate clone() { + return new VirtualApplianceCachingTemplate(delegate.clone(), virtualDatacenter, virtualAppliance); + } + + @Override + public String toString() { + return delegate.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VirtualApplianceCachingTemplate that = VirtualApplianceCachingTemplate.class.cast(o); + return delegate.equals(that) && virtualAppliance.getId().equals(that.virtualAppliance.getId()) + && virtualDatacenter.getId().equals(that.virtualDatacenter.getId()); + } + + @Override + public int hashCode() { + return Objects.hashCode(getImage(), getHardware(), getLocation(), getOptions(), virtualDatacenter, + virtualAppliance); + } + + static Builder from(Template template) { + return new Builder(template); + } + + static class Builder { + private Template template; + private VirtualDatacenter virtualDatacenter; + private VirtualAppliance virtualAppliance; + + public Builder(Template template) { + this.template = template; + } + + public Builder withVirtualDatacenter(VirtualDatacenter virtualDatacenter) { + this.virtualDatacenter = virtualDatacenter; + return this; + } + + public Builder withVirtualAppliance(VirtualAppliance virtualAppliance) { + this.virtualAppliance = virtualAppliance; + return this; + } + + public VirtualApplianceCachingTemplate build() { + return new VirtualApplianceCachingTemplate(template, virtualDatacenter, virtualAppliance); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs/blob/fea10aa1/abiquo/src/test/java/org/jclouds/abiquo/compute/AbiquoComputeServiceLiveTest.java ---------------------------------------------------------------------- diff --git a/abiquo/src/test/java/org/jclouds/abiquo/compute/AbiquoComputeServiceLiveTest.java b/abiquo/src/test/java/org/jclouds/abiquo/compute/AbiquoComputeServiceLiveTest.java index 44a980d..b1773e6 100644 --- a/abiquo/src/test/java/org/jclouds/abiquo/compute/AbiquoComputeServiceLiveTest.java +++ b/abiquo/src/test/java/org/jclouds/abiquo/compute/AbiquoComputeServiceLiveTest.java @@ -53,28 +53,13 @@ public abstract class AbiquoComputeServiceLiveTest extends BaseComputeServiceLiv } @Override - public void setServiceDefaults() { - System.setProperty("test.abiquo.template", - "imageNameMatches=ubuntu_server_ssh_iptables,loginUser=user:abiquo,authenticateSudo=true"); - } - - @Override protected Properties setupProperties() { Properties overrides = super.setupProperties(); overrides.put(Constants.PROPERTY_MAX_RETRIES, "0"); - overrides.put(Constants.PROPERTY_MAX_REDIRECTS, "0"); - overrides.put("jclouds.timeouts.CloudApi.listVirtualMachines", "60000"); return overrides; } @Override - protected void initializeContext() { - super.initializeContext(); - String templateId = buildTemplate(client.templateBuilder()).getImage().getId(); - view.utils().credentialStore().put("image#" + templateId, loginCredentials); - } - - @Override protected LoggingModule getLoggingModule() { return new SLF4JLoggingModule(); } @@ -96,11 +81,6 @@ public abstract class AbiquoComputeServiceLiveTest extends BaseComputeServiceLiv } } - @Override - public void testOptionToNotBlock() throws Exception { - // By default the provider blocks until the node is running - } - // Abiquo does not set the hostname @Override protected void checkResponseEqualsHostname(final ExecResponse execResponse, final NodeMetadata node) {
