From 87f6bdcce8080097ed1f21a03187bb8e7821154c Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Fri, 4 Mar 2011 18:23:16 -0500 Subject: [PATCH 01/13] cleaned up org and vdc for savvis --- .../org/jclouds/vcloud/xml/VDCHandler.java | 32 +- .../savvis/vpdc/SymphonyVPDCAsyncClient.java | 295 +++++++++++++++--- .../savvis/vpdc/SymphonyVPDCClient.java | 6 + .../savvis/vpdc/domain/SymphonyVPDCVDC.java | 34 ++ .../domain/internal/SymphonyVPDCVDCImpl.java | 75 +++++ .../vpdc/xml/SymphonyVPDCVDCHandler.java | 58 ++++ .../vpdc/SymphonyVPDCAsyncClientTest.java | 51 ++- .../vpdc/SymphonyVPDCClientLiveTest.java | 280 ++++++++++++++++- .../SymphonyVPDCComputeServiceLiveTest.java | 2 +- .../SymphonyVPDCTemplateBuilderLiveTest.java | 2 +- .../savvis/vpdc/xml/OrgHandlerTest.java | 66 ++++ .../vpdc/xml/SymphonyVPDCVDCHandlerTest.java | 93 ++++++ .../src/test/resources/savvis/org.xml | 5 + .../src/test/resources/savvis/vdc.xml | 13 + 14 files changed, 926 insertions(+), 86 deletions(-) create mode 100644 sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/domain/SymphonyVPDCVDC.java create mode 100644 sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/domain/internal/SymphonyVPDCVDCImpl.java create mode 100644 sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/xml/SymphonyVPDCVDCHandler.java create mode 100644 sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/xml/OrgHandlerTest.java create mode 100644 sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/xml/SymphonyVPDCVDCHandlerTest.java create mode 100644 sandbox-providers/savvis-symphony-vpdc/src/test/resources/savvis/org.xml create mode 100644 sandbox-providers/savvis-symphony-vpdc/src/test/resources/savvis/vdc.xml diff --git a/common/vcloud/src/main/java/org/jclouds/vcloud/xml/VDCHandler.java b/common/vcloud/src/main/java/org/jclouds/vcloud/xml/VDCHandler.java index c0270a84ef..8343314f9a 100644 --- a/common/vcloud/src/main/java/org/jclouds/vcloud/xml/VDCHandler.java +++ b/common/vcloud/src/main/java/org/jclouds/vcloud/xml/VDCHandler.java @@ -117,43 +117,43 @@ public class VDCHandler extends ParseSax.HandlerWithResult { public void endElement(String uri, String name, String qName) { taskHandler.endElement(uri, name, qName); - if (qName.equals("Task")) { + if (qName.endsWith("Task")) { this.tasks.add(taskHandler.getResult()); - } else if (qName.equals("Description")) { + } else if (qName.endsWith("Description")) { description = currentOrNull(); - } else if (qName.equals("AllocationModel")) { + } else if (qName.endsWith("AllocationModel")) { allocationModel = AllocationModel.fromValue(currentOrNull()); - } else if (qName.equals("Units")) { + } else if (qName.endsWith("Units")) { units = currentOrNull(); - } else if (qName.equals("Allocated")) { + } else if (qName.endsWith("Allocated")) { allocated = Integer.parseInt(currentOrNull()); - } else if (qName.equals("Used")) { + } else if (qName.endsWith("Used")) { used = Integer.parseInt(currentOrNull()); - } else if (qName.equals("Limit")) { + } else if (qName.endsWith("Limit")) { limit = Integer.parseInt(currentOrNull()); - } else if (qName.equals("Overhead")) { + } else if (qName.endsWith("Overhead")) { overhead = Integer.parseInt(currentOrNull()); - } else if (qName.equals("StorageCapacity")) { + } else if (qName.endsWith("StorageCapacity")) { storageCapacity = new Capacity(units, allocated, limit, used, overhead); resetCapacity(); - } else if (qName.equals("Cpu")) { + } else if (qName.endsWith("Cpu")) { cpuCapacity = new Capacity(units, allocated, limit, used, overhead); resetCapacity(); - } else if (qName.equals("Memory")) { + } else if (qName.endsWith("Memory")) { memoryCapacity = new Capacity(units, allocated, limit, used, overhead); resetCapacity(); - } else if (qName.equals("DeployedVmsQuota")) { + } else if (qName.endsWith("DeployedVmsQuota")) { vmQuota = (int) limit; // vcloud express doesn't have the zero is unlimited rule if (vmQuota == -1) vmQuota = 0; - } else if (qName.equals("VmQuota")) { + } else if (qName.endsWith("VmQuota")) { vmQuota = Integer.parseInt(currentOrNull()); - } else if (qName.equals("NicQuota")) { + } else if (qName.endsWith("NicQuota")) { nicQuota = Integer.parseInt(currentOrNull()); - } else if (qName.equals("NetworkQuota")) { + } else if (qName.endsWith("NetworkQuota")) { networkQuota = Integer.parseInt(currentOrNull()); - } else if (qName.equals("IsEnabled")) { + } else if (qName.endsWith("IsEnabled")) { isEnabled = Boolean.parseBoolean(currentOrNull()); } currentText = new StringBuilder(); diff --git a/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/SymphonyVPDCAsyncClient.java b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/SymphonyVPDCAsyncClient.java index a0ebf6572c..2a6f8f6954 100644 --- a/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/SymphonyVPDCAsyncClient.java +++ b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/SymphonyVPDCAsyncClient.java @@ -23,26 +23,53 @@ import java.net.URI; import javax.annotation.Nullable; import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import org.jclouds.predicates.validators.DnsNameValidator; import org.jclouds.rest.annotations.EndpointParam; import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.ParamValidators; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; +import org.jclouds.savvis.vpdc.domain.SymphonyVPDCVDC; import org.jclouds.savvis.vpdc.xml.SymphonyVPDCNetworkHandler; import org.jclouds.savvis.vpdc.xml.SymphonyVPDCVAppHandler; +import org.jclouds.savvis.vpdc.xml.SymphonyVPDCVDCHandler; import org.jclouds.vcloud.CommonVCloudClient; import org.jclouds.vcloud.VCloudExpressAsyncClient; +import org.jclouds.vcloud.VCloudExpressClient; +import org.jclouds.vcloud.binders.BindCloneVAppParamsToXmlPayload; +import org.jclouds.vcloud.binders.BindInstantiateVCloudExpressVAppTemplateParamsToXmlPayload; +import org.jclouds.vcloud.domain.Catalog; +import org.jclouds.vcloud.domain.CatalogItem; import org.jclouds.vcloud.domain.Org; +import org.jclouds.vcloud.domain.Task; +import org.jclouds.vcloud.domain.TasksList; import org.jclouds.vcloud.domain.VCloudExpressVApp; -import org.jclouds.vcloud.domain.VDC; +import org.jclouds.vcloud.domain.VCloudExpressVAppTemplate; import org.jclouds.vcloud.domain.network.OrgNetwork; import org.jclouds.vcloud.filters.SetVCloudTokenCookie; +import org.jclouds.vcloud.functions.OrgNameAndCatalogNameToEndpoint; import org.jclouds.vcloud.functions.OrgNameAndVDCNameToEndpoint; +import org.jclouds.vcloud.functions.OrgNameCatalogNameItemNameToEndpoint; +import org.jclouds.vcloud.functions.OrgNameCatalogNameVAppTemplateNameToEndpoint; import org.jclouds.vcloud.functions.OrgNameToEndpoint; +import org.jclouds.vcloud.functions.OrgNameToTasksListEndpoint; import org.jclouds.vcloud.functions.OrgNameVDCNameResourceEntityNameToEndpoint; +import org.jclouds.vcloud.options.CloneVAppOptions; +import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; +import org.jclouds.vcloud.xml.CatalogHandler; +import org.jclouds.vcloud.xml.CatalogItemHandler; import org.jclouds.vcloud.xml.OrgHandler; -import org.jclouds.vcloud.xml.VDCHandler; +import org.jclouds.vcloud.xml.TaskHandler; +import org.jclouds.vcloud.xml.TasksListHandler; +import org.jclouds.vcloud.xml.VCloudExpressVAppHandler; +import org.jclouds.vcloud.xml.VCloudExpressVAppTemplateHandler; import com.google.common.util.concurrent.ListenableFuture; @@ -59,86 +86,278 @@ public interface SymphonyVPDCAsyncClient extends VCloudExpressAsyncClient { /** * {@inheritDoc} */ - // savvis doesn't work with accept header - @Override @GET + // no accept header @XMLResponseParser(OrgHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override ListenableFuture findOrgNamed( - @Nullable @EndpointParam(parser = OrgNameToEndpoint.class) String orgName); + @Nullable @EndpointParam(parser = OrgNameToEndpoint.class) String orgName); /** * {@inheritDoc} */ - // savvis doesn't work with accept header - @Override + @GET + // no accept header @XMLResponseParser(OrgHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override ListenableFuture getOrg(@EndpointParam URI orgId); - + /** * @see CommonVCloudClient#getVDC(URI) */ @GET - @XMLResponseParser(VDCHandler.class) + // no accept header + @XMLResponseParser(SymphonyVPDCVDCHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture getVDC(@EndpointParam URI vdc); + @Override + ListenableFuture getVDC(@EndpointParam URI vdc); /** * @see CommonVCloudClient#findVDCInOrgNamed(String, String) */ @GET - @XMLResponseParser(VDCHandler.class) + // no accept header + @XMLResponseParser(SymphonyVPDCVDCHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture findVDCInOrgNamed( - @Nullable @EndpointParam(parser = OrgNameAndVDCNameToEndpoint.class) String orgName, - @Nullable @EndpointParam(parser = OrgNameAndVDCNameToEndpoint.class) String vdcName); - - /** - * @see CommonVCloudClient#findNetworkInOrgVDCNamed - *//* - @GET - @XMLResponseParser(TerremarkOrgNetworkFromTerremarkVCloudExpressNetworkHandler.class) - @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture findNetworkInOrgVDCNamed( - @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String orgName, - @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String catalogName, - @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String networkName); + @Override + ListenableFuture findVDCInOrgNamed( + @Nullable @EndpointParam(parser = OrgNameAndVDCNameToEndpoint.class) String orgName, + @Nullable @EndpointParam(parser = OrgNameAndVDCNameToEndpoint.class) String vdcName); - *//** - * @see CommonVCloudClient#getNetwork - *//* - @GET - @XMLResponseParser(TerremarkOrgNetworkFromTerremarkVCloudExpressNetworkHandler.class) - @ExceptionParser(ReturnNullOnNotFoundOr404.class) - ListenableFuture getNetwork(@EndpointParam URI network);*/ - /** * @see CommonVCloudClient#findNetworkInOrgVDCNamed */ @GET + // no accept header @XMLResponseParser(SymphonyVPDCNetworkHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override ListenableFuture findNetworkInOrgVDCNamed( - @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String orgName, - @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String catalogName, - @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String networkName); + @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String orgName, + @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String catalogName, + @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String networkName); /** * @see CommonVCloudClient#getNetwork */ @GET + // no accept header @XMLResponseParser(SymphonyVPDCNetworkHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override ListenableFuture getNetwork(@EndpointParam URI network); - + /** * @see VCloudClient#getVApp */ @GET + // no accept header @XMLResponseParser(SymphonyVPDCVAppHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override ListenableFuture getVApp(@EndpointParam URI vApp); - + + /** + * @see VCloudClient#getVAppTemplate + */ + @GET + // no accept header + @XMLResponseParser(VCloudExpressVAppTemplateHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override + ListenableFuture getVAppTemplate(@EndpointParam URI vAppTemplate); + + /** + * @see VCloudClient#findVAppTemplateInOrgCatalogNamed + */ + @GET + // no accept header + @XMLResponseParser(VCloudExpressVAppTemplateHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override + ListenableFuture findVAppTemplateInOrgCatalogNamed( + @Nullable @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String orgName, + @Nullable @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String catalogName, + @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String itemName); + + /** + * @see VCloudExpressClient#instantiateVAppTemplateInVDC + */ + @POST + @Path("/action/instantiateVAppTemplate") + @Produces("application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml") + // no accept header + @XMLResponseParser(VCloudExpressVAppHandler.class) + @MapBinder(BindInstantiateVCloudExpressVAppTemplateParamsToXmlPayload.class) + @Override + ListenableFuture instantiateVAppTemplateInVDC(@EndpointParam URI vdc, + @PayloadParam("template") URI template, + @PayloadParam("name") @ParamValidators(DnsNameValidator.class) String appName, + InstantiateVAppTemplateOptions... options); + + /** + * @see VCloudExpressClient#cloneVAppInVDC + */ + @POST + @Path("/action/cloneVApp") + @Produces("application/vnd.vmware.vcloud.cloneVAppParams+xml") + // no accept header + @XMLResponseParser(TaskHandler.class) + @MapBinder(BindCloneVAppParamsToXmlPayload.class) + @Override + ListenableFuture cloneVAppInVDC(@EndpointParam URI vdc, @PayloadParam("vApp") URI toClone, + @PayloadParam("newName") @ParamValidators(DnsNameValidator.class) String newName, CloneVAppOptions... options); + + /** + * @see VCloudClient#findVAppInOrgVDCNamed + */ + @GET + // no accept header + @XMLResponseParser(VCloudExpressVAppHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override + ListenableFuture findVAppInOrgVDCNamed( + @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String orgName, + @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String catalogName, + @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String vAppName); + + /** + * @see CommonVCloudClient#deployVApp + */ + @POST + // no accept header + @Path("/action/deploy") + @XMLResponseParser(TaskHandler.class) + @Override + ListenableFuture deployVApp(@EndpointParam URI vAppId); + + /** + * @see CommonVCloudClient#undeployVApp + */ + @POST + // no accept header + @Path("/action/undeploy") + @XMLResponseParser(TaskHandler.class) + @Override + ListenableFuture undeployVApp(@EndpointParam URI vAppId); + + /** + * @see CommonVCloudClient#powerOnVApp + */ + @POST + // no accept header + @Path("/power/action/powerOn") + @XMLResponseParser(TaskHandler.class) + @Override + ListenableFuture powerOnVApp(@EndpointParam URI vAppId); + + /** + * @see CommonVCloudClient#powerOffVApp + */ + @POST + // no accept header + @Path("/power/action/powerOff") + @XMLResponseParser(TaskHandler.class) + @Override + ListenableFuture powerOffVApp(@EndpointParam URI vAppId); + + /** + * @see CommonVCloudClient#resetVApp + */ + @POST + // no accept header + @Path("/power/action/reset") + @XMLResponseParser(TaskHandler.class) + @Override + ListenableFuture resetVApp(@EndpointParam URI vAppId); + + /** + * @see CommonVCloudClient#suspendVApp + */ + @POST + // no accept header + @Path("/power/action/suspend") + @XMLResponseParser(TaskHandler.class) + @Override + ListenableFuture suspendVApp(@EndpointParam URI vAppId); + + /** + * @see CommonVCloudClient#getCatalog + */ + @GET + @XMLResponseParser(CatalogHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + // no accept header + @Override + ListenableFuture getCatalog(@EndpointParam URI catalogId); + + /** + * @see CommonVCloudClient#findCatalogInOrgNamed + */ + @GET + @XMLResponseParser(CatalogHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + // no accept header + @Override + ListenableFuture findCatalogInOrgNamed( + @Nullable @EndpointParam(parser = OrgNameAndCatalogNameToEndpoint.class) String orgName, + @Nullable @EndpointParam(parser = OrgNameAndCatalogNameToEndpoint.class) String catalogName); + + /** + * @see CommonVCloudClient#getCatalogItem + */ + @GET + // no accept header + @XMLResponseParser(CatalogItemHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override + ListenableFuture getCatalogItem(@EndpointParam URI catalogItem); + + /** + * @see CommonVCloudClient#getCatalogItemInOrg + */ + @GET + // no accept header + @XMLResponseParser(CatalogItemHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override + ListenableFuture findCatalogItemInOrgCatalogNamed( + @Nullable @EndpointParam(parser = OrgNameCatalogNameItemNameToEndpoint.class) String orgName, + @Nullable @EndpointParam(parser = OrgNameCatalogNameItemNameToEndpoint.class) String catalogName, + @EndpointParam(parser = OrgNameCatalogNameItemNameToEndpoint.class) String itemName); + + /** + * @see CommonVCloudClient#getTasksList + */ + @GET + // no accept header + @XMLResponseParser(TasksListHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override + ListenableFuture getTasksList(@EndpointParam URI tasksListId); + + /** + * @see CommonVCloudClient#findTasksListInOrgNamed + */ + @GET + // no accept header + @XMLResponseParser(TasksListHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override + ListenableFuture findTasksListInOrgNamed( + @Nullable @EndpointParam(parser = OrgNameToTasksListEndpoint.class) String orgName); + + /** + * @see CommonVCloudClient#getTask + */ + @GET + // no accept header + @XMLResponseParser(TaskHandler.class) + @ExceptionParser(ReturnNullOnNotFoundOr404.class) + @Override + ListenableFuture getTask(@EndpointParam URI taskId); + } diff --git a/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/SymphonyVPDCClient.java b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/SymphonyVPDCClient.java index 04a012f2ad..236c990cb3 100644 --- a/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/SymphonyVPDCClient.java +++ b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/SymphonyVPDCClient.java @@ -19,9 +19,11 @@ package org.jclouds.savvis.vpdc; +import java.net.URI; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; +import org.jclouds.savvis.vpdc.domain.SymphonyVPDCVDC; import org.jclouds.vcloud.VCloudExpressClient; /** @@ -33,5 +35,9 @@ import org.jclouds.vcloud.VCloudExpressClient; */ @Timeout(duration = 300, timeUnit = TimeUnit.SECONDS) public interface SymphonyVPDCClient extends VCloudExpressClient { + @Override + SymphonyVPDCVDC findVDCInOrgNamed(String orgName, String vdcName); + @Override + SymphonyVPDCVDC getVDC(URI vdc); } \ No newline at end of file diff --git a/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/domain/SymphonyVPDCVDC.java b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/domain/SymphonyVPDCVDC.java new file mode 100644 index 0000000000..7c8913dbf5 --- /dev/null +++ b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/domain/SymphonyVPDCVDC.java @@ -0,0 +1,34 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.savvis.vpdc.domain; + +import org.jclouds.savvis.vpdc.domain.internal.SymphonyVPDCVDCImpl; +import org.jclouds.vcloud.domain.ReferenceType; +import org.jclouds.vcloud.domain.VDC; + +import com.google.inject.ImplementedBy; + +/** + * @author Adrian Cole + */ +@ImplementedBy(SymphonyVPDCVDCImpl.class) +public interface SymphonyVPDCVDC extends VDC { + String getOfferingTag(); +} \ No newline at end of file diff --git a/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/domain/internal/SymphonyVPDCVDCImpl.java b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/domain/internal/SymphonyVPDCVDCImpl.java new file mode 100644 index 0000000000..5a8d829b7f --- /dev/null +++ b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/domain/internal/SymphonyVPDCVDCImpl.java @@ -0,0 +1,75 @@ +package org.jclouds.savvis.vpdc.domain.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URI; +import java.util.Map; + +import javax.annotation.Nullable; + +import org.jclouds.savvis.vpdc.domain.SymphonyVPDCVDC; +import org.jclouds.vcloud.domain.AllocationModel; +import org.jclouds.vcloud.domain.Capacity; +import org.jclouds.vcloud.domain.ReferenceType; +import org.jclouds.vcloud.domain.Task; +import org.jclouds.vcloud.domain.VDCStatus; +import org.jclouds.vcloud.domain.internal.VDCImpl; + +/** + * Locations of resources in SymphonyVPDC vDC + * + * @author Adrian Cole + * + */ +public class SymphonyVPDCVDCImpl extends VDCImpl implements SymphonyVPDCVDC { + private final String offeringTag; + + /** The serialVersionUID */ + private static final long serialVersionUID = 8464716396538298809L; + + public SymphonyVPDCVDCImpl(String name, String type, URI id, VDCStatus status, ReferenceType org, + @Nullable String description, Iterable tasks, AllocationModel allocationModel, + @Nullable Capacity storageCapacity, @Nullable Capacity cpuCapacity, @Nullable Capacity memoryCapacity, + Map resourceEntities, Map availableNetworks, int nicQuota, + int networkQuota, int vmQuota, boolean isEnabled, String offeringTag) { + super(name, type, id, status, org, description, tasks, allocationModel, storageCapacity, cpuCapacity, + memoryCapacity, resourceEntities, availableNetworks, nicQuota, networkQuota, vmQuota, isEnabled); + this.offeringTag = checkNotNull(offeringTag, "offeringTag"); + } + + @Override + public String getOfferingTag() { + return offeringTag; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((offeringTag == null) ? 0 : offeringTag.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + SymphonyVPDCVDCImpl other = (SymphonyVPDCVDCImpl) obj; + if (offeringTag == null) { + if (other.offeringTag != null) + return false; + } else if (!offeringTag.equals(other.offeringTag)) + return false; + return true; + } + + @Override + public String toString() { + return "[id=" + getHref() + ", name=" + getName() + ", description=" + getDescription() + ", offeringTag=" + + offeringTag + "]"; + } +} \ No newline at end of file diff --git a/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/xml/SymphonyVPDCVDCHandler.java b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/xml/SymphonyVPDCVDCHandler.java new file mode 100644 index 0000000000..b1295f29c6 --- /dev/null +++ b/sandbox-providers/savvis-symphony-vpdc/src/main/java/org/jclouds/savvis/vpdc/xml/SymphonyVPDCVDCHandler.java @@ -0,0 +1,58 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.savvis.vpdc.xml; + +import javax.inject.Inject; + +import org.jclouds.savvis.vpdc.domain.SymphonyVPDCVDC; +import org.jclouds.savvis.vpdc.domain.internal.SymphonyVPDCVDCImpl; +import org.jclouds.vcloud.domain.VDC; +import org.jclouds.vcloud.xml.TaskHandler; +import org.jclouds.vcloud.xml.VDCHandler; + +/** + * @author Adrian Cole + */ +public class SymphonyVPDCVDCHandler extends VDCHandler { + + @Inject + public SymphonyVPDCVDCHandler(TaskHandler taskHandler) { + super(taskHandler); + } + + private String offeringTag; + + public SymphonyVPDCVDC getResult() { + VDC vDC = super.getResult(); + return new SymphonyVPDCVDCImpl(vDC.getName(), vDC.getType(), vDC.getHref(), status, org, description, tasks, + allocationModel, storageCapacity, cpuCapacity, memoryCapacity, resourceEntities, availableNetworks, + nicQuota, networkQuota, vmQuota, isEnabled, offeringTag); + } + + @Override + public void endElement(String uri, String name, String qName) { + if (qName.endsWith("OfferingTag")) { + this.offeringTag = currentOrNull(); + } + super.endElement(uri, name, qName); + + } + +} diff --git a/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/SymphonyVPDCAsyncClientTest.java b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/SymphonyVPDCAsyncClientTest.java index 79c6e9c0f8..3a7b8a5e2e 100644 --- a/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/SymphonyVPDCAsyncClientTest.java +++ b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/SymphonyVPDCAsyncClientTest.java @@ -50,9 +50,9 @@ import org.jclouds.rest.internal.RestAnnotationProcessor; import org.jclouds.savvis.vpdc.config.SymphonyVPDCRestClientModule; import org.jclouds.savvis.vpdc.xml.SymphonyVPDCNetworkHandler; import org.jclouds.savvis.vpdc.xml.SymphonyVPDCVAppHandler; +import org.jclouds.savvis.vpdc.xml.SymphonyVPDCVDCHandler; import org.jclouds.util.Strings2; import org.jclouds.vcloud.CommonVCloudClient; -import org.jclouds.vcloud.VCloudExpressAsyncClient; import org.jclouds.vcloud.VCloudExpressLoginAsyncClient; import org.jclouds.vcloud.VCloudExpressMediaType; import org.jclouds.vcloud.VCloudVersionsAsyncClient; @@ -77,7 +77,6 @@ import org.jclouds.vcloud.xml.TaskHandler; import org.jclouds.vcloud.xml.TasksListHandler; import org.jclouds.vcloud.xml.VCloudExpressVAppHandler; import org.jclouds.vcloud.xml.VCloudExpressVAppTemplateHandler; -import org.jclouds.vcloud.xml.VDCHandler; import org.testng.annotations.Test; import com.google.common.base.Supplier; @@ -104,7 +103,7 @@ public class SymphonyVPDCAsyncClientTest extends RestClientTest of(new Log4JLoggingModule()), overrides).getProviderSpecificContext(); + Injector injector = new RestContextFactory(restProperties).createContextBuilder(provider, + ImmutableSet. of(new Log4JLoggingModule(), new JschSshClientModule()), overrides).buildInjector(); + + connection = injector.getInstance(SymphonyVPDCClient.class); + + sshFactory = injector.getInstance(SshClient.Factory.class); + socketTester = new RetryablePredicate(injector.getInstance(SocketOpen.class), 130, 10, TimeUnit.SECONDS);// make + // it + // longer + // then + // default internet + // service timeout + successTester = new RetryablePredicate(injector.getInstance(TaskSuccess.class), 650, 10, TimeUnit.SECONDS); + } + + protected String expectedOs = "Ubuntu Linux (64-bit)"; + protected String itemName = "Ubuntu JeOS 9.10 (64-bit)"; + + protected Factory sshFactory; + private VCloudExpressVApp vApp; + private RetryablePredicate socketTester; + private RetryablePredicate successTester; + private VCloudExpressVApp clone; + private VDC vdc; + public static final String PREFIX = System.getProperty("user.name") + "-savvis"; + + @Test(enabled = true) + public void testInstantiateAndPowerOn() throws InterruptedException, ExecutionException, TimeoutException, + IOException { + prepare(); + StringBuffer name = new StringBuffer(); + for (int i = 0; i < 15; i++) + name.append("c"); + String serverName = name.toString();// "adriantest"; + + long hardDisk = 4194304; + + // long hardDisk = 4194304 / 4 * 10; + // String catalogOs = "CentOS 5.3 (64-bit)"; + // String expectedOs = "Red Hat Enterprise Linux 5 (64-bit)"; + + // lookup the datacenter you are deploying into + vdc = connection.findVDCInOrgNamed(null, null); + + ReferenceType vAppTemplateId = Iterables.find(vdc.getResourceEntities().values(), new Predicate() { + + @Override + public boolean apply(ReferenceType arg0) { + return arg0.getType().equals(VCloudMediaType.VAPPTEMPLATE_XML); + } + + }); + + VCloudExpressVAppTemplate vAppTemplate = connection.getVAppTemplate(vAppTemplateId.getHref()); + + assert vAppTemplate != null; + + // create an options object to collect the configuration we want. + InstantiateVAppTemplateOptions instantiateOptions = new InstantiateVAppTemplateOptions(); + + // instantiate, noting vApp returned has minimal details + vApp = connection.instantiateVAppTemplateInVDC(vdc.getHref(), vAppTemplate.getHref(), serverName, + instantiateOptions); + + assertEquals(vApp.getStatus(), Status.RESOLVED); + + Task deployTask = connection.deployVApp(vApp.getHref()); + + // check to see the result of calling deploy twice + deployTask = connection.deployVApp(vApp.getHref()); + assertEquals(deployTask.getHref(), deployTask.getHref()); + + vApp = connection.getVApp(vApp.getHref()); + + assertEquals(vApp.getStatus(), Status.RESOLVED); + + try {// per docs, this is not supported + connection.cancelTask(deployTask.getHref()); + } catch (UnsupportedOperationException e) { + } + + assert successTester.apply(deployTask.getHref()); + System.out.printf("%d: done deploying vApp%n", System.currentTimeMillis()); + + vApp = connection.getVApp(vApp.getHref()); + + ReferenceType vAppResource = connection.findVDCInOrgNamed(null, null).getResourceEntities().get(serverName); + assertEquals(vAppResource.getHref(), vApp.getHref()); + + int processorCount = 1; + long memory = 512; + verifyConfigurationOfVApp(vApp, serverName, expectedOs, processorCount, memory, hardDisk); + assertEquals(vApp.getStatus(), Status.OFF); + + assert successTester.apply(connection.powerOnVApp(vApp.getHref()).getHref()); + System.out.printf("%d: done powering on vApp%n", System.currentTimeMillis()); + + vApp = connection.getVApp(vApp.getHref()); + assertEquals(vApp.getStatus(), Status.ON); + } + + protected void prepare() { + + } + + @Test(enabled = true, dependsOnMethods = "testInstantiateAndPowerOn") + public void testCloneVApp() throws IOException { + assert successTester.apply(connection.powerOffVApp(vApp.getHref()).getHref()); + System.out.printf("%d: done powering off vApp%n", System.currentTimeMillis()); + + StringBuffer name = new StringBuffer(); + for (int i = 0; i < 15; i++) + name.append("b"); + String newName = name.toString(); + + CloneVAppOptions options = deploy().powerOn().withDescription("The description of " + newName); + + System.out.printf("%d: cloning vApp%n", System.currentTimeMillis()); + Task task = connection.cloneVAppInVDC(vdc.getHref(), vApp.getHref(), newName, options); + + // wait for the task to complete + assert successTester.apply(task.getHref()); + System.out.printf("%d: done cloning vApp%n", System.currentTimeMillis()); + + assert successTester.apply(connection.powerOnVApp(vApp.getHref()).getHref()); + System.out.printf("%d: done powering on vApp%n", System.currentTimeMillis()); + + // refresh task to get the new vApp location + task = connection.getTask(task.getHref()); + + clone = connection.getVApp(task.getOwner().getHref()); + assertEquals(clone.getStatus(), Status.ON); + + assertEquals(clone.getName(), newName); + assertEquals(clone.getNetworkToAddresses().values().size(), 1); + } + + private void loopAndCheckPass(String publicIp) throws IOException { + for (int i = 0; i < 5; i++) {// retry loop TODO replace with predicate. + try { + doCheckPass(publicIp); + return; + } catch (SshException e) { + try { + Thread.sleep(10 * 1000); + } catch (InterruptedException e1) { + } + continue; + } + } + } + + @Test(enabled = true, dependsOnMethods = "testCloneVApp") + public void testLifeCycle() throws InterruptedException, ExecutionException, TimeoutException, IOException { + + try {// per docs, this is not supported + connection.undeployVApp(vApp.getHref()); + assert false; + } catch (UnsupportedOperationException e) { + } + + try {// per docs, this is not supported + connection.suspendVApp(vApp.getHref()); + assert false; + } catch (UnsupportedOperationException e) { + } + + assert successTester.apply(connection.resetVApp(vApp.getHref()).getHref()); + + vApp = connection.getVApp(vApp.getHref()); + + assertEquals(vApp.getStatus(), Status.ON); + + // TODO we need to determine whether shutdown is supported before invoking + // it. + // connection.shutdownVApp(vApp.getId()); + // vApp = connection.getVApp(vApp.getId()); + // assertEquals(vApp.getStatus(), VAppStatus.ON); + + assert successTester.apply(connection.powerOffVApp(vApp.getHref()).getHref()); + + vApp = connection.getVApp(vApp.getHref()); + assertEquals(vApp.getStatus(), Status.OFF); + } + + private void verifyConfigurationOfVApp(VCloudExpressVApp vApp, String serverName, String expectedOs, + int processorCount, long memory, long hardDisk) { + assertEquals(vApp.getName(), serverName); + assertEquals(vApp.getOperatingSystemDescription(), expectedOs); + assertEquals(find(vApp.getResourceAllocations(), resourceType(ResourceType.PROCESSOR)).getVirtualQuantity(), + processorCount); + assertEquals( + find(vApp.getResourceAllocations(), resourceType(ResourceType.SCSI_CONTROLLER)).getVirtualQuantity(), 1); + assertEquals(find(vApp.getResourceAllocations(), resourceType(ResourceType.MEMORY)).getVirtualQuantity(), memory); + assertEquals(find(vApp.getResourceAllocations(), resourceType(ResourceType.DISK_DRIVE)).getVirtualQuantity(), + hardDisk); + assertEquals(vApp.getSize().longValue(), + find(vApp.getResourceAllocations(), resourceType(ResourceType.DISK_DRIVE)).getVirtualQuantity()); + } + + private void doCheckPass(String address) throws IOException { + IPSocket socket = new IPSocket(address, 22); + + System.out.printf("%d: %s awaiting ssh service to start%n", System.currentTimeMillis(), socket); + assert socketTester.apply(socket); + System.out.printf("%d: %s ssh service started%n", System.currentTimeMillis(), socket); + + SshClient connection = getConnectionFor(socket); + try { + connection.connect(); + System.out.printf("%d: %s ssh connection made%n", System.currentTimeMillis(), socket); + System.out.println(connection.exec("df -h")); + } finally { + if (connection != null) + connection.disconnect(); + } + } + + protected SshClient getConnectionFor(IPSocket socket) { + // TODO add in the correct login credentials for the vApp template + return sshFactory.create(socket, new Credentials("root", "password")); + } + + @AfterTest + void cleanup() throws InterruptedException, ExecutionException, TimeoutException { + if (vApp != null) { + try { + successTester.apply(connection.powerOffVApp(vApp.getHref()).getHref()); + } catch (Exception e) { + + } + connection.deleteVApp(vApp.getHref()); + } + if (clone != null) { + try { + successTester.apply(connection.powerOffVApp(clone.getHref()).getHref()); + } catch (Exception e) { + + } + connection.deleteVApp(clone.getHref()); + } - connection = context.getApi(); } } \ No newline at end of file diff --git a/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/compute/SymphonyVPDCComputeServiceLiveTest.java b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/compute/SymphonyVPDCComputeServiceLiveTest.java index 34fa9efc0b..f269cb8723 100644 --- a/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/compute/SymphonyVPDCComputeServiceLiveTest.java +++ b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/compute/SymphonyVPDCComputeServiceLiveTest.java @@ -32,7 +32,7 @@ import org.testng.annotations.Test; * * @author Kedar Dave */ -@Test(groups = "live", enabled = true, sequential = true) +@Test(groups = "live", enabled = false, sequential = true) public class SymphonyVPDCComputeServiceLiveTest extends BaseComputeServiceLiveTest { public SymphonyVPDCComputeServiceLiveTest() { provider = "savvis-symphony-vpdc"; diff --git a/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/compute/SymphonyVPDCTemplateBuilderLiveTest.java b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/compute/SymphonyVPDCTemplateBuilderLiveTest.java index 21082ea684..30cd9d297c 100644 --- a/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/compute/SymphonyVPDCTemplateBuilderLiveTest.java +++ b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/compute/SymphonyVPDCTemplateBuilderLiveTest.java @@ -28,7 +28,7 @@ import com.google.inject.Module; * * @author Adrian Cole */ -@Test(groups = "live") +@Test(groups = "live", enabled = false) public class SymphonyVPDCTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest { public SymphonyVPDCTemplateBuilderLiveTest() { diff --git a/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/xml/OrgHandlerTest.java b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/xml/OrgHandlerTest.java new file mode 100644 index 0000000000..3725b4cacb --- /dev/null +++ b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/xml/OrgHandlerTest.java @@ -0,0 +1,66 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.savvis.vpdc.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.net.URI; + +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ParseSax.Factory; +import org.jclouds.http.functions.config.SaxParserModule; +import org.jclouds.vcloud.domain.Org; +import org.jclouds.vcloud.domain.ReferenceType; +import org.jclouds.vcloud.domain.Task; +import org.jclouds.vcloud.domain.internal.OrgImpl; +import org.jclouds.vcloud.domain.internal.ReferenceTypeImpl; +import org.jclouds.vcloud.xml.OrgHandler; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * Tests behavior of {@code OrgHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class OrgHandlerTest { + + public void testSavvis() { + InputStream is = getClass().getResourceAsStream("/savvis/org.xml"); + Injector injector = Guice.createInjector(new SaxParserModule()); + Factory factory = injector.getInstance(ParseSax.Factory.class); + Org result = (Org) factory.create(injector.getInstance(OrgHandler.class)).parse(is); + assertEquals( + result, + new OrgImpl("100000.0", null, null, "100000.0", "SAVVISStation Integration Testing", ImmutableMap + . of(), ImmutableMap. of( + "demo_vpdcname", + new ReferenceTypeImpl("demo_vpdcname", "application/vnd.vmware.vcloud.vdc+xml", URI + .create("https://api.sandbox.symphonyvpdc.savvis.net/rest/api/v0.8/org/100000.0/vdc/2736"))), + ImmutableMap. of(), null, ImmutableSet. of())); + + } +} diff --git a/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/xml/SymphonyVPDCVDCHandlerTest.java b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/xml/SymphonyVPDCVDCHandlerTest.java new file mode 100644 index 0000000000..9a19945b85 --- /dev/null +++ b/sandbox-providers/savvis-symphony-vpdc/src/test/java/org/jclouds/savvis/vpdc/xml/SymphonyVPDCVDCHandlerTest.java @@ -0,0 +1,93 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.savvis.vpdc.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.net.URI; + +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ParseSax.Factory; +import org.jclouds.http.functions.config.SaxParserModule; +import org.jclouds.savvis.vpdc.domain.SymphonyVPDCVDC; +import org.jclouds.vcloud.VCloudMediaType; +import org.jclouds.vcloud.domain.ReferenceType; +import org.jclouds.vcloud.domain.internal.ReferenceTypeImpl; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * Tests behavior of {@code SymphonyVPDCVDCHandler} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class SymphonyVPDCVDCHandlerTest { + + public void test() { + InputStream is = getClass().getResourceAsStream("/savvis/vdc.xml"); + Injector injector = Guice.createInjector(new SaxParserModule()); + Factory factory = injector.getInstance(ParseSax.Factory.class); + SymphonyVPDCVDC result = (SymphonyVPDCVDC) factory.create(injector.getInstance(SymphonyVPDCVDCHandler.class)) + .parse(is); + assertEquals(result.getName(), "demo_vpdcname"); + assertEquals(result.getHref(), null); + assertEquals(result.getDescription(), + "ServiceProfileName = Balanced; ServiceLocation = North America; Email = jim@company.com;"); + assertEquals(result.getOfferingTag(), "Deployed"); + assertEquals(result.getStorageCapacity(), null); + assertEquals(result.getCpuCapacity(), null); + assertEquals(result.getMemoryCapacity(), null); + assertEquals(result.getVmQuota(), 0); + assertEquals( + result.getResourceEntities(), + ImmutableMap. of( + "DemoHost-1", + new ReferenceTypeImpl( + "DemoHost-1", + VCloudMediaType.VAPP_XML, + URI.create("https://api.sandbox.symphonyvpdc.savvis.net/rest/api/v0.8/org/100000.0/vdc/2736/vApp/1001")), + "DemoHost-2", + new ReferenceTypeImpl( + "DemoHost-2", + VCloudMediaType.VAPP_XML, + URI.create("https://api.sandbox.symphonyvpdc.savvis.net/rest/api/v0.8/org/100000.0/vdc/2736/vApp/1002")), + "DemoHost-3", + new ReferenceTypeImpl( + "DemoHost-3", + VCloudMediaType.VAPP_XML, + URI.create("https://api.sandbox.symphonyvpdc.savvis.net/rest/api/v0.8/org/100000.0/vdc/2736/vApp/1003")), + "CustomerTemplateName", + new ReferenceTypeImpl( + "CustomerTemplateName", + VCloudMediaType.VAPPTEMPLATE_XML, + URI.create("https://api.sandbox.symphonyvpdc.savvis.net/rest/api/v0.8/org/100000.0/vdc/2736/vAppTemplate/1234")), + "firewall", + new ReferenceTypeImpl( + "firewall", + "api.sandbox.symphonyVPDC.savvis.net+xml", + URI.create("https://api.sandbox.symphonyvpdc.savvis.net/rest/api/v0.8/org/100000.0/vdc/2736/FirewallService")))); + assertEquals(result.getAvailableNetworks(), ImmutableMap.of()); + } +} diff --git a/sandbox-providers/savvis-symphony-vpdc/src/test/resources/savvis/org.xml b/sandbox-providers/savvis-symphony-vpdc/src/test/resources/savvis/org.xml new file mode 100644 index 0000000000..22f18b1b1f --- /dev/null +++ b/sandbox-providers/savvis-symphony-vpdc/src/test/resources/savvis/org.xml @@ -0,0 +1,5 @@ + + + + SAVVISStation Integration Testing + \ No newline at end of file diff --git a/sandbox-providers/savvis-symphony-vpdc/src/test/resources/savvis/vdc.xml b/sandbox-providers/savvis-symphony-vpdc/src/test/resources/savvis/vdc.xml new file mode 100644 index 0000000000..57d28ef190 --- /dev/null +++ b/sandbox-providers/savvis-symphony-vpdc/src/test/resources/savvis/vdc.xml @@ -0,0 +1,13 @@ + + + ServiceProfileName = Balanced; ServiceLocation = North America; Email = jim@company.com; + Deployed + + + + + + + + + \ No newline at end of file From 9117644f26999c937c844d20035d24255bd903e2 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Fri, 4 Mar 2011 22:58:39 -0500 Subject: [PATCH 02/13] Issue 495:vcloud: NullPointerExceptions when a VM has no IP addresses --- .../compute/functions/VAppToNodeMetadata.java | 14 +- .../compute/util/VCloudComputeUtils.java | 32 +-- .../FindLocationForResourceTest.java | 67 +++++ .../functions/VAppToNodeMetadataTest.java | 148 +++++++++++ .../jclouds/vcloud/xml/VAppHandlerTest.java | 7 +- apis/vcloud/src/test/resources/log4j.xml | 170 +++++++++++++ apis/vcloud/src/test/resources/vapp-none.xml | 237 ++++++++++++++++++ apis/vcloud/src/test/resources/vapp-pool.xml | 227 +++++++++++++++++ ...VCloudComputeServiceContextModuleTest.java | 2 +- ...xpressComputeServiceContextModuleTest.java | 2 +- ...mmonVCloudComputeServiceContextModule.java | 18 +- .../functions/FindLocationForResource.java | 3 +- .../java/org/jclouds/util/InetAddresses2.java | 47 ++-- 13 files changed, 923 insertions(+), 51 deletions(-) create mode 100644 apis/vcloud/src/test/java/org/jclouds/vcloud/compute/functions/FindLocationForResourceTest.java create mode 100644 apis/vcloud/src/test/java/org/jclouds/vcloud/compute/functions/VAppToNodeMetadataTest.java create mode 100644 apis/vcloud/src/test/resources/log4j.xml create mode 100644 apis/vcloud/src/test/resources/vapp-none.xml create mode 100644 apis/vcloud/src/test/resources/vapp-pool.xml diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/functions/VAppToNodeMetadata.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/functions/VAppToNodeMetadata.java index a60128c890..9faac756f1 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/functions/VAppToNodeMetadata.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/functions/VAppToNodeMetadata.java @@ -20,13 +20,15 @@ package org.jclouds.vcloud.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.Iterables.filter; import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName; import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.getCredentialsFrom; -import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.getPrivateIpsFromVApp; -import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.getPublicIpsFromVApp; +import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.getIpsFromVApp; import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.toComputeOs; import java.util.Map; +import java.util.Set; import javax.annotation.Resource; import javax.inject.Inject; @@ -38,6 +40,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder; import org.jclouds.compute.domain.NodeState; import org.jclouds.domain.Credentials; import org.jclouds.logging.Logger; +import org.jclouds.util.InetAddresses2.IsPrivateIPAddress; import org.jclouds.vcloud.domain.Status; import org.jclouds.vcloud.domain.VApp; @@ -58,7 +61,7 @@ public class VAppToNodeMetadata implements Function { @Inject protected VAppToNodeMetadata(Map vAppStatusToNodeState, Map credentialStore, - FindLocationForResource findLocationForResourceInVDC, Function hardwareForVApp) { + FindLocationForResource findLocationForResourceInVDC, Function hardwareForVApp) { this.hardwareForVApp = checkNotNull(hardwareForVApp, "hardwareForVApp"); this.findLocationForResourceInVDC = checkNotNull(findLocationForResourceInVDC, "findLocationForResourceInVDC"); this.credentialStore = checkNotNull(credentialStore, "credentialStore"); @@ -75,8 +78,9 @@ public class VAppToNodeMetadata implements Function { builder.operatingSystem(toComputeOs(from, null)); builder.hardware(hardwareForVApp.apply(from)); builder.state(vAppStatusToNodeState.get(from.getStatus())); - builder.publicAddresses(getPublicIpsFromVApp(from)); - builder.privateAddresses(getPrivateIpsFromVApp(from)); + Set addresses = getIpsFromVApp(from); + builder.publicAddresses(filter(addresses, not(IsPrivateIPAddress.INSTANCE))); + builder.privateAddresses(filter(addresses, IsPrivateIPAddress.INSTANCE)); builder.credentials(getCredentialsFrom(from)); Credentials fromApi = getCredentialsFrom(from); if (fromApi != null && !credentialStore.containsKey("node#" + from.getHref().toASCIIString())) diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/util/VCloudComputeUtils.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/util/VCloudComputeUtils.java index f17fdb4e8e..9cd87d9f7d 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/util/VCloudComputeUtils.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/util/VCloudComputeUtils.java @@ -36,8 +36,9 @@ import org.jclouds.vcloud.domain.ovf.ResourceAllocation; import org.jclouds.vcloud.domain.ovf.ResourceType; import org.jclouds.vcloud.domain.ovf.VCloudNetworkAdapter; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; /** * @@ -77,7 +78,7 @@ public class VCloudComputeUtils { public static Credentials getCredentialsFrom(Vm vm) { String user = "root"; if (vm.getOperatingSystemSection() != null && vm.getOperatingSystemSection().getDescription() != null - && vm.getOperatingSystemSection().getDescription().indexOf("Windows") >= 0) + && vm.getOperatingSystemSection().getDescription().indexOf("Windows") >= 0) user = "Administrator"; String password = null; if (vm.getGuestCustomizationSection() != null) @@ -85,34 +86,33 @@ public class VCloudComputeUtils { return new Credentials(user, password); } - public static Set getPublicIpsFromVApp(VApp vApp) { - Set ips = Sets.newLinkedHashSet(); + public static Set getIpsFromVApp(VApp vApp) { // TODO make this work with composite vApps if (vApp.getChildren().size() == 0) - return ips; + return ImmutableSet.of(); + Builder ips = ImmutableSet. builder(); Vm vm = Iterables.get(vApp.getChildren(), 0); // TODO: figure out how to differentiate public from private ip addresses // assumption is that we'll do this from the network object, which may have // enough data to tell whether or not it is a public network without string - // parsing. At worst, we could have properties set per cloud provider to - // declare the networks which are public, then check against these in + // parsing. At worst, we could have properties set per cloud provider to + // declare the networks which are public, then check against these in // networkconnection.getNetwork if (vm.getNetworkConnectionSection() != null) { - for (NetworkConnection connection : vm.getNetworkConnectionSection().getConnections()) - ips.add(connection.getIpAddress()); + for (NetworkConnection connection : vm.getNetworkConnectionSection().getConnections()) { + if (connection.getIpAddress() != null) + ips.add(connection.getIpAddress()); + } } else { for (ResourceAllocation net : filter(vm.getVirtualHardwareSection().getResourceAllocations(), - resourceType(ResourceType.ETHERNET_ADAPTER))) { + resourceType(ResourceType.ETHERNET_ADAPTER))) { if (net instanceof VCloudNetworkAdapter) { VCloudNetworkAdapter vNet = VCloudNetworkAdapter.class.cast(net); - ips.add(vNet.getIpAddress()); + if (vNet.getIpAddress() != null) + ips.add(vNet.getIpAddress()); } } } - return ips; - } - - public static Set getPrivateIpsFromVApp(VApp vApp) { - return Sets.newLinkedHashSet(); + return ips.build(); } } diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/functions/FindLocationForResourceTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/functions/FindLocationForResourceTest.java new file mode 100644 index 0000000000..73dd87b5ce --- /dev/null +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/functions/FindLocationForResourceTest.java @@ -0,0 +1,67 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.vcloud.compute.functions; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.vcloud.domain.internal.ReferenceTypeImpl; +import org.testng.annotations.Test; + +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableSet; + +/** + * Tests behavior of {@code FindLocationForResource} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class FindLocationForResourceTest { + public void testMatchWhenIdIsHref() { + Location location = new LocationBuilder().id("http://foo").description("description") + .scope(LocationScope.PROVIDER).build(); + FindLocationForResource converter = new FindLocationForResource( + Suppliers.> ofInstance(ImmutableSet. of(location))); + assertEquals(converter.apply(new ReferenceTypeImpl("name", "type", URI.create("http://foo"))), location); + } + + @Test(expectedExceptions = NoSuchElementException.class) + public void testGracefulWhenHrefIsntLocationId() { + FindLocationForResource converter = new FindLocationForResource( + Suppliers.> ofInstance(ImmutableSet. of(new LocationBuilder() + .id("http://bar").description("description").scope(LocationScope.PROVIDER).build()))); + converter.apply(new ReferenceTypeImpl("name", "type", URI.create("http://foo"))); + } + + @Test(expectedExceptions = NoSuchElementException.class) + public void testGracefulWhenLocationIdIsntURI() { + FindLocationForResource converter = new FindLocationForResource( + Suppliers.> ofInstance(ImmutableSet. of(new LocationBuilder().id("1") + .description("description").scope(LocationScope.PROVIDER).build()))); + converter.apply(new ReferenceTypeImpl("name", "type", URI.create("http://foo"))); + } +} diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/functions/VAppToNodeMetadataTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/functions/VAppToNodeMetadataTest.java new file mode 100644 index 0000000000..13792ec293 --- /dev/null +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/functions/VAppToNodeMetadataTest.java @@ -0,0 +1,148 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.vcloud.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.Set; + +import javax.inject.Singleton; + +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeState; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ParseSax.Factory; +import org.jclouds.http.functions.config.SaxParserModule; +import org.jclouds.vcloud.VCloudPropertiesBuilder; +import org.jclouds.vcloud.compute.config.CommonVCloudComputeServiceContextModule; +import org.jclouds.vcloud.domain.Status; +import org.jclouds.vcloud.domain.VApp; +import org.jclouds.vcloud.xml.VAppHandler; +import org.testng.annotations.Test; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; + +/** + * Tests behavior of {@code VAppToNodeMetadata} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class VAppToNodeMetadataTest { + + public Injector createInjectorWithLocation(final Location location) { + return Guice.createInjector(new SaxParserModule(), new AbstractModule() { + + @Override + protected void configure() { + Properties props = new Properties(); + Names.bindProperties(binder(), checkNotNull(new VCloudPropertiesBuilder(props).build(), "properties")); + bind(new TypeLiteral>() { + }).to(new TypeLiteral() { + }); + } + + @SuppressWarnings("unused") + @Memoized + @Singleton + @Provides + Supplier> supplyLocations() { + return Suppliers.> ofInstance(ImmutableSet. of(location)); + } + + @SuppressWarnings("unused") + @Singleton + @Provides + Map supplyCreds() { + return Maps.newConcurrentMap(); + } + + @SuppressWarnings("unused") + @Singleton + @Provides + protected Map provideVAppStatusToNodeState() { + return CommonVCloudComputeServiceContextModule.VAPPSTATUS_TO_NODESTATE; + } + + }); + } + + public void testWhenVDCIsLocation() { + Location location = new LocationBuilder().id("https://1.1.1.1/api/v1.0/vdc/1").description("description") + .scope(LocationScope.PROVIDER).build(); + Injector injector = createInjectorWithLocation(location); + InputStream is = getClass().getResourceAsStream("/vapp-pool.xml"); + Factory factory = injector.getInstance(ParseSax.Factory.class); + VApp result = factory.create(injector.getInstance(VAppHandler.class)).parse(is); + VAppToNodeMetadata converter = injector.getInstance(VAppToNodeMetadata.class); + NodeMetadata node = converter.apply(result); + assertEquals(node.getLocation(), location); + assertEquals(node.getPrivateAddresses(), ImmutableSet.of("172.16.7.230")); + assertEquals(node.getPublicAddresses(), ImmutableSet.of()); + } + + public void testGracefulWhenNoIPs() { + Location location = new LocationBuilder().id("https://1.1.1.1/api/v1.0/vdc/1").description("description") + .scope(LocationScope.PROVIDER).build(); + Injector injector = createInjectorWithLocation(location); + InputStream is = getClass().getResourceAsStream("/vapp-none.xml"); + Factory factory = injector.getInstance(ParseSax.Factory.class); + VApp result = factory.create(injector.getInstance(VAppHandler.class)).parse(is); + VAppToNodeMetadata converter = injector.getInstance(VAppToNodeMetadata.class); + NodeMetadata node = converter.apply(result); + assertEquals(node.getLocation(), location); + assertEquals(node.getPrivateAddresses(), ImmutableSet.of()); + assertEquals(node.getPublicAddresses(), ImmutableSet.of()); + } + + @Test(expectedExceptions = NoSuchElementException.class) + public void testGracefulWhenVDCIsNotLocation() { + Location location = new LocationBuilder().id("https://1.1.1.1/api/v1.0/vdc/11111").description("description") + .scope(LocationScope.PROVIDER).build(); + Injector injector = createInjectorWithLocation(location); + InputStream is = getClass().getResourceAsStream("/vapp-pool.xml"); + Factory factory = injector.getInstance(ParseSax.Factory.class); + VApp result = factory.create(injector.getInstance(VAppHandler.class)).parse(is); + VAppToNodeMetadata converter = injector.getInstance(VAppToNodeMetadata.class); + NodeMetadata node = converter.apply(result); + assertEquals(node.getLocation(), location); + } +} diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/VAppHandlerTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/VAppHandlerTest.java index f2d7f47f23..523cd26429 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/VAppHandlerTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/VAppHandlerTest.java @@ -46,6 +46,7 @@ import com.google.inject.Injector; */ @Test(groups = "unit") public class VAppHandlerTest { + public void testRhelOffStatic() { InputStream is = getClass().getResourceAsStream("/vapp-rhel-off-static.xml"); Injector injector = Guice.createInjector(new SaxParserModule()); @@ -55,8 +56,10 @@ public class VAppHandlerTest { assertEquals(result.getHref(), URI.create("https://vcenterprise.bluelock.com/api/v1.0/vApp/vapp-607806320")); assertEquals(result.getType(), "application/vnd.vmware.vcloud.vApp+xml"); assertEquals(result.getStatus(), Status.OFF); - assertEquals(result.getVDC(), new ReferenceTypeImpl(null, VCloudMediaType.VDC_XML, URI - .create("https://vcenterprise.bluelock.com/api/v1.0/vdc/1014839439"))); + assertEquals( + result.getVDC(), + new ReferenceTypeImpl(null, VCloudMediaType.VDC_XML, URI + .create("https://vcenterprise.bluelock.com/api/v1.0/vdc/1014839439"))); assertEquals(result.getDescription(), null); assertEquals(result.getTasks(), ImmutableList.of()); assert result.isOvfDescriptorUploaded(); diff --git a/apis/vcloud/src/test/resources/log4j.xml b/apis/vcloud/src/test/resources/log4j.xml new file mode 100644 index 0000000000..7343ec00e2 --- /dev/null +++ b/apis/vcloud/src/test/resources/log4j.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apis/vcloud/src/test/resources/vapp-none.xml b/apis/vcloud/src/test/resources/vapp-none.xml new file mode 100644 index 0000000000..eb7e7a1f51 --- /dev/null +++ b/apis/vcloud/src/test/resources/vapp-none.xml @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + Lease settings section + + 0 + 7776000 + + + VApp startup section + + + + + The list of logical networks + + + + + This is a special place-holder used for disconnected network interfaces. + + + The configuration parameters for logical networks + + + + + + true + 172.16.7.1 + 255.255.255.0 + 208.95.232.10 + 208.95.232.11 + + + 172.16.7.230 + 172.16.7.239 + + + + + bridged + + + false + 3600 + 7200 + + 172.16.7.2 + 172.16.7.229 + + + + true + + + true + ipTranslation + allowTraffic + + + + true + + + This is a special place-holder used for disconnected network interfaces. + + + false + 196.254.254.254 + 255.255.0.0 + 196.254.254.254 + + isolated + + false + + + + + + + + + + + + + + + + + + + Virtual hardware requirements + + Virtual Hardware Family + 0 + Centos-5.5_x64 + vmx-07 + + + 00:50:56:01:02:38 + 0 + false + none + PCNet32 ethernet adapter + Network adapter 0 + 1 + PCNet32 + 10 + + + 0 + SCSI Controller + SCSI Controller 0 + 2 + lsilogic + 6 + + + 0 + Hard disk + Hard disk 1 + + 2000 + 2 + 17 + + + 0 + IDE Controller + IDE Controller 0 + 3 + 5 + + + 0 + false + CD/DVD Drive + CD/DVD Drive 1 + + 3002 + 3 + 15 + + + 0 + false + Floppy Drive + Floppy Drive 1 + + 8000 + 14 + + + hertz * 10^6 + Number of Virtual CPUs + 1 virtual CPU(s) + 4 + 0 + 3 + 1 + 0 + + + + byte * 2^20 + Memory Size + 2048 MB of memory + 5 + 0 + 4 + 2048 + 0 + + + + + + + + + + + + + + + Specifies the operating system installed + Red Hat Enterprise Linux 5 (64-bit) + + + + Specifies the available VM network connections + 0 + + 0 + false + 00:50:56:01:02:38 + NONE + + + + + Specifies Guest OS Customization Settings + true + false + 1 + false + false + true + true + secret + false + cat > /root/foo.txt<<EOF +I love candy +EOF + + Centos-5.5_x64 + + + Centos-5.5_x64 + + + diff --git a/apis/vcloud/src/test/resources/vapp-pool.xml b/apis/vcloud/src/test/resources/vapp-pool.xml new file mode 100644 index 0000000000..13d9432242 --- /dev/null +++ b/apis/vcloud/src/test/resources/vapp-pool.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + Lease settings section + + 0 + 0 + + + VApp startup section + + + + + The list of logical networks + + + + + + The configuration parameters for logical networks + + + + + + true + 172.16.7.1 + 255.255.255.0 + 1.1.1.2 + 1.1.1.3 + + + 172.16.7.230 + 172.16.7.239 + + + + + bridged + + + false + 3600 + 7200 + + 172.16.7.2 + 172.16.7.229 + + + + true + + + true + ipTranslation + allowTraffic + + + automatic + 100c208b-4f43-40bb-98d6-a046f6e48c3a + 0 + + + + + + true + + + + + + + + + + + + + + + + + + + Virtual hardware requirements + + Virtual Hardware Family + 0 + my-app + vmx-07 + + + 00:50:56:01:02:33 + 0 + true + Direct + PCNet32 ethernet adapter + Network adapter 0 + 1 + PCNet32 + 10 + + + 0 + SCSI Controller + SCSI Controller 0 + 2 + lsilogic + 6 + + + 0 + Hard disk + Hard disk 1 + + 2000 + 2 + 17 + + + 0 + IDE Controller + IDE Controller 0 + 3 + 5 + + + 0 + false + CD/DVD Drive + CD/DVD Drive 1 + + 3002 + 3 + 15 + + + 0 + false + Floppy Drive + Floppy Drive 1 + + 8000 + 14 + + + hertz * 10^6 + Number of Virtual CPUs + 1 virtual CPU(s) + 4 + 0 + 3 + 1 + 0 + + + + byte * 2^20 + Memory Size + 2048 MB of memory + 5 + 0 + 4 + 2048 + 0 + + + + + + + + + + + + + + + Specifies the operating system installed + Red Hat Enterprise Linux 5 (64-bit) + + + + Specifies the available VM network connections + 0 + + 0 + 172.16.7.230 + true + 00:50:56:01:02:33 + POOL + + + + + Specifies Guest OS Customization Settings + true + false + 1 + false + false + true + true + Favor + false + + my-app + + + 100c208b-4f43-40bb-98d6-a046f6e48c3a + + + diff --git a/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceContextModuleTest.java b/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceContextModuleTest.java index 2a5e964cc2..9d8be3b626 100644 --- a/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceContextModuleTest.java +++ b/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/compute/config/VCloudComputeServiceContextModuleTest.java @@ -36,7 +36,7 @@ public class VCloudComputeServiceContextModuleTest { for (Status state : EnumSet.allOf(Status.class).complementOf( EnumSet.of(Status.PENDING_DESCRIPTOR, Status.PENDING_CONTENTS, Status.COPYING, Status.QUARANTINED, Status.QUARANTINE_EXPIRED))) { - assert VCloudExpressComputeServiceContextModule.vAppStatusToNodeState.containsKey(state) : state; + assert VCloudExpressComputeServiceContextModule.VAPPSTATUS_TO_NODESTATE.containsKey(state) : state; } } diff --git a/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/compute/config/VCloudExpressComputeServiceContextModuleTest.java b/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/compute/config/VCloudExpressComputeServiceContextModuleTest.java index 5e1505b47b..11e1243c1d 100644 --- a/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/compute/config/VCloudExpressComputeServiceContextModuleTest.java +++ b/apis/vcloudexpress/src/test/java/org/jclouds/vcloud/compute/config/VCloudExpressComputeServiceContextModuleTest.java @@ -36,7 +36,7 @@ public class VCloudExpressComputeServiceContextModuleTest { for (Status state : EnumSet.allOf(Status.class).complementOf( EnumSet.of(Status.PENDING_DESCRIPTOR, Status.PENDING_CONTENTS, Status.COPYING, Status.QUARANTINED, Status.QUARANTINE_EXPIRED))) { - assert VCloudExpressComputeServiceContextModule.vAppStatusToNodeState.containsKey(state) : state; + assert VCloudExpressComputeServiceContextModule.VAPPSTATUS_TO_NODESTATE.containsKey(state) : state; } } diff --git a/common/vcloud/src/main/java/org/jclouds/vcloud/compute/config/CommonVCloudComputeServiceContextModule.java b/common/vcloud/src/main/java/org/jclouds/vcloud/compute/config/CommonVCloudComputeServiceContextModule.java index 3a14171817..a380f7cb4b 100644 --- a/common/vcloud/src/main/java/org/jclouds/vcloud/compute/config/CommonVCloudComputeServiceContextModule.java +++ b/common/vcloud/src/main/java/org/jclouds/vcloud/compute/config/CommonVCloudComputeServiceContextModule.java @@ -42,18 +42,18 @@ import com.google.inject.Provides; public abstract class CommonVCloudComputeServiceContextModule extends BaseComputeServiceContextModule { @VisibleForTesting - static final Map vAppStatusToNodeState = ImmutableMap. builder().put( - Status.OFF, NodeState.SUSPENDED).put(Status.ON, NodeState.RUNNING).put(Status.RESOLVED, NodeState.PENDING) - .put(Status.ERROR, NodeState.ERROR).put(Status.UNRECOGNIZED, NodeState.UNRECOGNIZED).put(Status.DEPLOYED, - NodeState.PENDING).put(Status.INCONSISTENT, NodeState.PENDING).put(Status.UNKNOWN, - NodeState.UNRECOGNIZED).put(Status.MIXED, NodeState.PENDING).put(Status.WAITING_FOR_INPUT, - NodeState.PENDING).put(Status.SUSPENDED, NodeState.SUSPENDED).put(Status.UNRESOLVED, - NodeState.PENDING).build(); + public static final Map VAPPSTATUS_TO_NODESTATE = ImmutableMap. builder() + .put(Status.OFF, NodeState.SUSPENDED).put(Status.ON, NodeState.RUNNING) + .put(Status.RESOLVED, NodeState.PENDING).put(Status.ERROR, NodeState.ERROR) + .put(Status.UNRECOGNIZED, NodeState.UNRECOGNIZED).put(Status.DEPLOYED, NodeState.PENDING) + .put(Status.INCONSISTENT, NodeState.PENDING).put(Status.UNKNOWN, NodeState.UNRECOGNIZED) + .put(Status.MIXED, NodeState.PENDING).put(Status.WAITING_FOR_INPUT, NodeState.PENDING) + .put(Status.SUSPENDED, NodeState.SUSPENDED).put(Status.UNRESOLVED, NodeState.PENDING).build(); @Singleton @Provides - Map provideVAppStatusToNodeState() { - return vAppStatusToNodeState; + protected Map provideVAppStatusToNodeState() { + return VAPPSTATUS_TO_NODESTATE; } @Override diff --git a/common/vcloud/src/main/java/org/jclouds/vcloud/compute/functions/FindLocationForResource.java b/common/vcloud/src/main/java/org/jclouds/vcloud/compute/functions/FindLocationForResource.java index 24eb90e943..1e9e84536d 100644 --- a/common/vcloud/src/main/java/org/jclouds/vcloud/compute/functions/FindLocationForResource.java +++ b/common/vcloud/src/main/java/org/jclouds/vcloud/compute/functions/FindLocationForResource.java @@ -63,8 +63,7 @@ public class FindLocationForResource { // link that only includes href and type. if (URI.create(input.getId()).equals(resource.getHref())) return input; - input = input.getParent(); - } while (input.getParent() != null); + } while ((input = input.getParent()) != null); } throw new NoSuchElementException(String.format("resource: %s not found in locations: %s", resource, locations .get())); diff --git a/core/src/main/java/org/jclouds/util/InetAddresses2.java b/core/src/main/java/org/jclouds/util/InetAddresses2.java index 82ebbd0df2..6fb3ee96f1 100644 --- a/core/src/main/java/org/jclouds/util/InetAddresses2.java +++ b/core/src/main/java/org/jclouds/util/InetAddresses2.java @@ -21,6 +21,9 @@ package org.jclouds.util; import static com.google.common.base.Preconditions.checkNotNull; +import javax.inject.Singleton; + +import com.google.common.base.Predicate; import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import com.google.common.net.InetAddresses; @@ -31,26 +34,40 @@ import com.google.common.net.InetAddresses; * @author Adrian Cole */ public class InetAddresses2 { + @Singleton + public static enum IsPrivateIPAddress implements Predicate { + INSTANCE; + + public boolean apply(String in) { + if (InetAddresses.isInetAddress(checkNotNull(in, "input address"))) { + // 24-bit Block (/8 prefix, 1/A) 10.0.0.0 10.255.255.255 16777216 + if (in.indexOf("10.") == 0) + return true; + // 20-bit Block (/12 prefix, 16/B) 172.16.0.0 172.31.255.255 1048576 + if (in.indexOf("172.") == 0) { + int second = Integer.parseInt(Iterables.get(Splitter.on('.').split(in), 1)); + if (second >= 16 && second <= 31) + return true; + } + // 16-bit Block (/16 prefix, 256/C) 192.168.0.0 192.168.255.255 65536 + if (in.indexOf("192.168.") == 0) + return true; + } + return false; + } + + @Override + public String toString() { + return "isPrivateIPAddress()"; + } + + } /** * @return true if the input is an ip4 address and in one of the 3 reserved private blocks. */ public static boolean isPrivateIPAddress(String in) { - if (InetAddresses.isInetAddress(checkNotNull(in, "input address"))) { - // 24-bit Block (/8 prefix, 1 × A) 10.0.0.0 10.255.255.255 16777216 - if (in.indexOf("10.") == 0) - return true; - // 20-bit Block (/12 prefix, 16 × B) 172.16.0.0 172.31.255.255 1048576 - if (in.indexOf("172.") == 0) { - int second = Integer.parseInt(Iterables.get(Splitter.on('.').split(in), 1)); - if (second >= 16 && second <= 31) - return true; - } - // 16-bit Block (/16 prefix, 256 × C) 192.168.0.0 192.168.255.255 65536 - if (in.indexOf("192.168.") == 0) - return true; - } - return false; + return IsPrivateIPAddress.INSTANCE.apply(in); } } From ad0acb56fabb16fe274d0f24a112e3d213dcf4dd Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 5 Mar 2011 16:35:37 -0500 Subject: [PATCH 03/13] fixed auth error when using an ssl site via ip and not hostname --- .../EC2ComputeServiceContextModule.java | 4 +- .../config/BaseVCloudRestClientModule.java | 4 +- .../BaseVCloudExpressRestClientModule.java | 4 +- .../config/TerremarkRestClientModule.java | 4 +- .../config/CommonVCloudRestClientModule.java | 16 +-- .../BaseComputeServiceContextModule.java | 57 +++++------ .../location/config/LocationModule.java | 4 +- ...tNotOnAuthorizationExceptionSupplier.java} | 39 ++------ ...ndThrowAuthorizationExceptionSupplier.java | 71 ++++++++++++++ ...rowAuthorizationExceptionSupplierTest.java | 97 +++++++++++++++++++ .../BaseLoadBalancerServiceContextModule.java | 4 +- 11 files changed, 227 insertions(+), 77 deletions(-) rename core/src/main/java/org/jclouds/rest/suppliers/{RetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java => MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java} (61%) create mode 100644 core/src/main/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplier.java create mode 100644 core/src/test/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplierTest.java diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java index dc545e3a52..acc16c5dbe 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/config/EC2ComputeServiceContextModule.java @@ -32,7 +32,7 @@ import org.jclouds.compute.domain.Image; import org.jclouds.ec2.compute.EC2ComputeService; import org.jclouds.ec2.compute.domain.RegionAndName; import org.jclouds.ec2.compute.suppliers.RegionAndNameToImageSupplier; -import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import com.google.common.base.Supplier; import com.google.inject.Provides; @@ -59,7 +59,7 @@ public class EC2ComputeServiceContextModule extends BaseComputeServiceContextMod @Singleton protected Supplier> provideRegionAndNameToImageSupplierCache( @Named(PROPERTY_SESSION_INTERVAL) long seconds, final RegionAndNameToImageSupplier supplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>( + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>( authException, seconds, new Supplier>() { @Override public Map get() { diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/config/BaseVCloudRestClientModule.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/config/BaseVCloudRestClientModule.java index ef13919798..626f36fe9f 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/config/BaseVCloudRestClientModule.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/config/BaseVCloudRestClientModule.java @@ -30,7 +30,7 @@ import javax.inject.Singleton; import org.jclouds.http.RequiresHttp; import org.jclouds.rest.AsyncClientFactory; import org.jclouds.rest.ConfiguresRestClient; -import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.vcloud.VCloudAsyncClient; import org.jclouds.vcloud.VCloudClient; import org.jclouds.vcloud.VCloudLoginAsyncClient; @@ -78,7 +78,7 @@ public abstract class BaseVCloudRestClientModule provideVCloudTokenCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final VCloudLoginAsyncClient login) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier(authException, seconds, + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(authException, seconds, new Supplier() { @Override diff --git a/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/config/BaseVCloudExpressRestClientModule.java b/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/config/BaseVCloudExpressRestClientModule.java index 37bdd3025d..8ec042739b 100644 --- a/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/config/BaseVCloudExpressRestClientModule.java +++ b/apis/vcloudexpress/src/main/java/org/jclouds/vcloud/config/BaseVCloudExpressRestClientModule.java @@ -30,7 +30,7 @@ import javax.inject.Singleton; import org.jclouds.http.RequiresHttp; import org.jclouds.rest.AsyncClientFactory; import org.jclouds.rest.ConfiguresRestClient; -import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.vcloud.VCloudExpressAsyncClient; import org.jclouds.vcloud.VCloudExpressClient; import org.jclouds.vcloud.VCloudExpressLoginAsyncClient; @@ -78,7 +78,7 @@ public abstract class BaseVCloudExpressRestClientModule provideVCloudTokenCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final VCloudExpressLoginAsyncClient login) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier(authException, seconds, + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier(authException, seconds, new Supplier() { @Override diff --git a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/config/TerremarkRestClientModule.java b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/config/TerremarkRestClientModule.java index 35c55d2243..d34af675ab 100644 --- a/common/trmk/src/main/java/org/jclouds/vcloud/terremark/config/TerremarkRestClientModule.java +++ b/common/trmk/src/main/java/org/jclouds/vcloud/terremark/config/TerremarkRestClientModule.java @@ -32,7 +32,7 @@ import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.Redirection; import org.jclouds.http.annotation.ServerError; -import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.util.Strings2; import org.jclouds.vcloud.config.BaseVCloudExpressRestClientModule; import org.jclouds.vcloud.domain.ReferenceType; @@ -109,7 +109,7 @@ public abstract class TerremarkRestClientModule> provideOrgToKeysListCache( @Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgNameToKeysListSupplier supplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, new Supplier>() { @Override public Map get() { diff --git a/common/vcloud/src/main/java/org/jclouds/vcloud/config/CommonVCloudRestClientModule.java b/common/vcloud/src/main/java/org/jclouds/vcloud/config/CommonVCloudRestClientModule.java index b0da952d99..aaf34075bb 100644 --- a/common/vcloud/src/main/java/org/jclouds/vcloud/config/CommonVCloudRestClientModule.java +++ b/common/vcloud/src/main/java/org/jclouds/vcloud/config/CommonVCloudRestClientModule.java @@ -60,7 +60,7 @@ import org.jclouds.rest.AsyncClientFactory; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.ConfiguresRestClient; import org.jclouds.rest.config.RestClientModule; -import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.vcloud.CommonVCloudAsyncClient; import org.jclouds.vcloud.CommonVCloudClient; import org.jclouds.vcloud.VCloudToken; @@ -146,7 +146,7 @@ public class CommonVCloudRestClientModule> provideVDCtoORG(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final Supplier> orgToVDCSupplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, new Supplier>() { @Override public Map get() { @@ -193,7 +193,7 @@ public class CommonVCloudRestClientModule> provideOrgMapCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgMapSupplier supplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, new Supplier>() { @Override public Map get() { @@ -287,7 +287,7 @@ public class CommonVCloudRestClientModule> provideVDCtoORG(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgNameToOrgSupplier supplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, new Supplier>() { @Override public Map get() { @@ -300,7 +300,7 @@ public class CommonVCloudRestClientModule> provideURIToVDC( @Named(PROPERTY_SESSION_INTERVAL) long seconds, final URItoVDC supplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>( + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>( authException, seconds, new Supplier>() { @Override public Map get() { @@ -463,7 +463,7 @@ public class CommonVCloudRestClientModule>> provideOrgCatalogItemMapSupplierCache( @Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgCatalogSupplier supplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>>( + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>>( authException, seconds, new Supplier>>() { @Override @@ -478,7 +478,7 @@ public class CommonVCloudRestClientModule>> provideOrgVDCSupplierCache( @Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgVDCSupplier supplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>>( + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>>( authException, seconds, new Supplier>>() { @Override @@ -561,7 +561,7 @@ public class CommonVCloudRestClientModule>>> provideOrgCatalogItemSupplierCache( @Named(PROPERTY_SESSION_INTERVAL) long seconds, final OrgCatalogItemSupplier supplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>>>( + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>>>( authException, seconds, new Supplier>>>() { @Override diff --git a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java index 078766af95..7dd15bf7be 100644 --- a/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java +++ b/compute/src/main/java/org/jclouds/compute/config/BaseComputeServiceContextModule.java @@ -52,7 +52,7 @@ import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap; import org.jclouds.json.Json; import org.jclouds.location.config.LocationModule; import org.jclouds.rest.AuthorizationException; -import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.scriptbuilder.domain.Statement; import org.jclouds.scriptbuilder.domain.Statements; import org.jclouds.ssh.SshClient; @@ -83,19 +83,20 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { bind(new TypeLiteral>() { }).to(TemplateOptionsToStatement.class); - install(new FactoryModuleBuilder().implement(RunScriptOnNode.class, Names.named("direct"), - RunScriptOnNodeUsingSsh.class).implement(RunScriptOnNode.class, Names.named("blocking"), - RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete.class).implement(RunScriptOnNode.class, - Names.named("nonblocking"), RunScriptOnNodeAsInitScriptUsingSsh.class).build( - RunScriptOnNodeFactoryImpl.Factory.class)); + install(new FactoryModuleBuilder() + .implement(RunScriptOnNode.class, Names.named("direct"), RunScriptOnNodeUsingSsh.class) + .implement(RunScriptOnNode.class, Names.named("blocking"), + RunScriptOnNodeAsInitScriptUsingSshAndBlockUntilComplete.class) + .implement(RunScriptOnNode.class, Names.named("nonblocking"), RunScriptOnNodeAsInitScriptUsingSsh.class) + .build(RunScriptOnNodeFactoryImpl.Factory.class)); bind(RunScriptOnNode.Factory.class).to(RunScriptOnNodeFactoryImpl.class); install(new FactoryModuleBuilder().implement(new TypeLiteral>() { - }, CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.class).implement( - new TypeLiteral>() { - }, CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.class).build( - CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory.class)); + }, CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.class) + .implement(new TypeLiteral>() { + }, CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.class) + .build(CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory.class)); install(new FactoryModuleBuilder().implement(new TypeLiteral>() { }, InitializeRunScriptOnNodeOrPlaceInBadMap.class).build(InitializeRunScriptOnNodeOrPlaceInBadMap.Factory.class)); @@ -129,8 +130,8 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { checkNotNull(runScript, "runScript"); checkNotNull(options, "options"); return !options.shouldWrapInInitScript() ? factory.exec(node, runScript, options) : (options - .shouldBlockOnComplete() ? factory.backgroundAndBlockOnComplete(node, runScript, options) : factory - .background(node, runScript, options)); + .shouldBlockOnComplete() ? factory.backgroundAndBlockOnComplete(node, runScript, options) : factory + .background(node, runScript, options)); } @Override @@ -197,14 +198,14 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { @Singleton @Memoized protected Supplier> supplyImageCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, - final Supplier> imageSupplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, - new Supplier>() { - @Override - public Set get() { - return imageSupplier.get(); - } - }); + final Supplier> imageSupplier) { + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, + new Supplier>() { + @Override + public Set get() { + return imageSupplier.get(); + } + }); } @Provides @@ -231,14 +232,14 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule { @Singleton @Memoized protected Supplier> supplySizeCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, - final Supplier> hardwareSupplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, - new Supplier>() { - @Override - public Set get() { - return hardwareSupplier.get(); - } - }); + final Supplier> hardwareSupplier) { + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, + new Supplier>() { + @Override + public Set get() { + return hardwareSupplier.get(); + } + }); } @Provides diff --git a/core/src/main/java/org/jclouds/location/config/LocationModule.java b/core/src/main/java/org/jclouds/location/config/LocationModule.java index 22bf018804..91f65747e0 100644 --- a/core/src/main/java/org/jclouds/location/config/LocationModule.java +++ b/core/src/main/java/org/jclouds/location/config/LocationModule.java @@ -31,7 +31,7 @@ import javax.inject.Singleton; import org.jclouds.collect.Memoized; import org.jclouds.domain.Location; import org.jclouds.rest.AuthorizationException; -import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import com.google.common.base.Function; import com.google.common.base.Supplier; @@ -81,7 +81,7 @@ public class LocationModule extends AbstractModule { @Memoized protected Supplier> supplyLocationCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final Supplier> locationSupplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, new Supplier>() { @Override public Set get() { diff --git a/core/src/main/java/org/jclouds/rest/suppliers/RetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java similarity index 61% rename from core/src/main/java/org/jclouds/rest/suppliers/RetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java rename to core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java index 6b5d96dcde..cbf85d29f5 100644 --- a/core/src/main/java/org/jclouds/rest/suppliers/RetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java +++ b/core/src/main/java/org/jclouds/rest/suppliers/MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier.java @@ -19,6 +19,8 @@ package org.jclouds.rest.suppliers; +import static com.google.common.base.Suppliers.memoizeWithExpiration; + import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -26,8 +28,6 @@ import org.jclouds.concurrent.RetryOnTimeOutExceptionSupplier; import org.jclouds.rest.AuthorizationException; import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; -import com.google.common.base.Throwables; /** * This will retry the supplier if it encounters a timeout exception, but not if it encounters an @@ -44,33 +44,15 @@ import com.google.common.base.Throwables; * * @author Adrian Cole */ -public class RetryOnTimeOutButNotOnAuthorizationExceptionSupplier implements Supplier { +public class MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier implements Supplier { private final Supplier delegate; + private final long seconds; - public RetryOnTimeOutButNotOnAuthorizationExceptionSupplier( - final AtomicReference authException, final long seconds, final Supplier delegate) { - this.delegate = Suppliers. memoizeWithExpiration(new RetryOnTimeOutExceptionSupplier(new Supplier() { - public T get() { - if (authException.get() != null) - throw authException.get(); - try { - return delegate.get(); - } catch (AuthorizationException e) { - authException.set(e); - throw e; - } catch (Exception e) { - Throwables.propagate(e); - assert false : e; - return null; - } - } - - @Override - public String toString() { - return "memoizeWithExpiration(" + delegate + ", seconds=" + seconds + ")"; - } - - }), seconds, TimeUnit.SECONDS); + public MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier( + AtomicReference authException, long seconds, Supplier delegate) { + this.delegate = memoizeWithExpiration(new RetryOnTimeOutExceptionSupplier( + new SetAndThrowAuthorizationExceptionSupplier(delegate, authException)), seconds, TimeUnit.SECONDS); + this.seconds = seconds; } @Override @@ -80,7 +62,6 @@ public class RetryOnTimeOutButNotOnAuthorizationExceptionSupplier implements @Override public String toString() { - return "RetryOnTimeOutButNotOnAuthorizationExceptionSupplier(" + delegate + ")"; + return "memoizeWithExpiration(" + delegate + ", seconds=" + seconds + ")"; } - } \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplier.java b/core/src/main/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplier.java new file mode 100644 index 0000000000..e367fd5aba --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplier.java @@ -0,0 +1,71 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.rest.suppliers; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Throwables.propagate; +import static org.jclouds.util.Throwables2.getFirstThrowableOfType; + +import java.util.concurrent.atomic.AtomicReference; + +import org.jclouds.rest.AuthorizationException; + +import com.google.common.base.Supplier; + +/** + * + * @author Adrian Cole + */ +public class SetAndThrowAuthorizationExceptionSupplier implements Supplier { + private final Supplier delegate; + private final AtomicReference authException; + + public SetAndThrowAuthorizationExceptionSupplier(Supplier delegate, + AtomicReference authException) { + this.delegate = checkNotNull(delegate, "delegate"); + this.authException = checkNotNull(authException, "authException"); + } + + public T get() { + if (authException.get() != null) + throw authException.get(); + try { + return delegate.get(); + } catch (AuthorizationException e) { + authException.set(e); + throw e; + } catch (Exception e) { + AuthorizationException aex = getFirstThrowableOfType(e, AuthorizationException.class); + if (aex != null) { + authException.set(aex); + throw aex; + } + propagate(e); + assert false : e; + return null; + } + } + + @Override + public String toString() { + return "RetryOnTimeOutButNotOnAuthorizationExceptionSupplier(" + delegate + ")"; + } + +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplierTest.java b/core/src/test/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplierTest.java new file mode 100644 index 0000000000..5fd632c175 --- /dev/null +++ b/core/src/test/java/org/jclouds/rest/suppliers/SetAndThrowAuthorizationExceptionSupplierTest.java @@ -0,0 +1,97 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.rest.suppliers; + +import static org.testng.Assert.assertEquals; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; + +import org.jclouds.rest.AuthorizationException; +import org.testng.annotations.Test; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +/** + * Tests behavior of {@code SetAndThrowAuthorizationExceptionSupplier} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class SetAndThrowAuthorizationExceptionSupplierTest { + @Test + public void testNormal() { + AtomicReference authException = new AtomicReference(); + assertEquals( + new SetAndThrowAuthorizationExceptionSupplier(Suppliers.ofInstance("foo"), authException).get(), + "foo"); + assertEquals(authException.get(), null); + } + + @Test(expectedExceptions = AuthorizationException.class) + public void testThrowsAuthorizationExceptionAndAlsoSetsExceptionType() { + AtomicReference authException = new AtomicReference(); + try { + new SetAndThrowAuthorizationExceptionSupplier(new Supplier() { + + @Override + public String get() { + throw new AuthorizationException(); + } + }, authException).get(); + } finally { + assertEquals(authException.get().getClass(), AuthorizationException.class); + } + } + + @Test(expectedExceptions = AuthorizationException.class) + public void testThrowsAuthorizationExceptionAndAlsoSetsExceptionTypeWhenNested() { + AtomicReference authException = new AtomicReference(); + try { + new SetAndThrowAuthorizationExceptionSupplier(new Supplier() { + + @Override + public String get() { + throw new RuntimeException(new ExecutionException(new AuthorizationException())); + } + }, authException).get(); + } finally { + assertEquals(authException.get().getClass(), AuthorizationException.class); + } + } + + @Test(expectedExceptions = RuntimeException.class) + public void testThrowsOriginalExceptionAndAlsoSetsExceptionTypeWhenNestedAndNotAuthorizationException() { + AtomicReference authException = new AtomicReference(); + try { + new SetAndThrowAuthorizationExceptionSupplier(new Supplier() { + + @Override + public String get() { + throw new RuntimeException(new IllegalArgumentException("foo")); + } + }, authException).get(); + } finally { + assertEquals(authException.get().getClass(), RuntimeException.class); + } + } + +} diff --git a/loadbalancer/src/main/java/org/jclouds/loadbalancer/config/BaseLoadBalancerServiceContextModule.java b/loadbalancer/src/main/java/org/jclouds/loadbalancer/config/BaseLoadBalancerServiceContextModule.java index a394d0186e..4861fa033a 100644 --- a/loadbalancer/src/main/java/org/jclouds/loadbalancer/config/BaseLoadBalancerServiceContextModule.java +++ b/loadbalancer/src/main/java/org/jclouds/loadbalancer/config/BaseLoadBalancerServiceContextModule.java @@ -31,7 +31,7 @@ import javax.inject.Singleton; import org.jclouds.collect.Memoized; import org.jclouds.domain.Location; import org.jclouds.rest.AuthorizationException; -import org.jclouds.rest.suppliers.RetryOnTimeOutButNotOnAuthorizationExceptionSupplier; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import com.google.common.base.Function; import com.google.common.base.Supplier; @@ -79,7 +79,7 @@ public abstract class BaseLoadBalancerServiceContextModule extends AbstractModul @Memoized protected Supplier> supplyLocationCache(@Named(PROPERTY_SESSION_INTERVAL) long seconds, final Supplier> locationSupplier) { - return new RetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, seconds, new Supplier>() { @Override public Set get() { From 7eef59db020e80f282e65a9dd9261ac950f85a76 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 5 Mar 2011 16:50:48 -0500 Subject: [PATCH 04/13] allowed overriding of rest.properties during testing --- .../compute/VCloudComputeServiceLiveTest.java | 8 +++---- .../vcloud/CommonVCloudClientLiveTest.java | 21 ++++++++++++------- .../compute/BaseComputeServiceLiveTest.java | 6 +++--- .../compute/BaseTemplateBuilderLiveTest.java | 7 ++++++- .../ServerManagerComputeServiceLiveTest.java | 2 +- 5 files changed, 27 insertions(+), 17 deletions(-) diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/VCloudComputeServiceLiveTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/VCloudComputeServiceLiveTest.java index 33867bbdca..92e22a12fd 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/VCloudComputeServiceLiveTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/VCloudComputeServiceLiveTest.java @@ -52,8 +52,8 @@ public class VCloudComputeServiceLiveTest extends BaseComputeServiceLiveTest { public void testAssignability() throws Exception { @SuppressWarnings("unused") - RestContext tmContext = new ComputeServiceContextFactory().createContext( - provider, identity, credential).getProviderSpecificContext(); + RestContext tmContext = new ComputeServiceContextFactory(setupRestProperties()) + .createContext(provider, identity, credential).getProviderSpecificContext(); } @Override @@ -64,8 +64,8 @@ public class VCloudComputeServiceLiveTest extends BaseComputeServiceLiveTest { assertEquals(node.getType(), ComputeType.NODE); NodeMetadata allData = client.getNodeMetadata(node.getId()); System.out.println(allData.getHardware()); - RestContext tmContext = new ComputeServiceContextFactory().createContext( - provider, identity, credential).getProviderSpecificContext(); + RestContext tmContext = new ComputeServiceContextFactory( + setupRestProperties()).createContext(provider, identity, credential).getProviderSpecificContext(); VApp vApp = tmContext.getApi().findVAppInOrgVDCNamed(null, null, allData.getName()); assertEquals(vApp.getName(), allData.getName()); } diff --git a/common/vcloud/src/test/java/org/jclouds/vcloud/CommonVCloudClientLiveTest.java b/common/vcloud/src/test/java/org/jclouds/vcloud/CommonVCloudClientLiveTest.java index 6ed150b8e8..8b845bc8a8 100644 --- a/common/vcloud/src/test/java/org/jclouds/vcloud/CommonVCloudClientLiveTest.java +++ b/common/vcloud/src/test/java/org/jclouds/vcloud/CommonVCloudClientLiveTest.java @@ -30,6 +30,7 @@ import org.jclouds.compute.ComputeServiceContextFactory; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.rest.AuthorizationException; import org.jclouds.rest.RestContext; +import org.jclouds.rest.RestContextFactory; import org.jclouds.vcloud.domain.Catalog; import org.jclouds.vcloud.domain.CatalogItem; import org.jclouds.vcloud.domain.Org; @@ -95,13 +96,13 @@ public abstract class CommonVCloudClientLiveTest of(new Log4JLoggingModule()), overrides).getProviderSpecificContext(); connection = context.getApi(); diff --git a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java index 7b37036fb1..86d61cd96c 100755 --- a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java @@ -161,12 +161,12 @@ public abstract class BaseComputeServiceLiveTest { if (context != null) context.close(); Properties props = setupProperties(); - context = new ComputeServiceContextFactory(getRestProperties()).createContext(provider, ImmutableSet.of( + context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, ImmutableSet.of( new Log4JLoggingModule(), getSshModule()), props); client = context.getComputeService(); } - protected Properties getRestProperties() { + protected Properties setupRestProperties() { return RestContextFactory.getPropertiesFromResource("/rest.properties"); } @@ -182,7 +182,7 @@ public abstract class BaseComputeServiceLiveTest { public void testCorrectAuthException() throws Exception { ComputeServiceContext context = null; try { - context = new ComputeServiceContextFactory().createContext(provider, "MOMMA", "MIA", ImmutableSet + context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, "MOMMA", "MIA", ImmutableSet . of(new Log4JLoggingModule())); context.getComputeService().listNodes(); } catch (AuthorizationException e) { diff --git a/compute/src/test/java/org/jclouds/compute/BaseTemplateBuilderLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseTemplateBuilderLiveTest.java index 9fc48f5276..efe574ff5b 100644 --- a/compute/src/test/java/org/jclouds/compute/BaseTemplateBuilderLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/BaseTemplateBuilderLiveTest.java @@ -44,6 +44,7 @@ import org.jclouds.domain.LocationScope; import org.jclouds.json.Json; import org.jclouds.json.config.GsonModule; import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.rest.RestContextFactory; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; @@ -91,10 +92,14 @@ public abstract class BaseTemplateBuilderLiveTest { return overrides; } + protected Properties setupRestProperties() { + return RestContextFactory.getPropertiesFromResource("/rest.properties"); + } + @BeforeClass public void setupClient() throws InterruptedException, ExecutionException, TimeoutException, IOException { setupCredentials(); - context = new ComputeServiceContextFactory().createContext(provider, ImmutableSet + context = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, ImmutableSet . of(new Log4JLoggingModule()), setupProperties()); } diff --git a/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceLiveTest.java b/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceLiveTest.java index b64f7008a0..51d7a72a79 100644 --- a/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceLiveTest.java +++ b/skeletons/standalone-compute/src/test/java/org/jclouds/servermanager/compute/ServerManagerComputeServiceLiveTest.java @@ -24,7 +24,7 @@ public class ServerManagerComputeServiceLiveTest extends BaseComputeServiceLiveT } @Override - protected Properties getRestProperties() { + protected Properties setupRestProperties() { Properties restProperties = new Properties(); restProperties.setProperty("servermanager.contextbuilder", ServerManagerComputeServiceContextBuilder.class.getName()); From fb663fceeda4abce16da68de0110c54087bb0227 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 5 Mar 2011 16:55:59 -0500 Subject: [PATCH 05/13] Issue 498:[vcloud] expose IpAddressAllocationMode to compute service --- .../org/jclouds/vcloud/VCloudAsyncClient.java | 47 +++++--- .../java/org/jclouds/vcloud/VCloudClient.java | 27 +++-- ...dNetworkConnectionSectionToXmlPayload.java | 109 ++++++++++++++++++ .../options/VCloudTemplateOptions.java | 37 +++++- ...IntoNameThenCustomizeDeployAndPowerOn.java | 83 +++++++++---- .../vcloud/domain/NetworkConnection.java | 73 +++++++++++- .../domain/NetworkConnectionSection.java | 70 +++++++++-- ...workConnectionSectionToXmlPayloadTest.java | 86 ++++++++++++++ .../options/VCloudTemplateOptionsTest.java | 26 ++++- 9 files changed, 486 insertions(+), 72 deletions(-) create mode 100644 apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindNetworkConnectionSectionToXmlPayload.java create mode 100644 apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindNetworkConnectionSectionToXmlPayloadTest.java diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudAsyncClient.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudAsyncClient.java index 6813714173..9f34a339e2 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudAsyncClient.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudAsyncClient.java @@ -21,6 +21,7 @@ package org.jclouds.vcloud; import static org.jclouds.vcloud.VCloudMediaType.DEPLOYVAPPPARAMS_XML; import static org.jclouds.vcloud.VCloudMediaType.GUESTCUSTOMIZATIONSECTION_XML; +import static org.jclouds.vcloud.VCloudMediaType.NETWORKCONNECTIONSECTION_XML; import static org.jclouds.vcloud.VCloudMediaType.TASK_XML; import static org.jclouds.vcloud.VCloudMediaType.UNDEPLOYVAPPPARAMS_XML; import static org.jclouds.vcloud.VCloudMediaType.VAPPTEMPLATE_XML; @@ -47,9 +48,9 @@ import org.jclouds.rest.annotations.Endpoint; import org.jclouds.rest.annotations.EndpointParam; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.MapBinder; +import org.jclouds.rest.annotations.ParamValidators; import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.PayloadParams; -import org.jclouds.rest.annotations.ParamValidators; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; @@ -59,8 +60,10 @@ import org.jclouds.vcloud.binders.BindCloneVAppParamsToXmlPayload; import org.jclouds.vcloud.binders.BindDeployVAppParamsToXmlPayload; import org.jclouds.vcloud.binders.BindGuestCustomizationSectionToXmlPayload; import org.jclouds.vcloud.binders.BindInstantiateVAppTemplateParamsToXmlPayload; +import org.jclouds.vcloud.binders.BindNetworkConnectionSectionToXmlPayload; import org.jclouds.vcloud.binders.BindUndeployVAppParamsToXmlPayload; import org.jclouds.vcloud.domain.GuestCustomizationSection; +import org.jclouds.vcloud.domain.NetworkConnectionSection; import org.jclouds.vcloud.domain.ReferenceType; import org.jclouds.vcloud.domain.Task; import org.jclouds.vcloud.domain.VApp; @@ -140,9 +143,9 @@ public interface VCloudAsyncClient extends CommonVCloudAsyncClient { @XMLResponseParser(VAppTemplateHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) ListenableFuture findVAppTemplateInOrgCatalogNamed( - @Nullable @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String orgName, - @Nullable @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String catalogName, - @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String itemName); + @Nullable @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String orgName, + @Nullable @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String catalogName, + @EndpointParam(parser = OrgNameCatalogNameVAppTemplateNameToEndpoint.class) String itemName); /** * @see VCloudClient#instantiateVAppTemplateInVDC @@ -154,9 +157,9 @@ public interface VCloudAsyncClient extends CommonVCloudAsyncClient { @XMLResponseParser(VAppHandler.class) @MapBinder(BindInstantiateVAppTemplateParamsToXmlPayload.class) ListenableFuture instantiateVAppTemplateInVDC(@EndpointParam URI vdc, - @PayloadParam("template") URI template, - @PayloadParam("name") @ParamValidators(DnsNameValidator.class) String appName, - InstantiateVAppTemplateOptions... options); + @PayloadParam("template") URI template, + @PayloadParam("name") @ParamValidators(DnsNameValidator.class) String appName, + InstantiateVAppTemplateOptions... options); /** * @see VCloudClient#cloneVAppInVDC @@ -168,8 +171,7 @@ public interface VCloudAsyncClient extends CommonVCloudAsyncClient { @XMLResponseParser(TaskHandler.class) @MapBinder(BindCloneVAppParamsToXmlPayload.class) ListenableFuture cloneVAppInVDC(@EndpointParam URI vdc, @PayloadParam("vApp") URI toClone, - @PayloadParam("newName") @ParamValidators(DnsNameValidator.class) String newName, - CloneVAppOptions... options); + @PayloadParam("newName") @ParamValidators(DnsNameValidator.class) String newName, CloneVAppOptions... options); /** * @see VCloudClient#captureVAppInVDC @@ -181,9 +183,9 @@ public interface VCloudAsyncClient extends CommonVCloudAsyncClient { @XMLResponseParser(VAppTemplateHandler.class) @MapBinder(BindCaptureVAppParamsToXmlPayload.class) ListenableFuture captureVAppInVDC(@EndpointParam URI vdc, - @PayloadParam("vApp") URI toCapture, - @PayloadParam("templateName") @ParamValidators(DnsNameValidator.class) String templateName, - CaptureVAppOptions... options); + @PayloadParam("vApp") URI toCapture, + @PayloadParam("templateName") @ParamValidators(DnsNameValidator.class) String templateName, + CaptureVAppOptions... options); /** * @see VCloudClient#findVAppInOrgVDCNamed @@ -193,9 +195,9 @@ public interface VCloudAsyncClient extends CommonVCloudAsyncClient { @XMLResponseParser(VAppHandler.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class) ListenableFuture findVAppInOrgVDCNamed( - @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String orgName, - @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String catalogName, - @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String vAppName); + @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String orgName, + @Nullable @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String catalogName, + @EndpointParam(parser = OrgNameVDCNameResourceEntityNameToEndpoint.class) String vAppName); /** * @see VCloudClient#getVApp @@ -224,8 +226,19 @@ public interface VCloudAsyncClient extends CommonVCloudAsyncClient { @Path("/guestCustomizationSection") @XMLResponseParser(TaskHandler.class) ListenableFuture updateGuestCustomizationOfVm( - @EndpointParam URI vm, - @BinderParam(BindGuestCustomizationSectionToXmlPayload.class) GuestCustomizationSection guestCustomizationSection); + @EndpointParam URI vm, + @BinderParam(BindGuestCustomizationSectionToXmlPayload.class) GuestCustomizationSection guestCustomizationSection); + + /** + * @see VCloudClient#updateNetworkConnectionOfVm + */ + @PUT + @Consumes(TASK_XML) + @Produces(NETWORKCONNECTIONSECTION_XML) + @Path("/networkConnectionSection") + @XMLResponseParser(TaskHandler.class) + ListenableFuture updateNetworkConnectionOfVm(@EndpointParam URI vm, + @BinderParam(BindNetworkConnectionSectionToXmlPayload.class) NetworkConnectionSection networkConnectionSection); /** * @see VCloudClient#deployVAppOrVm diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudClient.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudClient.java index 25fcfd2559..78bb784fa4 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudClient.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/VCloudClient.java @@ -29,6 +29,7 @@ import javax.annotation.Nullable; import org.jclouds.concurrent.Timeout; import org.jclouds.vcloud.domain.GuestCustomizationSection; +import org.jclouds.vcloud.domain.NetworkConnectionSection; import org.jclouds.vcloud.domain.ReferenceType; import org.jclouds.vcloud.domain.Task; import org.jclouds.vcloud.domain.VApp; @@ -48,14 +49,15 @@ import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; */ @Timeout(duration = 300, timeUnit = TimeUnit.SECONDS) public interface VCloudClient extends CommonVCloudClient { - + /** * Get a Screen Thumbnail for a Virtual Machine * - * @param vm to snapshot + * @param vm + * to snapshot */ InputStream getThumbnailOfVm(URI vm); - + /** * The response to a login request includes a list of the organizations to which the * authenticated user has access. @@ -68,11 +70,9 @@ public interface VCloudClient extends CommonVCloudClient { Task cloneVAppInVDC(URI vDC, URI toClone, String newName, CloneVAppOptions... options); - /** - * The captureVApp request creates a vApp template from an instantiated vApp. - *

Note

- * Before it can be captured, a vApp must be undeployed + * The captureVApp request creates a vApp template from an instantiated vApp.

Note

+ * Before it can be captured, a vApp must be undeployed * * @param vDC * @param toClone @@ -97,6 +97,17 @@ public interface VCloudClient extends CommonVCloudClient { */ Task updateGuestCustomizationOfVm(URI vm, GuestCustomizationSection guestCustomizationSection); + /** + * Modify the Network Connection Section of a Virtual Machine + * + * @param vm + * uri to modify + * @param updated + * networkConnectionSection + * @return task in progress + */ + Task updateNetworkConnectionOfVm(URI vm, NetworkConnectionSection guestCustomizationSection); + /** * returns the vapp template corresponding to a catalog item in the catalog associated with the * specified name. Note that the org and catalog parameters can be null to choose default. @@ -112,7 +123,7 @@ public interface VCloudClient extends CommonVCloudClient { * if you specified an org, catalog, or catalog item name that isn't present */ VAppTemplate findVAppTemplateInOrgCatalogNamed(@Nullable String orgName, @Nullable String catalogName, - String itemName); + String itemName); VApp findVAppInOrgVDCNamed(@Nullable String orgName, @Nullable String catalogName, String vAppName); diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindNetworkConnectionSectionToXmlPayload.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindNetworkConnectionSectionToXmlPayload.java new file mode 100644 index 0000000000..0f84287976 --- /dev/null +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/binders/BindNetworkConnectionSectionToXmlPayload.java @@ -0,0 +1,109 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.vcloud.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_XML_NAMESPACE; +import static org.jclouds.vcloud.reference.VCloudConstants.PROPERTY_VCLOUD_XML_SCHEMA; + +import java.util.Properties; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.http.HttpRequest; +import org.jclouds.logging.Logger; +import org.jclouds.rest.binders.BindToStringPayload; +import org.jclouds.vcloud.domain.NetworkConnection; +import org.jclouds.vcloud.domain.NetworkConnectionSection; + +import com.google.common.base.Throwables; +import com.google.inject.Inject; +import com.jamesmurty.utils.XMLBuilder; + +/** + * + * @author Adrian Cole + * + */ +@Singleton +public class BindNetworkConnectionSectionToXmlPayload extends BindToStringPayload { + @Resource + protected Logger logger = Logger.NULL; + + protected final String ns; + protected final String schema; + + @Inject + public BindNetworkConnectionSectionToXmlPayload(BindToStringPayload stringBinder, + @Named(PROPERTY_VCLOUD_XML_NAMESPACE) String ns, @Named(PROPERTY_VCLOUD_XML_SCHEMA) String schema) { + this.ns = ns; + this.schema = schema; + } + + @Override + public R bindToRequest(R request, Object payload) { + checkArgument(checkNotNull(payload, "NetworkConnectionSection") instanceof NetworkConnectionSection, + "this binder is only valid for NetworkConnectionSection!"); + NetworkConnectionSection net = NetworkConnectionSection.class.cast(payload); + XMLBuilder networkConnectionSection; + try { + networkConnectionSection = XMLBuilder.create("NetworkConnectionSection").a("xmlns", ns) + .a("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1").a("type", net.getType()) + .a("href", net.getHref().toASCIIString()).a("ovf:required", "false"); + networkConnectionSection.e("ovf:Info").t(net.getInfo()); + + if (net.getPrimaryNetworkConnectionIndex() != null) + networkConnectionSection.e("PrimaryNetworkConnectionIndex").t( + net.getPrimaryNetworkConnectionIndex().toString()); + for (NetworkConnection networkConnection : net.getConnections()) { + XMLBuilder networkConnectionSectionChild = networkConnectionSection.e("NetworkConnection").a("network", + networkConnection.getNetwork()); + networkConnectionSectionChild.e("NetworkConnectionIndex").t( + networkConnection.getNetworkConnectionIndex() + ""); + if (networkConnection.getExternalIpAddress() != null) + networkConnectionSectionChild.e("ExternalIpAddress").t(networkConnection.getExternalIpAddress()); + if (networkConnection.getIpAddress() != null) + networkConnectionSectionChild.e("IpAddress").t(networkConnection.getIpAddress()); + networkConnectionSectionChild.e("IsConnected").t(networkConnection.isConnected() + ""); + if (networkConnection.getMACAddress() != null) + networkConnectionSectionChild.e("MACAddress").t(networkConnection.getMACAddress()); + if (networkConnection.getIpAddressAllocationMode() != null) + networkConnectionSectionChild.e("IpAddressAllocationMode").t( + networkConnection.getIpAddressAllocationMode().toString()); + } + + if (net.getEdit() != null) + networkConnectionSection.e("Link").a("rel", "edit").a("type", net.getType()) + .a("href", net.getHref().toASCIIString()); + + Properties outputProperties = new Properties(); + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + request = super.bindToRequest(request, networkConnectionSection.asString(outputProperties)); + request.getPayload().getContentMetadata().setContentType(net.getType()); + } catch (Exception e) { + Throwables.propagate(e); + } + return request; + } + +} diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java index 8214f4587f..584ec7079f 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java @@ -24,6 +24,7 @@ import java.util.Arrays; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.io.Payload; import org.jclouds.util.Preconditions2; +import org.jclouds.vcloud.domain.network.IpAddressAllocationMode; /** * Contains options supported in the {@code ComputeService#runNode} operation on the "vcloud" @@ -45,6 +46,7 @@ import org.jclouds.util.Preconditions2; public class VCloudTemplateOptions extends TemplateOptions { private String customizationScript = null; + private IpAddressAllocationMode ipAddressAllocationMode = null; public static final VCloudTemplateOptions NONE = new VCloudTemplateOptions(); @@ -57,8 +59,15 @@ public class VCloudTemplateOptions extends TemplateOptions { return this; } - public static class Builder { + /** + * Specifies the ipAddressAllocationMode used to for network interfaces on the VMs + */ + public VCloudTemplateOptions ipAddressAllocationMode(IpAddressAllocationMode ipAddressAllocationMode) { + this.ipAddressAllocationMode = ipAddressAllocationMode; + return this; + } + public static class Builder { /** * @see VCloudTemplateOptions#customizationScript */ @@ -67,6 +76,14 @@ public class VCloudTemplateOptions extends TemplateOptions { return VCloudTemplateOptions.class.cast(options.customizationScript(customizationScript)); } + /** + * @see VCloudTemplateOptions#ipAddressAllocationMode + */ + public static VCloudTemplateOptions ipAddressAllocationMode(IpAddressAllocationMode ipAddressAllocationMode) { + VCloudTemplateOptions options = new VCloudTemplateOptions(); + return VCloudTemplateOptions.class.cast(options.ipAddressAllocationMode(ipAddressAllocationMode)); + } + // methods that only facilitate returning the correct object type /** * @see TemplateOptions#inboundPorts @@ -125,6 +142,13 @@ public class VCloudTemplateOptions extends TemplateOptions { return customizationScript; } + /** + * @return ipAddressAllocationMode on the vms + */ + public IpAddressAllocationMode getIpAddressAllocationMode() { + return ipAddressAllocationMode; + } + // methods that only facilitate returning the correct object type /** @@ -212,6 +236,7 @@ public class VCloudTemplateOptions extends TemplateOptions { final int prime = 31; int result = super.hashCode(); result = prime * result + ((customizationScript == null) ? 0 : customizationScript.hashCode()); + result = prime * result + ((ipAddressAllocationMode == null) ? 0 : ipAddressAllocationMode.hashCode()); return result; } @@ -229,15 +254,17 @@ public class VCloudTemplateOptions extends TemplateOptions { return false; } else if (!customizationScript.equals(other.customizationScript)) return false; + if (ipAddressAllocationMode != other.ipAddressAllocationMode) + return false; return true; } @Override public String toString() { - return "[customizationScript=" + customizationScript + ", inboundPorts=" + Arrays.toString(inboundPorts) - + ", privateKey=" + (privateKey != null) + ", publicKey=" + (publicKey != null) + ", runScript=" - + (script != null) + ", port:seconds=" + port + ":" + seconds + ", metadata/details: " + includeMetadata - + "]"; + return "[customizationScript=" + (customizationScript != null) + ", ipAddressAllocationMode=" + + ipAddressAllocationMode + ", inboundPorts=" + Arrays.toString(inboundPorts) + ", privateKey=" + + (privateKey != null) + ", publicKey=" + (publicKey != null) + ", runScript=" + (script != null) + + ", port:seconds=" + port + ":" + seconds + ", metadata/details: " + includeMetadata + "]"; } } diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java index c49fa8ebf1..095f50934a 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java @@ -38,11 +38,16 @@ import org.jclouds.logging.Logger; import org.jclouds.vcloud.VCloudClient; import org.jclouds.vcloud.compute.options.VCloudTemplateOptions; import org.jclouds.vcloud.domain.GuestCustomizationSection; +import org.jclouds.vcloud.domain.NetworkConnection; +import org.jclouds.vcloud.domain.NetworkConnectionSection; +import org.jclouds.vcloud.domain.NetworkConnectionSection.Builder; import org.jclouds.vcloud.domain.Task; import org.jclouds.vcloud.domain.VApp; import org.jclouds.vcloud.domain.Vm; +import org.jclouds.vcloud.domain.network.IpAddressAllocationMode; import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; +import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; @@ -50,7 +55,8 @@ import com.google.common.collect.Iterables; * @author Adrian Cole */ @Singleton -public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn implements CreateNodeWithGroupEncodedIntoName { +public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn implements + CreateNodeWithGroupEncodedIntoName { @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER) protected Logger logger = Logger.NULL; @@ -60,8 +66,8 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA protected final Predicate successTester; @Inject - protected InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn(Predicate successTester, VCloudClient client, - GetNodeMetadataStrategy getNode) { + protected InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn(Predicate successTester, + VCloudClient client, GetNodeMetadataStrategy getNode) { this.client = client; this.successTester = successTester; this.getNode = getNode; @@ -70,13 +76,15 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA @Override public NodeMetadata createNodeWithGroupEncodedIntoName(String tag, String name, Template template) { InstantiateVAppTemplateOptions options = processorCount((int) getCores(template.getHardware())).memory( - template.getHardware().getRam()).disk( - (long) ((template.getHardware().getVolumes().get(0).getSize()) * 1024 * 1024l)); + template.getHardware().getRam()).disk( + (long) ((template.getHardware().getVolumes().get(0).getSize()) * 1024 * 1024l)); String customizationScript = null; + IpAddressAllocationMode ipAddressAllocationMode = null; if (template.getOptions() instanceof VCloudTemplateOptions) { customizationScript = VCloudTemplateOptions.class.cast(template.getOptions()).getCustomizationScript(); - if (customizationScript != null) { + ipAddressAllocationMode = VCloudTemplateOptions.class.cast(template.getOptions()).getIpAddressAllocationMode(); + if (customizationScript != null || ipAddressAllocationMode != null) { options.customizeOnInstantiate(false); options.deploy(false); options.powerOn(false); @@ -96,39 +104,64 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA Task task = vAppResponse.getTasks().get(0); - if (customizationScript == null) { + if (customizationScript == null && ipAddressAllocationMode == null) { return blockOnDeployAndPowerOnIfConfigured(options, vAppResponse, task); } else { if (!successTester.apply(task.getHref())) { - throw new RuntimeException(String - .format("failed to %s %s: %s", "instantiate", vAppResponse.getName(), task)); + throw new RuntimeException( + String.format("failed to %s %s: %s", "instantiate", vAppResponse.getName(), task)); } Vm vm = Iterables.get(client.getVApp(vAppResponse.getHref()).getChildren(), 0); - GuestCustomizationSection guestConfiguration = vm.getGuestCustomizationSection(); - // guestConfiguration - // .setCustomizationScript(guestConfiguration.getCustomizationScript() - // != null ? - // guestConfiguration - // .getCustomizationScript() - // + "\n" + customizationScript : customizationScript); - guestConfiguration.setCustomizationScript(customizationScript); - task = client.updateGuestCustomizationOfVm(vm.getHref(), guestConfiguration); - if (!successTester.apply(task.getHref())) { - throw new RuntimeException(String.format("failed to %s %s: %s", "updateGuestCustomizationOfVm", vm - .getName(), task)); - } + if (customizationScript != null) + updateVmWithCustomizationScript(vm, customizationScript); + if (ipAddressAllocationMode != null) + updateVmWithIpAddressAllocationMode(vm, ipAddressAllocationMode); task = client.deployAndPowerOnVAppOrVm(vAppResponse.getHref()); return blockOnDeployAndPowerOnIfConfigured(options, vAppResponse, task); } } + public void updateVmWithCustomizationScript(Vm vm, String customizationScript) { + Task task; + GuestCustomizationSection guestConfiguration = vm.getGuestCustomizationSection(); + // TODO: determine if the server version is beyond 1.0.0, and if so append to, but + // not overwrite the customization script. In version 1.0.0, the api returns a script that + // loses newlines. + guestConfiguration.setCustomizationScript(customizationScript); + task = client.updateGuestCustomizationOfVm(vm.getHref(), guestConfiguration); + if (!successTester.apply(task.getHref())) { + throw new RuntimeException(String.format("failed to %s %s: %s", "updateGuestCustomizationOfVm", vm.getName(), + task)); + } + } + + public void updateVmWithIpAddressAllocationMode(Vm vm, final IpAddressAllocationMode ipAddressAllocationMode) { + Task task; + NetworkConnectionSection net = vm.getNetworkConnectionSection(); + Builder builder = net.toBuilder(); + builder.connections(Iterables.transform(net.getConnections(), + new Function() { + + @Override + public NetworkConnection apply(NetworkConnection arg0) { + return arg0.toBuilder().connected(true).ipAddressAllocationMode(ipAddressAllocationMode).build(); + } + + })); + task = client.updateNetworkConnectionOfVm(vm.getHref(), builder.build()); + if (!successTester.apply(task.getHref())) { + throw new RuntimeException(String.format("failed to %s %s: %s", "updateNetworkConnectionOfVm", vm.getName(), + task)); + } + } + private NodeMetadata blockOnDeployAndPowerOnIfConfigured(InstantiateVAppTemplateOptions options, VApp vAppResponse, - Task task) { + Task task) { if (options.shouldBlock()) { if (!successTester.apply(task.getHref())) { - throw new RuntimeException(String.format("failed to %s %s: %s", "deploy and power on", vAppResponse - .getName(), task)); + throw new RuntimeException(String.format("failed to %s %s: %s", "deploy and power on", + vAppResponse.getName(), task)); } logger.debug("<< ready vApp(%s)", vAppResponse.getName()); } diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/domain/NetworkConnection.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/domain/NetworkConnection.java index 061367331a..a0c1c95daa 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/domain/NetworkConnection.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/domain/NetworkConnection.java @@ -29,6 +29,67 @@ import org.jclouds.vcloud.domain.network.IpAddressAllocationMode; * @author Adrian Cole */ public class NetworkConnection { + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String network; + private int networkConnectionIndex; + private String ipAddress; + private String externalIpAddress; + private boolean connected; + private String MACAddress; + private IpAddressAllocationMode ipAddressAllocationMode; + + public Builder network(String network) { + this.network = network; + return this; + } + + public Builder networkConnectionIndex(int networkConnectionIndex) { + this.networkConnectionIndex = networkConnectionIndex; + return this; + } + + public Builder ipAddress(String ipAddress) { + this.ipAddress = ipAddress; + return this; + } + + public Builder externalIpAddress(String externalIpAddress) { + this.externalIpAddress = externalIpAddress; + return this; + } + + public Builder connected(boolean connected) { + this.connected = connected; + return this; + } + + public Builder MACAddress(String MACAddress) { + this.MACAddress = MACAddress; + return this; + } + + public Builder ipAddressAllocationMode(IpAddressAllocationMode ipAddressAllocationMode) { + this.ipAddressAllocationMode = ipAddressAllocationMode; + return this; + } + + public NetworkConnection build() { + return new NetworkConnection(network, networkConnectionIndex, ipAddress, externalIpAddress, connected, + MACAddress, ipAddressAllocationMode); + } + + public static Builder fromNetworkConnection(NetworkConnection in) { + return new Builder().network(in.getNetwork()).networkConnectionIndex(in.getNetworkConnectionIndex()) + .ipAddress(in.getIpAddress()).externalIpAddress(in.getExternalIpAddress()).connected(in.isConnected()) + .MACAddress(in.getMACAddress()).ipAddressAllocationMode(in.getIpAddressAllocationMode()); + } + } + private final String network; private final int networkConnectionIndex; @Nullable @@ -41,8 +102,8 @@ public class NetworkConnection { private final IpAddressAllocationMode ipAddressAllocationMode; public NetworkConnection(String network, int networkConnectionIndex, @Nullable String ipAddress, - @Nullable String externalIpAddress, boolean connected, @Nullable String MACAddress, - IpAddressAllocationMode ipAddressAllocationMode) { + @Nullable String externalIpAddress, boolean connected, @Nullable String MACAddress, + IpAddressAllocationMode ipAddressAllocationMode) { this.network = network; this.networkConnectionIndex = networkConnectionIndex; this.ipAddress = ipAddress; @@ -162,11 +223,15 @@ public class NetworkConnection { return true; } + public Builder toBuilder() { + return Builder.fromNetworkConnection(this); + } + @Override public String toString() { return "[network=" + network + ", connected=" + connected + ", ipAddress=" + ipAddress + ", externalIpAddress=" - + externalIpAddress + ", networkConnectionIndex=" + networkConnectionIndex - + ", ipAddressAllocationMode=" + ipAddressAllocationMode + ", MACAddress=" + MACAddress + "]"; + + externalIpAddress + ", networkConnectionIndex=" + networkConnectionIndex + ", ipAddressAllocationMode=" + + ipAddressAllocationMode + ", MACAddress=" + MACAddress + "]"; } } \ No newline at end of file diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/domain/NetworkConnectionSection.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/domain/NetworkConnectionSection.java index 5c693210f7..47e19eb938 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/domain/NetworkConnectionSection.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/domain/NetworkConnectionSection.java @@ -24,8 +24,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.net.URI; import java.util.Set; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; +import com.google.common.collect.ImmutableSet; /** * The NetworkConnectionSection element specifies how a Vm is connected to a vApp network. It @@ -36,20 +35,73 @@ import com.google.common.collect.Sets; * values specified in the NetworkConnectionSection override those specified in the NetworkSection. */ public class NetworkConnectionSection { + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + protected String type; + protected URI href; + protected String info; + protected Integer primaryNetworkConnectionIndex; + protected Set connections = ImmutableSet.of(); + protected ReferenceType edit; + + public Builder type(String type) { + this.type = type; + return this; + } + + public Builder href(URI href) { + this.href = href; + return this; + } + + public Builder info(String info) { + this.info = info; + return this; + } + + public Builder primaryNetworkConnectionIndex(Integer primaryNetworkConnectionIndex) { + this.primaryNetworkConnectionIndex = primaryNetworkConnectionIndex; + return this; + } + + public Builder connections(Iterable connections) { + this.connections = ImmutableSet.copyOf(checkNotNull(connections, "connections")); + return this; + } + + public Builder edit(ReferenceType edit) { + this.edit = edit; + return this; + } + + public NetworkConnectionSection build() { + return new NetworkConnectionSection(type, href, info, primaryNetworkConnectionIndex, connections, edit); + } + + public static Builder fromNetworkConnectionSection(NetworkConnectionSection in) { + return new Builder().type(in.getType()).href(in.getHref()).info(in.getInfo()) + .primaryNetworkConnectionIndex(in.getPrimaryNetworkConnectionIndex()).connections(in.getConnections()) + .edit(in.getEdit()); + } + } + protected final String type; protected final URI href; protected final String info; protected final Integer primaryNetworkConnectionIndex; - protected final Set connections = Sets.newLinkedHashSet(); + protected final Set connections; protected final ReferenceType edit; public NetworkConnectionSection(String type, URI href, String info, Integer primaryNetworkConnectionIndex, - Iterable connections, ReferenceType edit) { + Iterable connections, ReferenceType edit) { this.type = type; this.href = href; this.info = info; this.primaryNetworkConnectionIndex = primaryNetworkConnectionIndex; - Iterables.addAll(this.connections, checkNotNull(connections, "connections")); + this.connections = ImmutableSet.copyOf(checkNotNull(connections, "connections")); this.edit = edit; } @@ -109,7 +161,7 @@ public class NetworkConnectionSection { result = prime * result + ((href == null) ? 0 : href.hashCode()); result = prime * result + ((info == null) ? 0 : info.hashCode()); result = prime * result - + ((primaryNetworkConnectionIndex == null) ? 0 : primaryNetworkConnectionIndex.hashCode()); + + ((primaryNetworkConnectionIndex == null) ? 0 : primaryNetworkConnectionIndex.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; } @@ -156,10 +208,14 @@ public class NetworkConnectionSection { return true; } + public Builder toBuilder() { + return Builder.fromNetworkConnectionSection(this); + } + @Override public String toString() { return "[href=" + href + ", connections=" + connections + ", primaryNetworkConnectionIndex=" - + primaryNetworkConnectionIndex + "]"; + + primaryNetworkConnectionIndex + "]"; } } \ No newline at end of file diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindNetworkConnectionSectionToXmlPayloadTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindNetworkConnectionSectionToXmlPayloadTest.java new file mode 100644 index 0000000000..e9845644e9 --- /dev/null +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/binders/BindNetworkConnectionSectionToXmlPayloadTest.java @@ -0,0 +1,86 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.vcloud.binders; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.URI; +import java.util.Properties; + +import org.jclouds.http.HttpRequest; +import org.jclouds.vcloud.VCloudPropertiesBuilder; +import org.jclouds.vcloud.domain.NetworkConnection; +import org.jclouds.vcloud.domain.NetworkConnectionSection; +import org.jclouds.vcloud.domain.network.IpAddressAllocationMode; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.name.Names; + +/** + * Tests behavior of {@code BindNetworkConnectionSectionToXmlPayload} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class BindNetworkConnectionSectionToXmlPayloadTest { + Injector injector = Guice.createInjector(new AbstractModule() { + + @Override + protected void configure() { + Properties props = new Properties(); + Names.bindProperties(binder(), checkNotNull(new VCloudPropertiesBuilder(props).build(), "properties")); + } + }); + + public void testWithIpAllocationModeNONE() throws IOException { + + @SuppressWarnings("rawtypes") + HttpRequest request = new HttpRequest.Builder().endpoint(URI.create("http://localhost/key")).method("GET") + .build(); + + BindNetworkConnectionSectionToXmlPayload binder = injector + .getInstance(BindNetworkConnectionSectionToXmlPayload.class); + + binder.bindToRequest( + request, + NetworkConnectionSection + .builder() + .type("application/vnd.vmware.vcloud.networkConnectionSection+xml") + .info("Specifies the available VM network connections") + .href(URI.create("https://1.1.1.1/api/v1.0/vApp/vm-1/networkConnectionSection/")) + .connections( + ImmutableSet. of(NetworkConnection.builder().network("none") + .ipAddressAllocationMode(IpAddressAllocationMode.NONE).build())).build()); + assertEquals(request.getPayload().getContentMetadata().getContentType(), + "application/vnd.vmware.vcloud.networkConnectionSection+xml"); + + assertEquals( + request.getPayload().getRawContent(), + "Specifies the available VM network connections0falseNONE"); + + } + +} diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java index d1c4333234..5fac22935e 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java @@ -24,12 +24,14 @@ import static org.jclouds.vcloud.compute.options.VCloudTemplateOptions.Builder.b import static org.jclouds.vcloud.compute.options.VCloudTemplateOptions.Builder.customizationScript; import static org.jclouds.vcloud.compute.options.VCloudTemplateOptions.Builder.inboundPorts; import static org.jclouds.vcloud.compute.options.VCloudTemplateOptions.Builder.installPrivateKey; +import static org.jclouds.vcloud.compute.options.VCloudTemplateOptions.Builder.ipAddressAllocationMode; import static org.testng.Assert.assertEquals; import java.io.IOException; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.io.Payloads; +import org.jclouds.vcloud.domain.network.IpAddressAllocationMode; import org.testng.annotations.Test; /** @@ -38,6 +40,18 @@ import org.testng.annotations.Test; * @author Adrian Cole */ public class VCloudTemplateOptionsTest { + @Test + public void testipAddressAllocationMode() { + VCloudTemplateOptions options = new VCloudTemplateOptions(); + options.ipAddressAllocationMode(IpAddressAllocationMode.NONE); + assertEquals(options.getIpAddressAllocationMode(), IpAddressAllocationMode.NONE); + } + + @Test + public void testipAddressAllocationModeStatic() { + VCloudTemplateOptions options = ipAddressAllocationMode(IpAddressAllocationMode.NONE); + assertEquals(options.getIpAddressAllocationMode(), IpAddressAllocationMode.NONE); + } public void testAs() { TemplateOptions options = new VCloudTemplateOptions(); @@ -50,6 +64,12 @@ public class VCloudTemplateOptionsTest { options.customizationScript(""); } + @Test + public void testNullcustomizationScript() { + VCloudTemplateOptions options = new VCloudTemplateOptions(); + assertEquals(options.getCustomizationScript(), null); + } + @Test public void testcustomizationScript() { VCloudTemplateOptions options = new VCloudTemplateOptions(); @@ -57,12 +77,6 @@ public class VCloudTemplateOptionsTest { assertEquals(options.getCustomizationScript(), "mykeypair"); } - @Test - public void testNullcustomizationScript() { - VCloudTemplateOptions options = new VCloudTemplateOptions(); - assertEquals(options.getCustomizationScript(), null); - } - @Test public void testcustomizationScriptStatic() { VCloudTemplateOptions options = customizationScript("mykeypair"); From e3e0ad3e843bd9fc4a19bd6c50802ad17d864661 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 5 Mar 2011 16:56:33 -0500 Subject: [PATCH 06/13] add public ips to collection for vcloud --- .../org/jclouds/vcloud/compute/util/VCloudComputeUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/util/VCloudComputeUtils.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/util/VCloudComputeUtils.java index 9cd87d9f7d..a5d19e7c15 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/util/VCloudComputeUtils.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/util/VCloudComputeUtils.java @@ -102,6 +102,8 @@ public class VCloudComputeUtils { for (NetworkConnection connection : vm.getNetworkConnectionSection().getConnections()) { if (connection.getIpAddress() != null) ips.add(connection.getIpAddress()); + if (connection.getExternalIpAddress() != null) + ips.add(connection.getExternalIpAddress()); } } else { for (ResourceAllocation net : filter(vm.getVirtualHardwareSection().getResourceAllocations(), From 59c0dbb6cd6e219e28fd21da8b37f4f28ba727ee Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 5 Mar 2011 17:03:54 -0500 Subject: [PATCH 07/13] Issue 497:revise template options on a per-provider basis --- .../internal/EC2TemplateBuilderImpl.java | 9 +- .../internal/VCloudTemplateBuilderImpl.java | 8 +- .../GuestCustomizationSectionHandlerTest.java | 17 ++-- .../domain/internal/TemplateBuilderImpl.java | 89 +++++++++---------- .../compute/AWSEC2TemplateBuilderImpl.java | 6 +- 5 files changed, 65 insertions(+), 64 deletions(-) diff --git a/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java b/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java index a2f87d1733..8b534d3323 100644 --- a/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java +++ b/apis/ec2/src/main/java/org/jclouds/ec2/compute/internal/EC2TemplateBuilderImpl.java @@ -52,9 +52,9 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl { @Inject protected EC2TemplateBuilderImpl(@Memoized Supplier> locations, - @Memoized Supplier> images, @Memoized Supplier> sizes, - Supplier defaultLocation, Provider optionsProvider, - @Named("DEFAULT") Provider defaultTemplateProvider, Map imageMap) { + @Memoized Supplier> images, @Memoized Supplier> sizes, + Supplier defaultLocation, @Named("DEFAULT") Provider optionsProvider, + @Named("DEFAULT") Provider defaultTemplateProvider, Map imageMap) { super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider); this.imageMap = imageMap; } @@ -85,8 +85,7 @@ public class EC2TemplateBuilderImpl extends TemplateBuilderImpl { if (imageId != null) { String[] regionName = imageId.split("/"); checkArgument(regionName.length == 2, - "amazon image ids must include the region ( ex. us-east-1/ami-7ea24a17 ) you specified: " - + imageId); + "amazon image ids must include the region ( ex. us-east-1/ami-7ea24a17 ) you specified: " + imageId); RegionAndName key = new RegionAndName(regionName[0], regionName[1]); try { return imageMap.get(key); diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/internal/VCloudTemplateBuilderImpl.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/internal/VCloudTemplateBuilderImpl.java index 4ea5afa141..9383a4d3e4 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/internal/VCloudTemplateBuilderImpl.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/compute/internal/VCloudTemplateBuilderImpl.java @@ -44,9 +44,9 @@ public class VCloudTemplateBuilderImpl extends TemplateBuilderImpl { @Inject protected VCloudTemplateBuilderImpl(@Memoized Supplier> locations, - @Memoized Supplier> images, @Memoized Supplier> sizes, - Supplier defaultLocation, Provider optionsProvider, - @Named("DEFAULT") Provider defaultTemplateProvider) { + @Memoized Supplier> images, @Memoized Supplier> sizes, + Supplier defaultLocation, @Named("DEFAULT") Provider optionsProvider, + @Named("DEFAULT") Provider defaultTemplateProvider) { super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider); } @@ -58,6 +58,8 @@ public class VCloudTemplateBuilderImpl extends TemplateBuilderImpl { VCloudTemplateOptions eTo = VCloudTemplateOptions.class.cast(to); if (eFrom.getCustomizationScript() != null) eTo.customizationScript(eFrom.getCustomizationScript()); + if (eFrom.getIpAddressAllocationMode() != null) + eTo.ipAddressAllocationMode(eFrom.getIpAddressAllocationMode()); } } diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/GuestCustomizationSectionHandlerTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/GuestCustomizationSectionHandlerTest.java index 17a4c7b0fb..17fd4ce9e9 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/GuestCustomizationSectionHandlerTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/xml/GuestCustomizationSectionHandlerTest.java @@ -43,7 +43,8 @@ public class GuestCustomizationSectionHandlerTest extends BaseHandlerTest { public void testDefault() throws UnknownHostException { InputStream is = getClass().getResourceAsStream("/guestCustomization.xml"); - GuestCustomizationSection result = factory.create(injector.getInstance(GuestCustomizationSectionHandler.class)).parse(is); + GuestCustomizationSection result = factory.create(injector.getInstance(GuestCustomizationSectionHandler.class)) + .parse(is); checkGuestCustomization(result); @@ -52,8 +53,8 @@ public class GuestCustomizationSectionHandlerTest extends BaseHandlerTest { @Test(enabled = false) public static void checkGuestCustomization(GuestCustomizationSection result) { assertEquals(result.getType(), VCloudMediaType.GUESTCUSTOMIZATIONSECTION_XML); - assertEquals(result.getHref(), URI - .create("https://vcenterprise.bluelock.com/api/v1.0/vApp/vm-2087535248/guestCustomizationSection/")); + assertEquals(result.getHref(), + URI.create("https://vcenterprise.bluelock.com/api/v1.0/vApp/vm-2087535248/guestCustomizationSection/")); assertEquals(result.getInfo(), "Specifies Guest OS Customization Settings"); assertEquals(result.isEnabled(), new Boolean(true)); assertEquals(result.shouldChangeSid(), new Boolean(false)); @@ -67,11 +68,11 @@ public class GuestCustomizationSectionHandlerTest extends BaseHandlerTest { assertEquals(result.isAdminPasswordAuto(), new Boolean(true)); assertEquals(result.getAdminPassword(), null); assertEquals(result.isResetPasswordRequired(), new Boolean(false)); - assertEquals( - result.getCustomizationScript(), - "#!/bin/bash if [[ $1 == \"postcustomization\" ]]; then echo \"post customization\" touch /root/.postcustomization ping www.redhat.com -c 1 sleep 30 # register with RHN /usr/sbin/rhnreg_ks --profilename vic_`hostname`_`dmidecode -s system-uuid` --activationkey=XXXXXXXXXXXX --force echo \"rhn registered\" # make hostname fully qualified to speed up sendmail start perl -i -pe \"s/`hostname`/`hostname`.victory.blk/g\" /etc/sysconfig/network rm /etc/ssh/*_key* service sshd restart echo \"completed\" fi"); + assertEquals(result.getCustomizationScript(), "cat > /root/foo.txt<> locations, - @Memoized Supplier> images, @Memoized Supplier> hardwares, - Supplier defaultLocation2, Provider optionsProvider, - @Named("DEFAULT") Provider defaultTemplateProvider) { + @Memoized Supplier> images, @Memoized Supplier> hardwares, + Supplier defaultLocation2, @Named("DEFAULT") Provider optionsProvider, + @Named("DEFAULT") Provider defaultTemplateProvider) { this.locations = locations; this.images = images; this.hardwares = hardwares; @@ -140,8 +140,8 @@ public class TemplateBuilderImpl implements TemplateBuilder { boolean returnVal = true; if (location != null && input.getLocation() != null) returnVal = location.equals(input.getLocation()) || location.getParent() != null - && location.getParent().equals(input.getLocation()) || location.getParent().getParent() != null - && location.getParent().getParent().equals(input.getLocation()); + && location.getParent().equals(input.getLocation()) || location.getParent().getParent() != null + && location.getParent().getParent().equals(input.getLocation()); return returnVal; } @@ -215,7 +215,7 @@ public class TemplateBuilderImpl implements TemplateBuilder { returnVal = false; else returnVal = input.getDescription().contains(osDescription) - || input.getDescription().matches(osDescription); + || input.getDescription().matches(osDescription); } return returnVal; } @@ -329,8 +329,8 @@ public class TemplateBuilderImpl implements TemplateBuilder { returnVal = false; else returnVal = input.getDescription().equals(imageDescription) - || input.getDescription().contains(imageDescription) - || input.getDescription().matches(imageDescription); + || input.getDescription().contains(imageDescription) + || input.getDescription().matches(imageDescription); } return returnVal; } @@ -385,12 +385,12 @@ public class TemplateBuilderImpl implements TemplateBuilder { } }; private final Predicate hardwarePredicate = and(hardwareIdPredicate, locationPredicate, - hardwareCoresPredicate, hardwareRamPredicate); + hardwareCoresPredicate, hardwareRamPredicate); static final Ordering DEFAULT_SIZE_ORDERING = new Ordering() { public int compare(Hardware left, Hardware right) { return ComparisonChain.start().compare(getCores(left), getCores(right)).compare(left.getRam(), right.getRam()) - .compare(getSpace(left), getSpace(right)).result(); + .compare(getSpace(left), getSpace(right)).result(); } }; static final Ordering BY_CORES_ORDERING = new Ordering() { @@ -400,16 +400,16 @@ public class TemplateBuilderImpl implements TemplateBuilder { }; static final Ordering DEFAULT_IMAGE_ORDERING = new Ordering() { public int compare(Image left, Image right) { - return ComparisonChain.start().compare(left.getName(), right.getName(), - Ordering. natural().nullsLast()).compare(left.getVersion(), right.getVersion(), - Ordering. natural().nullsLast()).compare(left.getOperatingSystem().getName(), - right.getOperatingSystem().getName(),// - Ordering. natural().nullsLast()).compare(left.getOperatingSystem().getVersion(), - right.getOperatingSystem().getVersion(),// - Ordering. natural().nullsLast()).compare(left.getOperatingSystem().getDescription(), - right.getOperatingSystem().getDescription(),// - Ordering. natural().nullsLast()).compare(left.getOperatingSystem().getArch(), - right.getOperatingSystem().getArch()).result(); + return ComparisonChain.start() + .compare(left.getName(), right.getName(), Ordering. natural().nullsLast()) + .compare(left.getVersion(), right.getVersion(), Ordering. natural().nullsLast()) + .compare(left.getOperatingSystem().getName(), right.getOperatingSystem().getName(),// + Ordering. natural().nullsLast()) + .compare(left.getOperatingSystem().getVersion(), right.getOperatingSystem().getVersion(),// + Ordering. natural().nullsLast()) + .compare(left.getOperatingSystem().getDescription(), right.getOperatingSystem().getDescription(),// + Ordering. natural().nullsLast()) + .compare(left.getOperatingSystem().getArch(), right.getOperatingSystem().getArch()).result(); } }; @@ -549,7 +549,7 @@ public class TemplateBuilderImpl implements TemplateBuilder { Iterable supportedImages = filter(images, buildImagePredicate()); if (Iterables.size(supportedImages) == 0) throw new NoSuchElementException(String.format( - "no image matched predicate %s images that didn't match below:\n%s", imagePredicate, images)); + "no image matched predicate %s images that didn't match below:\n%s", imagePredicate, images)); Hardware hardware = resolveSize(hardwareSorter(), supportedImages); Image image = resolveImage(hardware, supportedImages); logger.debug("<< matched image(%s)", image); @@ -562,29 +562,29 @@ public class TemplateBuilderImpl implements TemplateBuilder { Hardware hardware; try { Iterable hardwaresThatAreCompatibleWithOurImages = filter(hardwaresl, - new Predicate() { - @Override - public boolean apply(final Hardware hardware) { - return Iterables.any(images, new Predicate() { + new Predicate() { + @Override + public boolean apply(final Hardware hardware) { + return Iterables.any(images, new Predicate() { - @Override - public boolean apply(Image input) { - return hardware.supportsImage().apply(input); - } + @Override + public boolean apply(Image input) { + return hardware.supportsImage().apply(input); + } - @Override - public String toString() { - return "hardware(" + hardware + ").supportsImage()"; - } + @Override + public String toString() { + return "hardware(" + hardware + ").supportsImage()"; + } - }); + }); - } - }); + } + }); hardware = hardwareOrdering.max(filter(hardwaresThatAreCompatibleWithOurImages, hardwarePredicate)); } catch (NoSuchElementException exception) { throw new NoSuchElementException("hardwares don't support any images: " + toString() + "\n" + hardwaresl - + "\n" + images); + + "\n" + images); } logger.debug("<< matched hardware(%s)", hardware); return hardware; @@ -692,7 +692,7 @@ public class TemplateBuilderImpl implements TemplateBuilder { // looks verbose, but explicit type needed for this to compile // properly Predicate imagePredicate = predicates.size() == 1 ? Iterables.> get(predicates, 0) - : Predicates. and(predicates); + : Predicates. and(predicates); return imagePredicate; } @@ -840,9 +840,8 @@ public class TemplateBuilderImpl implements TemplateBuilder { @VisibleForTesting boolean nothingChangedExceptOptions() { return osFamily == null && location == null && imageId == null && hardwareId == null && osName == null - && osDescription == null && imageVersion == null && osVersion == null && osArch == null - && os64Bit == null && imageName == null && imageDescription == null && minCores == 0 && minRam == 0 - && !biggest && !fastest; + && osDescription == null && imageVersion == null && osVersion == null && osArch == null && os64Bit == null + && imageName == null && imageDescription == null && minCores == 0 && minRam == 0 && !biggest && !fastest; } /** @@ -856,10 +855,10 @@ public class TemplateBuilderImpl implements TemplateBuilder { @Override public String toString() { return "[biggest=" + biggest + ", fastest=" + fastest + ", imageName=" + imageName + ", imageDescription=" - + imageDescription + ", imageId=" + imageId + ", imageVersion=" + imageVersion + ", location=" - + location + ", minCores=" + minCores + ", minRam=" + minRam + ", osFamily=" + osFamily + ", osName=" - + osName + ", osDescription=" + osDescription + ", osVersion=" + osVersion + ", osArch=" + osArch - + ", os64Bit=" + os64Bit + ", hardwareId=" + hardwareId + "]"; + + imageDescription + ", imageId=" + imageId + ", imageVersion=" + imageVersion + ", location=" + location + + ", minCores=" + minCores + ", minRam=" + minRam + ", osFamily=" + osFamily + ", osName=" + osName + + ", osDescription=" + osDescription + ", osVersion=" + osVersion + ", osArch=" + osArch + ", os64Bit=" + + os64Bit + ", hardwareId=" + hardwareId + "]"; } @Override diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java index fd41fc3a84..d01d2f1572 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/compute/AWSEC2TemplateBuilderImpl.java @@ -45,9 +45,9 @@ public class AWSEC2TemplateBuilderImpl extends EC2TemplateBuilderImpl { @Inject protected AWSEC2TemplateBuilderImpl(@Memoized Supplier> locations, - @Memoized Supplier> images, @Memoized Supplier> sizes, - Supplier defaultLocation, Provider optionsProvider, - @Named("DEFAULT") Provider defaultTemplateProvider, Map imageMap) { + @Memoized Supplier> images, @Memoized Supplier> sizes, + Supplier defaultLocation, @Named("DEFAULT") Provider optionsProvider, + @Named("DEFAULT") Provider defaultTemplateProvider, Map imageMap) { super(locations, images, sizes, defaultLocation, optionsProvider, defaultTemplateProvider, imageMap); } From d494c8a9f31e467e8d8f389860940d43390f4db3 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 5 Mar 2011 17:06:05 -0500 Subject: [PATCH 08/13] Issue 494:vCloud director 1.0.1 fixes guest customization bug --- .../xml/GuestCustomizationSectionHandler.java | 12 +-- .../VCloudGuestCustomizationLiveTest.java | 102 +++++++++++++----- .../src/test/resources/guestCustomization.xml | 5 +- apis/vcloud/src/test/resources/vapp-none.xml | 4 +- .../test/resources/vapp-rhel-off-static.xml | 5 +- .../src/test/resources/vm-rhel-off-static.xml | 5 +- .../BaseComputeServiceContextModule.java | 9 ++ ...oudDirectorGuestCustomizationLiveTest.java | 4 + 8 files changed, 107 insertions(+), 39 deletions(-) diff --git a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/GuestCustomizationSectionHandler.java b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/GuestCustomizationSectionHandler.java index 0b341e1b20..7fb899e35f 100644 --- a/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/GuestCustomizationSectionHandler.java +++ b/apis/vcloud/src/main/java/org/jclouds/vcloud/xml/GuestCustomizationSectionHandler.java @@ -56,9 +56,9 @@ public class GuestCustomizationSectionHandler extends ParseSax.HandlerWithResult public GuestCustomizationSection getResult() { GuestCustomizationSection system = new GuestCustomizationSection(guest.getType(), guest.getHref(), info, enabled, - changeSid, virtualMachineId, joinDomainEnabled, useOrgSettings, domainName, domainUserName, - domainUserPassword, adminPasswordEnabled, adminPasswordAuto, adminPassword, resetPasswordRequired, - customizationScript, computerName, edit); + changeSid, virtualMachineId, joinDomainEnabled, useOrgSettings, domainName, domainUserName, + domainUserPassword, adminPasswordEnabled, adminPasswordAuto, adminPassword, resetPasswordRequired, + customizationScript, computerName, edit); this.guest = null; this.info = null; this.edit = null; @@ -81,6 +81,7 @@ public class GuestCustomizationSectionHandler extends ParseSax.HandlerWithResult public void startElement(String uri, String localName, String qName, Attributes attrs) { Map attributes = cleanseAttributes(attrs); + this.currentText = new StringBuilder(); if (qName.endsWith("GuestCustomizationSection")) { guest = newReferenceType(attributes); } else if (qName.endsWith("Link") && "edit".equals(attributes.get("rel"))) { @@ -119,8 +120,7 @@ public class GuestCustomizationSectionHandler extends ParseSax.HandlerWithResult } else if (qName.endsWith("CustomizationScript")) { this.customizationScript = currentOrNull(); if (this.customizationScript != null) - customizationScript = customizationScript.replace("<", "<").replace(">", ">").replace(""", "\"") - .replace("'", "'").replace(" ", "\r\n").replace(" ", "\n").replace("&", "&"); + customizationScript = customizationScript.replace(">", ">"); } else if (qName.endsWith("ComputerName")) { this.computerName = currentOrNull(); } else if (qName.endsWith("Name")) { @@ -134,7 +134,7 @@ public class GuestCustomizationSectionHandler extends ParseSax.HandlerWithResult } protected String currentOrNull() { - String returnVal = currentText.toString().trim(); + String returnVal = currentText.toString(); return returnVal.equals("") ? null : returnVal; } } diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudGuestCustomizationLiveTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudGuestCustomizationLiveTest.java index 09acac0efe..c90300743a 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudGuestCustomizationLiveTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/VCloudGuestCustomizationLiveTest.java @@ -22,6 +22,7 @@ package org.jclouds.vcloud; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Iterables.get; import static com.google.common.collect.Iterables.getOnlyElement; +import static org.jclouds.crypto.CryptoStreams.base64; import static org.testng.Assert.assertEquals; import java.util.Properties; @@ -31,26 +32,31 @@ import org.jclouds.Constants; import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.options.TemplateOptions; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.net.IPSocket; import org.jclouds.predicates.InetSocketAddressConnect; import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.rest.RestContextFactory; import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshClient.Factory; import org.jclouds.ssh.jsch.config.JschSshClientModule; import org.jclouds.vcloud.compute.options.VCloudTemplateOptions; +import org.jclouds.vcloud.domain.Vm; +import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.inject.Guice; import com.google.inject.Module; /** * This tests that we can use guest customization as an alternative to bootstrapping with ssh. There - * are a few advangroupes to this, including the fact that it can work inside google appengine where + * are a few advantages to this, including the fact that it can work inside google appengine where * network sockets (ssh:22) are prohibited. * * @author Adrian Cole @@ -58,7 +64,7 @@ import com.google.inject.Module; @Test(groups = "live", enabled = true, sequential = true) public class VCloudGuestCustomizationLiveTest { - public static final String PARSE_VMTOOLSD = "vmtoolsd --cmd=\"info-get guestinfo.ovfenv\" |grep vCloud_CustomizationInfo|sed 's/.*value=\"\\(.*\\)\".*/\\1/g'|base64 -d"; + public static final String PARSE_VMTOOLSD = "vmtoolsd --cmd=\"info-get guestinfo.ovfenv\" |grep vCloud_CustomizationInfo|sed 's/.*value=\"\\(.*\\)\".*/\\1/g'"; protected ComputeServiceContext context; protected ComputeService client; @@ -71,6 +77,8 @@ public class VCloudGuestCustomizationLiveTest { protected String endpoint; protected String apiversion; + protected NodeMetadata node; + protected void setupCredentials() { identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity"); credential = System.getProperty("test." + provider + ".credential"); @@ -96,51 +104,89 @@ public class VCloudGuestCustomizationLiveTest { public void setupClient() { setupCredentials(); Properties overrides = setupProperties(); - - client = new ComputeServiceContextFactory().createContext(provider, + client = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider, ImmutableSet. of(new Log4JLoggingModule()), overrides).getComputeService(); - socketTester = new RetryablePredicate(new InetSocketAddressConnect(), 60, 1, TimeUnit.SECONDS); + socketTester = new RetryablePredicate(new InetSocketAddressConnect(), 300, 1, TimeUnit.SECONDS); sshFactory = Guice.createInjector(getSshModule()).getInstance(Factory.class); } + protected Properties setupRestProperties() { + return RestContextFactory.getPropertiesFromResource("/rest.properties"); + } + protected JschSshClientModule getSshModule() { return new JschSshClientModule(); } + // make sure the script has a lot of screwy characters, knowing our parser throws-out \r + private String iLoveAscii = "I '\"love\"' {asc|!}*&"; + + String script = "cat > /root/foo.txt<true true false - #!/bin/bash if [[ $1 == "postcustomization" ]]; then echo "post customization" touch /root/.postcustomization ping www.redhat.com -c 1 sleep 30 # register with RHN /usr/sbin/rhnreg_ks --profilename vic_`hostname`_`dmidecode -s system-uuid` --activationkey=XXXXXXXXXXXX --force echo "rhn registered" # make hostname fully qualified to speed up sendmail start perl -i -pe "s/`hostname`/`hostname`.victory.blk/g" /etc/sysconfig/network rm /etc/ssh/*_key* service sshd restart echo "completed" fi + cat > /root/foo.txt<<EOF +I '"love"' {asc|!}*& +EOF + RHEL5 This is a special place-holder used for disconnected network interfaces. - + + The configuration parameters for logical networks diff --git a/apis/vcloud/src/test/resources/vapp-rhel-off-static.xml b/apis/vcloud/src/test/resources/vapp-rhel-off-static.xml index ac5046390e..6da1abe907 100644 --- a/apis/vcloud/src/test/resources/vapp-rhel-off-static.xml +++ b/apis/vcloud/src/test/resources/vapp-rhel-off-static.xml @@ -310,7 +310,10 @@ true true false - #!/bin/bash if [[ $1 == "postcustomization" ]]; then echo "post customization" touch /root/.postcustomization ping www.redhat.com -c 1 sleep 30 # register with RHN /usr/sbin/rhnreg_ks --profilename vic_`hostname`_`dmidecode -s system-uuid` --activationkey=XXXXXXXXXXXX --force echo "rhn registered" # make hostname fully qualified to speed up sendmail start perl -i -pe "s/`hostname`/`hostname`.victory.blk/g" /etc/sysconfig/network rm /etc/ssh/*_key* service sshd restart echo "completed" fi + cat > /root/foo.txt<<EOF +I '"love"' {asc|!}*& +EOF + RHEL5 true true false - #!/bin/bash if [[ $1 == "postcustomization" ]]; then echo "post customization" touch /root/.postcustomization ping www.redhat.com -c 1 sleep 30 # register with RHN /usr/sbin/rhnreg_ks --profilename vic_`hostname`_`dmidecode -s system-uuid` --activationkey=XXXXXXXXXXXX --force echo "rhn registered" # make hostname fully qualified to speed up sendmail start perl -i -pe "s/`hostname`/`hostname`.victory.blk/g" /etc/sysconfig/network rm /etc/ssh/*_key* service sshd restart echo "completed" fi + cat > /root/foo.txt<<EOF +I '"love"' {asc|!}*& +EOF + RHEL5 Date: Sat, 5 Mar 2011 20:56:54 -0500 Subject: [PATCH 09/13] added rest properties to vcloud test --- .../vcloud/compute/VCloudComputeServiceLiveTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/VCloudComputeServiceLiveTest.java b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/VCloudComputeServiceLiveTest.java index 92e22a12fd..6acb4e4b6d 100644 --- a/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/VCloudComputeServiceLiveTest.java +++ b/apis/vcloud/src/test/java/org/jclouds/vcloud/compute/VCloudComputeServiceLiveTest.java @@ -33,6 +33,9 @@ import org.jclouds.vcloud.VCloudClient; import org.jclouds.vcloud.domain.VApp; import org.testng.annotations.Test; +import com.google.common.collect.ImmutableSet; +import com.google.inject.Module; + /** * * @@ -65,7 +68,8 @@ public class VCloudComputeServiceLiveTest extends BaseComputeServiceLiveTest { NodeMetadata allData = client.getNodeMetadata(node.getId()); System.out.println(allData.getHardware()); RestContext tmContext = new ComputeServiceContextFactory( - setupRestProperties()).createContext(provider, identity, credential).getProviderSpecificContext(); + setupRestProperties()).createContext(provider, identity, credential, ImmutableSet. of(), + setupProperties()).getProviderSpecificContext(); VApp vApp = tmContext.getApi().findVAppInOrgVDCNamed(null, null, allData.getName()); assertEquals(vApp.getName(), allData.getName()); } From a988b476274e576597d80fcaa0c1481dc35b3310 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 5 Mar 2011 20:57:19 -0500 Subject: [PATCH 10/13] Issue 471:add better error message when there's an interrupted exception --- .../java/org/jclouds/concurrent/Futures.java | 123 ++++++++++++------ .../org/jclouds/concurrent/FuturesTest.java | 93 +++++++++++++ 2 files changed, 173 insertions(+), 43 deletions(-) create mode 100644 core/src/test/java/org/jclouds/concurrent/FuturesTest.java diff --git a/core/src/main/java/org/jclouds/concurrent/Futures.java b/core/src/main/java/org/jclouds/concurrent/Futures.java index f74ffbb9c1..139d72a5f2 100644 --- a/core/src/main/java/org/jclouds/concurrent/Futures.java +++ b/core/src/main/java/org/jclouds/concurrent/Futures.java @@ -21,7 +21,6 @@ package org.jclouds.concurrent; import static com.google.common.base.Preconditions.checkNotNull; -import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -31,6 +30,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import com.google.common.annotations.Beta; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.collect.ForwardingObject; import com.google.common.util.concurrent.ExecutionList; @@ -44,54 +44,90 @@ import com.google.common.util.concurrent.ListenableFuture; */ @Beta public class Futures { + @VisibleForTesting + static class CallGetAndRunExecutionList implements Runnable { + private final Future delegate; + private final ExecutionList executionList; - public static class FutureListener { - private final Future future; - final ExecutorService executor; - private final ExecutionList executionList = new ExecutionList(); - private final AtomicBoolean hasListeners = new AtomicBoolean(false); - - static FutureListener create(Future future, ExecutorService executor) { - return new FutureListener(future, executor); + public CallGetAndRunExecutionList(Future delegate, ExecutionList executionList) { + this.delegate = checkNotNull(delegate, "delegate"); + this.executionList = checkNotNull(executionList, "executionList"); } - private FutureListener(Future future, ExecutorService executor) { - this.future = checkNotNull(future, "future"); - this.executor = checkNotNull(executor, "executor"); + @Override + public void run() { + try { + delegate.get(); + } catch (InterruptedException e) { + // This thread was interrupted. This should never happen, so we + // throw an IllegalStateException. + Thread.currentThread().interrupt(); + throw new IllegalStateException(String.format( + "interrupted calling get() on [%s], so could not run listeners", delegate), e); + } catch (Throwable e) { + // ExecutionException / CancellationException / RuntimeException + // The task is done, run the listeners. + } + executionList.run(); + } + + @Override + public String toString() { + return "[delegate=" + delegate + ", executionList=" + executionList + "]"; + } + } + + // Adapted from Guava + // + // * to allow us to enforce supply of an adapterExecutor + // note that this is done so that we can operate in Google AppEngine which + // restricts thread creation + // * to allow us to print debug info about what the delegate was doing + public static class FutureListener { + + final ExecutorService adapterExecutor; + + // The execution list to hold our listeners. + private final ExecutionList executionList = new ExecutionList(); + + // This allows us to only start up a thread waiting on the delegate future + // when the first listener is added. + private final AtomicBoolean hasListeners = new AtomicBoolean(false); + + // The delegate future. + private final Future delegate; + + static FutureListener create(Future delegate, ExecutorService adapterExecutor) { + return new FutureListener(delegate, adapterExecutor); + } + + private FutureListener(Future delegate, ExecutorService adapterExecutor) { + this.delegate = checkNotNull(delegate, "delegate"); + this.adapterExecutor = checkNotNull(adapterExecutor, "adapterExecutor"); } public void addListener(Runnable listener, Executor exec) { + executionList.add(listener, exec); // When a listener is first added, we run a task that will wait for - // the future to finish, and when it is done will run the listeners. - if (!hasListeners.get() && hasListeners.compareAndSet(false, true)) { - executor.execute(new Runnable() { - /* @Override */ - public void run() { - try { - future.get(); - } catch (CancellationException e) { - // The task was cancelled, so it is done, run the listeners. - } catch (InterruptedException e) { - // This thread was interrupted. This should never happen, so we - // throw an IllegalStateException. - throw new IllegalStateException("Adapter thread interrupted!", e); - } catch (ExecutionException e) { - // The task caused an exception, so it is done, run the listeners. - } - executionList.run(); - } - }); + // the delegate to finish, and when it is done will run the listeners. + if (hasListeners.compareAndSet(false, true)) { + if (delegate.isDone()) { + // If the delegate is already done, run the execution list + // immediately on the current thread. + executionList.run(); + return; + } + adapterExecutor.execute(new CallGetAndRunExecutionList(delegate, executionList)); } - executionList.add(listener, exec); } Future getFuture() { - return future; + return delegate; } ExecutorService getExecutor() { - return executor; + return adapterExecutor; } } @@ -119,27 +155,27 @@ public class Futures { } public static class LazyListenableFutureFunctionAdapter extends ForwardingObject implements - ListenableFuture { + ListenableFuture { private final FutureListener futureListener; private final Function function; static LazyListenableFutureFunctionAdapter create(Future future, - Function function, ExecutorService executor) { + Function function, ExecutorService executor) { return new LazyListenableFutureFunctionAdapter(future, function, executor); } static LazyListenableFutureFunctionAdapter create(FutureListener futureListener, - Function function) { + Function function) { return new LazyListenableFutureFunctionAdapter(futureListener, function); } private LazyListenableFutureFunctionAdapter(Future future, Function function, - ExecutorService executor) { + ExecutorService executor) { this(FutureListener.create(future, executor), function); } private LazyListenableFutureFunctionAdapter(FutureListener futureListener, - Function function) { + Function function) { this.futureListener = checkNotNull(futureListener, "futureListener"); this.function = checkNotNull(function, "function"); } @@ -219,18 +255,19 @@ public class Futures { * chaining, so that we don't invoke get() early. */ public static ListenableFuture compose(Future future, final Function function, - ExecutorService executorService) { + ExecutorService executorService) { if (future instanceof Futures.ListenableFutureAdapter) { Futures.ListenableFutureAdapter lf = (ListenableFutureAdapter) future; - if (lf.futureListener.executor.getClass().isAnnotationPresent(SingleThreaded.class)) + if (lf.futureListener.adapterExecutor.getClass().isAnnotationPresent(SingleThreaded.class)) return Futures.LazyListenableFutureFunctionAdapter.create( - ((org.jclouds.concurrent.Futures.ListenableFutureAdapter) future).futureListener, function); + ((org.jclouds.concurrent.Futures.ListenableFutureAdapter) future).futureListener, function); else return com.google.common.util.concurrent.Futures.compose(lf, function, executorService); } else if (executorService.getClass().isAnnotationPresent(SingleThreaded.class)) { return Futures.LazyListenableFutureFunctionAdapter.create(future, function, executorService); } else { - return com.google.common.util.concurrent.Futures.compose(Futures.makeListenable(future, executorService), function, executorService); + return com.google.common.util.concurrent.Futures.compose(Futures.makeListenable(future, executorService), + function, executorService); } } diff --git a/core/src/test/java/org/jclouds/concurrent/FuturesTest.java b/core/src/test/java/org/jclouds/concurrent/FuturesTest.java new file mode 100644 index 0000000000..36706c3a79 --- /dev/null +++ b/core/src/test/java/org/jclouds/concurrent/FuturesTest.java @@ -0,0 +1,93 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.concurrent; + +import static org.easymock.EasyMock.expect; +import static org.easymock.classextension.EasyMock.createMock; +import static org.easymock.classextension.EasyMock.replay; +import static org.easymock.classextension.EasyMock.verify; +import static org.testng.Assert.assertEquals; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import org.jclouds.concurrent.Futures.CallGetAndRunExecutionList; +import org.testng.annotations.Test; + +import com.google.common.util.concurrent.ExecutionList; + +/** + * Tests behavior of Futures + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class FuturesTest { + ExecutorService executorService = MoreExecutors.sameThreadExecutor(); + + @Test + public void testCallGetAndRunRunnableRunsListOnRuntimeException() throws InterruptedException, ExecutionException { + + Runnable runnable = createMock(Runnable.class); + @SuppressWarnings("unchecked") + Future future = createMock(Future.class); + runnable.run(); + expect(future.get()).andThrow(new RuntimeException()); + replay(runnable); + replay(future); + + ExecutionList executionList = new ExecutionList(); + executionList.add(runnable, executorService); + + CallGetAndRunExecutionList caller = new CallGetAndRunExecutionList(future, executionList); + caller.run(); + + verify(runnable); + verify(future); + } + + @Test + public void testCallGetAndRunRunnableInterruptsAndThrowsIllegalStateExceptionOnInterruptedException() + throws InterruptedException, ExecutionException { + + Runnable runnable = createMock(Runnable.class); + @SuppressWarnings("unchecked") + Future future = createMock(Future.class); + expect(future.get()).andThrow(new InterruptedException()); + replay(runnable); + replay(future); + + ExecutionList executionList = new ExecutionList(); + executionList.add(runnable, executorService); + + CallGetAndRunExecutionList caller = new CallGetAndRunExecutionList(future, executionList); + try { + caller.run(); + assert false; + } catch (IllegalStateException e) { + assertEquals(e.getMessage(), "interrupted calling get() on [EasyMock for interface java.util.concurrent.Future], so could not run listeners"); + } + + verify(runnable); + verify(future); + } + +} From 2c5e97627dd6a9738261f5cabedfb8673a737c2d Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sat, 5 Mar 2011 21:08:55 -0500 Subject: [PATCH 11/13] removed deprecated usage --- .../compute/BaseComputeServiceLiveTest.java | 70 +++++++++---------- .../src/main/java/org/jclouds/util/Utils.java | 4 -- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java index 86d61cd96c..4eb69cb03f 100755 --- a/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java +++ b/compute/src/test/java/org/jclouds/compute/BaseComputeServiceLiveTest.java @@ -35,8 +35,8 @@ import static org.jclouds.compute.options.TemplateOptions.Builder.blockOnComplet import static org.jclouds.compute.options.TemplateOptions.Builder.overrideCredentialsWith; import static org.jclouds.compute.predicates.NodePredicates.TERMINATED; import static org.jclouds.compute.predicates.NodePredicates.all; -import static org.jclouds.compute.predicates.NodePredicates.runningWithTag; -import static org.jclouds.compute.predicates.NodePredicates.withTag; +import static org.jclouds.compute.predicates.NodePredicates.runningInGroup; +import static org.jclouds.compute.predicates.NodePredicates.inGroup; import static org.jclouds.compute.util.ComputeServiceUtils.getCores; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -207,7 +207,7 @@ public abstract class BaseComputeServiceLiveTest { @Test(enabled = true, expectedExceptions = NoSuchElementException.class) public void testCorrectExceptionRunningNodesNotFound() throws Exception { - client.runScriptOnNodesMatching(runningWithTag("zebras-are-awesome"), buildScript(new OperatingSystemBuilder() + client.runScriptOnNodesMatching(runningInGroup("zebras-are-awesome"), buildScript(new OperatingSystemBuilder() .family(OsFamily.UBUNTU).description("ffoo").build())); } @@ -215,23 +215,23 @@ public abstract class BaseComputeServiceLiveTest { // starting this one alphabetically before create2nodes.. @Test(enabled = true, dependsOnMethods = { "testCompareSizes" }) public void testAScriptExecutionAfterBootWithBasicTemplate() throws Exception { - String tag = this.group + "r"; + String group = this.group + "r"; try { - client.destroyNodesMatching(withTag(tag)); + client.destroyNodesMatching(inGroup(group)); } catch (Exception e) { } TemplateOptions options = client.templateOptions().blockOnPort(22, 120); try { - Set nodes = client.runNodesWithTag(tag, 1, options); + Set nodes = client.createNodesInGroup(group, 1, options); Credentials good = nodes.iterator().next().getCredentials(); assert good.identity != null : nodes; assert good.credential != null : nodes; OperatingSystem os = get(nodes, 0).getOperatingSystem(); try { - Map responses = runScriptWithCreds(tag, os, new Credentials( + Map responses = runScriptWithCreds(group, os, new Credentials( good.identity, "romeo")); assert false : "shouldn't pass with a bad password\n" + responses; } catch (RunScriptOnNodesException e) { @@ -239,17 +239,17 @@ public abstract class BaseComputeServiceLiveTest { } for (Entry response : client.runScriptOnNodesMatching( - runningWithTag(tag), Statements.exec("echo hello"), + runningInGroup(group), Statements.exec("echo hello"), overrideCredentialsWith(good).wrapInInitScript(false).runAsRoot(false)).entrySet()) assert response.getValue().getOutput().trim().equals("hello") : response.getKey() + ": " + response.getValue(); - runScriptWithCreds(tag, os, good); + runScriptWithCreds(group, os, good); - checkNodes(nodes, tag); + checkNodes(nodes, group); } finally { - client.destroyNodesMatching(withTag(tag)); + client.destroyNodesMatching(inGroup(group)); } } @@ -267,13 +267,13 @@ public abstract class BaseComputeServiceLiveTest { @Test(enabled = true, dependsOnMethods = "testCompareSizes") public void testCreateTwoNodesWithRunScript() throws Exception { try { - client.destroyNodesMatching(withTag(group)); + client.destroyNodesMatching(inGroup(group)); } catch (NoSuchElementException e) { } refreshTemplate(); try { - nodes = newTreeSet(client.runNodesWithTag(group, 2, template)); + nodes = newTreeSet(client.createNodesInGroup(group, 2, template)); } catch (RunNodesException e) { nodes = newTreeSet(concat(e.getSuccessfulNodes(), e.getNodeErrors().keySet())); throw e; @@ -324,7 +324,7 @@ public abstract class BaseComputeServiceLiveTest { public void testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired() throws Exception { initializeContextAndClient(); refreshTemplate(); - TreeSet nodes = newTreeSet(client.runNodesWithTag(group, 1, template)); + TreeSet nodes = newTreeSet(client.createNodesInGroup(group, 1, template)); checkNodes(nodes, group); NodeMetadata node = nodes.first(); this.nodes.add(node); @@ -340,21 +340,21 @@ public abstract class BaseComputeServiceLiveTest { assert (context.getCredentialStore().get("node#" + node.getId()) != null) : "credentials for " + node.getId(); } - protected Map runScriptWithCreds(final String tag, OperatingSystem os, + protected Map runScriptWithCreds(final String group, OperatingSystem os, Credentials creds) throws RunScriptOnNodesException { try { - return client.runScriptOnNodesMatching(runningWithTag(tag), buildScript(os), overrideCredentialsWith(creds) + return client.runScriptOnNodesMatching(runningInGroup(group), buildScript(os), overrideCredentialsWith(creds) .nameTask("runScriptWithCreds")); } catch (SshException e) { throw e; } } - protected void checkNodes(Iterable nodes, String tag) throws IOException { + protected void checkNodes(Iterable nodes, String group) throws IOException { for (NodeMetadata node : nodes) { assertNotNull(node.getProviderId()); - assertNotNull(node.getTag()); - assertEquals(node.getTag(), tag); + assertNotNull(node.getGroup()); + assertEquals(node.getGroup(), group); assertEquals(node.getState(), NodeState.RUNNING); Credentials fromStore = context.getCredentialStore().get("node#" + node.getId()); assertEquals(fromStore, node.getCredentials()); @@ -375,7 +375,7 @@ public abstract class BaseComputeServiceLiveTest { @Test(enabled = true, dependsOnMethods = "testCreateAnotherNodeWithANewContextToEnsureSharedMemIsntRequired") public void testGet() throws Exception { Map metadataMap = newLinkedHashMap(uniqueIndex(filter(client - .listNodesDetailsMatching(all()), and(withTag(group), not(TERMINATED))), + .listNodesDetailsMatching(all()), and(inGroup(group), not(TERMINATED))), new Function() { @Override @@ -388,7 +388,7 @@ public abstract class BaseComputeServiceLiveTest { metadataMap.remove(node.getId()); NodeMetadata metadata = client.getNodeMetadata(node.getId()); assertEquals(metadata.getProviderId(), node.getProviderId()); - assertEquals(metadata.getTag(), node.getTag()); + assertEquals(metadata.getGroup(), node.getGroup()); assertLocationSameOrChild(metadata.getLocation(), template.getLocation()); checkImageIdMatchesTemplate(metadata); checkOsMatchesTemplate(metadata); @@ -407,14 +407,14 @@ public abstract class BaseComputeServiceLiveTest { @Test(enabled = true, dependsOnMethods = "testGet") public void testReboot() throws Exception { - client.rebootNodesMatching(withTag(group));// TODO test + client.rebootNodesMatching(inGroup(group));// TODO test // validation testGet(); } @Test(enabled = true, dependsOnMethods = "testReboot") public void testSuspendResume() throws Exception { - client.suspendNodesMatching(withTag(group)); + client.suspendNodesMatching(inGroup(group)); Set stoppedNodes = refreshNodes(); @@ -430,7 +430,7 @@ public abstract class BaseComputeServiceLiveTest { }) : stoppedNodes; - client.resumeNodesMatching(withTag(group)); + client.resumeNodesMatching(inGroup(group)); testGet(); } @@ -467,24 +467,24 @@ public abstract class BaseComputeServiceLiveTest { @Test(enabled = true, dependsOnMethods = { "testListNodes", "testGetNodesWithDetails" }) public void testDestroyNodes() { int toDestroy = refreshNodes().size(); - Set destroyed = client.destroyNodesMatching(withTag(group)); + Set destroyed = client.destroyNodesMatching(inGroup(group)); assertEquals(toDestroy, destroyed.size()); - for (NodeMetadata node : filter(client.listNodesDetailsMatching(all()), withTag(group))) { + for (NodeMetadata node : filter(client.listNodesDetailsMatching(all()), inGroup(group))) { assert node.getState() == NodeState.TERMINATED : node; assertEquals(context.getCredentialStore().get("node#" + node.getId()), null); } } private Set refreshNodes() { - return filter(client.listNodesDetailsMatching(all()), and(withTag(group), not(TERMINATED))); + return filter(client.listNodesDetailsMatching(all()), and(inGroup(group), not(TERMINATED))); } @Test(enabled = true) public void testCreateAndRunAService() throws Exception { - String tag = this.group + "s"; + String group = this.group + "s"; try { - client.destroyNodesMatching(withTag(tag)); + client.destroyNodesMatching(inGroup(group)); } catch (Exception e) { } @@ -497,11 +497,11 @@ public abstract class BaseComputeServiceLiveTest { RunScriptData.createScriptInstallAndStartJBoss(keyPair.get("public"), template.getImage() .getOperatingSystem())); try { - NodeMetadata node = getOnlyElement(client.runNodesWithTag(tag, 1, template)); + NodeMetadata node = getOnlyElement(client.createNodesInGroup(group, 1, template)); checkHttpGet(node); } finally { - client.destroyNodesMatching(withTag(tag)); + client.destroyNodesMatching(inGroup(group)); } } @@ -554,9 +554,9 @@ public abstract class BaseComputeServiceLiveTest { } public void testOptionToNotBlock() throws Exception { - String tag = this.group + "block"; + String group = this.group + "block"; try { - client.destroyNodesMatching(withTag(tag)); + client.destroyNodesMatching(inGroup(group)); } catch (Exception e) { } @@ -564,13 +564,13 @@ public abstract class BaseComputeServiceLiveTest { TemplateOptions options = client.templateOptions().blockUntilRunning(false).inboundPorts(); try { long time = System.currentTimeMillis(); - Set nodes = client.runNodesWithTag(tag, 1, options); + Set nodes = client.createNodesInGroup(group, 1, options); NodeMetadata node = getOnlyElement(nodes); assert node.getState() != NodeState.RUNNING; long duration = System.currentTimeMillis() - time; assert duration < 30 * 1000 : "duration longer than 30 seconds!: " + duration / 1000; } finally { - client.destroyNodesMatching(withTag(tag)); + client.destroyNodesMatching(inGroup(group)); } } diff --git a/core/src/main/java/org/jclouds/util/Utils.java b/core/src/main/java/org/jclouds/util/Utils.java index 23b75726b3..1d4a95d946 100644 --- a/core/src/main/java/org/jclouds/util/Utils.java +++ b/core/src/main/java/org/jclouds/util/Utils.java @@ -67,7 +67,6 @@ public class Utils { * @see Lists2#multiMax */ @Deprecated - @SuppressWarnings("unchecked") public static List multiMax(Comparator ordering, Iterable iterable) { return Lists2. multiMax(ordering, iterable); } @@ -86,7 +85,6 @@ public class Utils { * @see Throwables2#getFirstThrowableOfType(Throwable, Class) */ @Deprecated - @SuppressWarnings("unchecked") public static T getFirstThrowableOfType(Throwable from, Class clazz) { return Throwables2. getFirstThrowableOfType(from, clazz); } @@ -253,7 +251,6 @@ public class Utils { * @see Providers#resolveContextBuilderClass */ @Deprecated - @SuppressWarnings("unchecked") public static Class> resolveContextBuilderClass(String provider, Properties properties) throws ClassNotFoundException, IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { @@ -277,7 +274,6 @@ public class Utils { * @see Providers#resolvePropertiesBuilderClass */ @Deprecated - @SuppressWarnings("unchecked") public static Class resolvePropertiesBuilderClass(String providerName, Properties props) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { From d42f50e0c0c5c020463330e854a0c667c19654df Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Sun, 6 Mar 2011 08:06:34 -0500 Subject: [PATCH 12/13] fixed order in unit test --- .../org/jclouds/ibmdev/IBMDeveloperCloudAsyncClientTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox-providers/ibmdev/src/test/java/org/jclouds/ibmdev/IBMDeveloperCloudAsyncClientTest.java b/sandbox-providers/ibmdev/src/test/java/org/jclouds/ibmdev/IBMDeveloperCloudAsyncClientTest.java index a433310872..fd061b6c89 100644 --- a/sandbox-providers/ibmdev/src/test/java/org/jclouds/ibmdev/IBMDeveloperCloudAsyncClientTest.java +++ b/sandbox-providers/ibmdev/src/test/java/org/jclouds/ibmdev/IBMDeveloperCloudAsyncClientTest.java @@ -456,7 +456,7 @@ public class IBMDeveloperCloudAsyncClientTest extends RestClientTest Date: Sun, 6 Mar 2011 09:22:37 -0500 Subject: [PATCH 13/13] Issue 499: added ssh key import to AWSKeyPairClient --- providers/aws-ec2/pom.xml | 2 +- .../jclouds/aws/ec2/AWSEC2AsyncClient.java | 9 +- .../org/jclouds/aws/ec2/AWSEC2Client.java | 7 + .../ec2/config/AWSEC2RestClientModule.java | 6 +- .../EncodedRSAPublicKeyToBase64.java | 73 +++++++ .../ec2/services/AWSKeyPairAsyncClient.java | 66 ++++++ .../aws/ec2/services/AWSKeyPairClient.java | 71 +++++++ .../ec2/services/MonitoringAsyncClient.java | 4 +- .../services/PlacementGroupAsyncClient.java | 4 +- .../EncodedRSAPublicKeyToBase64Test.java | 28 +++ .../ec2/services/AWSAMIAsyncClientTest.java | 162 ++++++++------- .../services/AWSInstanceAsyncClientTest.java | 192 +++++++++--------- .../services/AWSKeyPairAsyncClientTest.java | 135 ++++++++++++ .../services/AWSKeyPairClientLiveTest.java | 178 ++++++++++++++++ .../services/MonitoringAsyncClientTest.java | 4 +- .../PlacementGroupAsyncClientTest.java | 10 +- 16 files changed, 763 insertions(+), 188 deletions(-) create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java create mode 100644 providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairClient.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64Test.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClientTest.java create mode 100644 providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java diff --git a/providers/aws-ec2/pom.xml b/providers/aws-ec2/pom.xml index e66781ffe5..d566f55547 100644 --- a/providers/aws-ec2/pom.xml +++ b/providers/aws-ec2/pom.xml @@ -36,7 +36,7 @@ https://ec2.us-east-1.amazonaws.com - 2010-06-15 + 2010-11-15 ${test.aws.identity} ${test.aws.credential} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java index c556e487f7..dc856615b3 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2AsyncClient.java @@ -21,6 +21,7 @@ package org.jclouds.aws.ec2; import org.jclouds.aws.ec2.services.AWSAMIAsyncClient; import org.jclouds.aws.ec2.services.AWSInstanceAsyncClient; +import org.jclouds.aws.ec2.services.AWSKeyPairAsyncClient; import org.jclouds.aws.ec2.services.MonitoringAsyncClient; import org.jclouds.aws.ec2.services.PlacementGroupAsyncClient; import org.jclouds.ec2.EC2AsyncClient; @@ -32,7 +33,7 @@ import org.jclouds.rest.annotations.Delegate; * @author Adrian Cole */ public interface AWSEC2AsyncClient extends EC2AsyncClient { - public final static String VERSION = "2010-06-15"; + public final static String VERSION = "2010-11-15"; /** * {@inheritDoc} @@ -60,4 +61,10 @@ public interface AWSEC2AsyncClient extends EC2AsyncClient { @Delegate MonitoringAsyncClient getMonitoringServices(); + /** + * {@inheritDoc} + */ + @Delegate + @Override + AWSKeyPairAsyncClient getKeyPairServices(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java index 8bd8ea53f7..9a6f9087b6 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/AWSEC2Client.java @@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit; import org.jclouds.aws.ec2.services.AWSAMIClient; import org.jclouds.aws.ec2.services.AWSInstanceClient; +import org.jclouds.aws.ec2.services.AWSKeyPairClient; import org.jclouds.aws.ec2.services.MonitoringClient; import org.jclouds.aws.ec2.services.PlacementGroupClient; import org.jclouds.concurrent.Timeout; @@ -63,4 +64,10 @@ public interface AWSEC2Client extends EC2Client { @Delegate MonitoringClient getMonitoringServices(); + /** + * {@inheritDoc} + */ + @Delegate + @Override + AWSKeyPairClient getKeyPairServices(); } diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java index ad0866e6d2..719b3eb411 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/config/AWSEC2RestClientModule.java @@ -31,6 +31,8 @@ import org.jclouds.aws.ec2.services.AWSAMIAsyncClient; import org.jclouds.aws.ec2.services.AWSAMIClient; import org.jclouds.aws.ec2.services.AWSInstanceAsyncClient; import org.jclouds.aws.ec2.services.AWSInstanceClient; +import org.jclouds.aws.ec2.services.AWSKeyPairAsyncClient; +import org.jclouds.aws.ec2.services.AWSKeyPairClient; import org.jclouds.aws.ec2.services.MonitoringAsyncClient; import org.jclouds.aws.ec2.services.MonitoringClient; import org.jclouds.aws.ec2.services.PlacementGroupAsyncClient; @@ -50,8 +52,6 @@ import org.jclouds.ec2.services.ElasticIPAddressAsyncClient; import org.jclouds.ec2.services.ElasticIPAddressClient; import org.jclouds.ec2.services.InstanceAsyncClient; import org.jclouds.ec2.services.InstanceClient; -import org.jclouds.ec2.services.KeyPairAsyncClient; -import org.jclouds.ec2.services.KeyPairClient; import org.jclouds.ec2.services.SecurityGroupAsyncClient; import org.jclouds.ec2.services.SecurityGroupClient; import org.jclouds.ec2.services.WindowsAsyncClient; @@ -75,7 +75,7 @@ public class AWSEC2RestClientModule extends EC2RestClientModule + * + * ==================================================================== + * Licensed 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.aws.ec2.functions; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.inject.Singleton; + +import org.jclouds.crypto.CryptoStreams; + +import com.google.common.base.Charsets; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +/** + * @see + * @author Adrian Cole + */ +@Singleton +public class EncodedRSAPublicKeyToBase64 implements Function { + private static Predicate startsWith(String value) { + return new ToStringStartsWith(value); + } + + private static final class ToStringStartsWith implements Predicate { + private final String value; + + private ToStringStartsWith(String value) { + this.value = value; + } + + @Override + public boolean apply(Object input) { + return input.toString().startsWith(value); + } + + public String toString() { + return "toStringStartsWith(" + value + ")"; + } + } + + @SuppressWarnings("unchecked") + private static final Predicate ALLOWED_MARKERS = Predicates.or(startsWith("ssh-rsa"), + startsWith("-----BEGIN CERTIFICATE-----"), startsWith("---- BEGIN SSH2 PUBLIC KEY ----")); + + @Override + public String apply(Object from) { + checkNotNull(from, "input"); + checkArgument(ALLOWED_MARKERS.apply(from), "must be a ssh public key, conforming to %s ", ALLOWED_MARKERS); + return CryptoStreams.base64(from.toString().getBytes(Charsets.UTF_8)); + } +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java new file mode 100644 index 0000000000..c6a9d2b7c4 --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairAsyncClient.java @@ -0,0 +1,66 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.aws.ec2.services; + +import static org.jclouds.aws.reference.FormParameters.ACTION; +import static org.jclouds.aws.reference.FormParameters.VERSION; + +import javax.annotation.Nullable; +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import org.jclouds.aws.ec2.AWSEC2AsyncClient; +import org.jclouds.aws.ec2.functions.EncodedRSAPublicKeyToBase64; +import org.jclouds.aws.filters.FormSigner; +import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.ec2.services.KeyPairAsyncClient; +import org.jclouds.ec2.services.KeyPairClient; +import org.jclouds.ec2.xml.KeyPairResponseHandler; +import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.FormParams; +import org.jclouds.rest.annotations.ParamParser; +import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.annotations.VirtualHost; +import org.jclouds.rest.annotations.XMLResponseParser; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * @author Adrian Cole + */ +@RequestFilters(FormSigner.class) +@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION) +@VirtualHost +public interface AWSKeyPairAsyncClient extends KeyPairAsyncClient { + + /** + * @see KeyPairClient#importKeyPairInRegion + */ + @POST + @Path("/") + @FormParams(keys = ACTION, values = "ImportKeyPair") + @XMLResponseParser(KeyPairResponseHandler.class) + ListenableFuture importKeyPairInRegion( + @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @Nullable String region, + @FormParam("KeyName") String keyName, + @FormParam("PublicKeyMaterial") @ParamParser(EncodedRSAPublicKeyToBase64.class) String publicKeyMaterial); +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairClient.java new file mode 100644 index 0000000000..b21ca6ab3a --- /dev/null +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/AWSKeyPairClient.java @@ -0,0 +1,71 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.aws.ec2.services; + +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nullable; + +import org.jclouds.concurrent.Timeout; +import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.ec2.services.KeyPairClient; + +/** + * + * @author Adrian Cole + */ +@Timeout(duration = 90, timeUnit = TimeUnit.SECONDS) +public interface AWSKeyPairClient extends KeyPairClient { + + /** + * Imports the public key from an RSA key pair that you created with a third-party tool. Compare + * this with CreateKeyPair, in which AWS creates the key pair and gives the keys to you (AWS + * keeps a copy of the public key). With ImportKeyPair, you create the key pair and give AWS just + * the public key. The private key is never transferred between you and AWS. + * + *

+ * You can easily create an RSA key pair on Windows and Linux using the ssh-keygen command line + * tool (provided with the standard OpenSSH installation). Standard library support for RSA key + * pair creation is also available in Java, Ruby, Python, and many other programming languages. + * + *

+ *

Supported Formats

+ *
    + *
  • OpenSSH public key format (e.g., the format in ~/.ssh/authorized_keys)
  • + *
  • Base64 encoded DER format
  • + *
  • SSH public key file format as specified in RFC4716
  • + *
+ * DSA keys are not supported. Make sure your key generator is set up to create RSA keys. + *

+ * Supported lengths: 1024, 2048, and 4096. + *

+ * + * @param region + * region to import the key into + * @param keyName + * A unique name for the key pair. Accepts alphanumeric characters, spaces, dashes, and + * underscores. + * @param publicKeyMaterial + * The public key + * @return imported key including fingerprint + */ + KeyPair importKeyPairInRegion(@Nullable String region, String keyName, String publicKeyMaterial); + +} diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/MonitoringAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/MonitoringAsyncClient.java index 0c42604f61..088bfcb8ee 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/MonitoringAsyncClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/MonitoringAsyncClient.java @@ -29,10 +29,10 @@ import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; +import org.jclouds.aws.ec2.AWSEC2AsyncClient; import org.jclouds.aws.ec2.domain.MonitoringState; import org.jclouds.aws.ec2.xml.MonitoringStateHandler; import org.jclouds.aws.filters.FormSigner; -import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.binders.BindInstanceIdsToIndexedFormParams; import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; import org.jclouds.rest.annotations.BinderParam; @@ -51,7 +51,7 @@ import com.google.common.util.concurrent.ListenableFuture; * @author Adrian Cole */ @RequestFilters(FormSigner.class) -@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION) +@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION) @VirtualHost public interface MonitoringAsyncClient { diff --git a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClient.java b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClient.java index 45adce9236..d44c637a38 100644 --- a/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClient.java +++ b/providers/aws-ec2/src/main/java/org/jclouds/aws/ec2/services/PlacementGroupAsyncClient.java @@ -29,10 +29,10 @@ import javax.ws.rs.FormParam; import javax.ws.rs.POST; import javax.ws.rs.Path; +import org.jclouds.aws.ec2.AWSEC2AsyncClient; import org.jclouds.aws.ec2.domain.PlacementGroup; import org.jclouds.aws.ec2.xml.DescribePlacementGroupsResponseHandler; import org.jclouds.aws.filters.FormSigner; -import org.jclouds.ec2.EC2AsyncClient; import org.jclouds.ec2.binders.BindGroupNamesToIndexedFormParams; import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull; import org.jclouds.rest.annotations.BinderParam; @@ -54,7 +54,7 @@ import com.google.common.util.concurrent.ListenableFuture; * @author Adrian Cole */ @RequestFilters(FormSigner.class) -@FormParams(keys = VERSION, values = EC2AsyncClient.VERSION) +@FormParams(keys = VERSION, values = AWSEC2AsyncClient.VERSION) @VirtualHost public interface PlacementGroupAsyncClient { diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64Test.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64Test.java new file mode 100644 index 0000000000..9c235ee8c3 --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/functions/EncodedRSAPublicKeyToBase64Test.java @@ -0,0 +1,28 @@ +package org.jclouds.aws.ec2.functions; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; + +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code EncodedRSAPublicKeyToBase64} + * + * @author Adrian Cole + */ +@Test(groups = "unit") +public class EncodedRSAPublicKeyToBase64Test { + EncodedRSAPublicKeyToBase64 function = new EncodedRSAPublicKeyToBase64(); + + public void testAllowedMarkers() throws IOException { + assertEquals(function.apply("-----BEGIN CERTIFICATE-----"), "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t"); + assertEquals(function.apply("ssh-rsa"), "c3NoLXJzYQ=="); + assertEquals(function.apply("---- BEGIN SSH2 PUBLIC KEY ----"), "LS0tLSBCRUdJTiBTU0gyIFBVQkxJQyBLRVkgLS0tLQ=="); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testDisallowedMarkersIllegalArgument() throws IOException { + function.apply("ssh-dsa"); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClientTest.java index 55e59271ab..8fb246c94d 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClientTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSAMIAsyncClientTest.java @@ -58,13 +58,13 @@ public class AWSAMIAsyncClientTest extends BaseAWSEC2AsyncClientTest mapping = Maps.newLinkedHashMap(); mapping.put("/dev/sda1", new BlockDevice("vol-test1", true)); @@ -479,14 +485,14 @@ public class AWSInstanceAsyncClientTest extends BaseAWSEC2AsyncClientTest + * + * ==================================================================== + * Licensed 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.aws.ec2.services; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +import org.jclouds.ec2.xml.DescribeKeyPairsResponseHandler; +import org.jclouds.ec2.xml.KeyPairResponseHandler; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ReleasePayloadAndReturn; +import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; +import org.jclouds.rest.internal.RestAnnotationProcessor; +import org.testng.annotations.Test; + +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of {@code AWSKeyPairAsyncClient} + * + * @author Adrian Cole + */ +// NOTE:without testName, this will not call @Before* and fail w/NPE during surefire +@Test(groups = "unit", testName = "AWSKeyPairAsyncClientTest") +public class AWSKeyPairAsyncClientTest extends BaseAWSEC2AsyncClientTest { + + public void testCreateKeyPair() throws SecurityException, NoSuchMethodException, IOException { + Method method = AWSKeyPairAsyncClient.class.getMethod("createKeyPairInRegion", String.class, String.class); + HttpRequest request = processor.createRequest(method, null, "mykey"); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2010-11-15&Action=CreateKeyPair&KeyName=mykey", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, KeyPairResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(request); + } + + public void testImportKeyPair() throws SecurityException, NoSuchMethodException, IOException { + Method method = AWSKeyPairAsyncClient.class.getMethod("importKeyPairInRegion", String.class, String.class, + String.class); + HttpRequest request = processor.createRequest(method, null, "mykey", "ssh-rsa AA"); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2010-11-15&Action=ImportKeyPair&PublicKeyMaterial=c3NoLXJzYSBBQQ%3D%3D&KeyName=mykey", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, KeyPairResponseHandler.class); + assertExceptionParserClassEquals(method, null); + + checkFilters(request); + } + + public void testDeleteKeyPair() throws SecurityException, NoSuchMethodException, IOException { + Method method = AWSKeyPairAsyncClient.class.getMethod("deleteKeyPairInRegion", String.class, String.class); + HttpRequest request = processor.createRequest(method, null, "mykey"); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2010-11-15&Action=DeleteKeyPair&KeyName=mykey", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ReleasePayloadAndReturn.class); + assertSaxResponseParserClassEquals(method, null); + assertExceptionParserClassEquals(method, null); + + checkFilters(request); + } + + public void testDescribeKeyPairs() throws SecurityException, NoSuchMethodException, IOException { + Method method = AWSKeyPairAsyncClient.class.getMethod("describeKeyPairsInRegion", String.class, Array + .newInstance(String.class, 0).getClass()); + HttpRequest request = processor.createRequest(method, (String) null); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2010-11-15&Action=DescribeKeyPairs", "application/x-www-form-urlencoded", + false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeKeyPairsResponseHandler.class); + assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); + + checkFilters(request); + } + + public void testDescribeKeyPairsArgs() throws SecurityException, NoSuchMethodException, IOException { + Method method = AWSKeyPairAsyncClient.class.getMethod("describeKeyPairsInRegion", String.class, Array + .newInstance(String.class, 0).getClass()); + HttpRequest request = processor.createRequest(method, null, "1", "2"); + + assertRequestLineEquals(request, "POST https://ec2.us-east-1.amazonaws.com/ HTTP/1.1"); + assertNonPayloadHeadersEqual(request, "Host: ec2.us-east-1.amazonaws.com\n"); + assertPayloadEquals(request, "Version=2010-11-15&Action=DescribeKeyPairs&KeyName.1=1&KeyName.2=2", + "application/x-www-form-urlencoded", false); + + assertResponseParserClassEquals(method, request, ParseSax.class); + assertSaxResponseParserClassEquals(method, DescribeKeyPairsResponseHandler.class); + assertExceptionParserClassEquals(method, ReturnEmptySetOnNotFoundOr404.class); + + checkFilters(request); + } + + @Override + protected TypeLiteral> createTypeLiteral() { + return new TypeLiteral>() { + }; + } + +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java new file mode 100644 index 0000000000..aabfe33cff --- /dev/null +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/AWSKeyPairClientLiveTest.java @@ -0,0 +1,178 @@ +/** + * + * Copyright (C) 2010 Cloud Conscious, LLC. + * + * ==================================================================== + * Licensed 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.aws.ec2.services; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.SortedSet; + +import org.jclouds.Constants; +import org.jclouds.aws.domain.Region; +import org.jclouds.aws.ec2.AWSEC2AsyncClient; +import org.jclouds.aws.ec2.AWSEC2Client; +import org.jclouds.compute.ComputeServiceContextFactory; +import org.jclouds.compute.ComputeTestUtils; +import org.jclouds.ec2.domain.KeyPair; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.rest.RestContext; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.inject.Module; + +/** + * Tests behavior of {@code AWSKeyPairClient} + * + * @author Adrian Cole + */ +@Test(groups = "live", sequential = true) +public class AWSKeyPairClientLiveTest { + + private AWSKeyPairClient client; + private RestContext context; + + protected String provider = "aws-ec2"; + protected String identity; + protected String credential; + protected String endpoint; + protected String apiversion; + + protected void setupCredentials() { + identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity"); + credential = checkNotNull(System.getProperty("test." + provider + ".credential"), "test." + provider + + ".credential"); + endpoint = System.getProperty("test." + provider + ".endpoint", null); + apiversion = System.getProperty("test." + provider + ".apiversion", null); + } + + protected Properties setupProperties() { + Properties overrides = new Properties(); + overrides.setProperty(Constants.PROPERTY_TRUST_ALL_CERTS, "true"); + overrides.setProperty(Constants.PROPERTY_RELAX_HOSTNAME, "true"); + overrides.setProperty(provider + ".identity", identity); + overrides.setProperty(provider + ".credential", credential); + if (endpoint != null) + overrides.setProperty(provider + ".endpoint", endpoint); + if (apiversion != null) + overrides.setProperty(provider + ".apiversion", apiversion); + return overrides; + } + + @BeforeGroups(groups = { "live" }) + public void setupClient() { + setupCredentials(); + Properties overrides = setupProperties(); + context = new ComputeServiceContextFactory().createContext(provider, + ImmutableSet. of(new Log4JLoggingModule()), overrides).getProviderSpecificContext(); + client = context.getApi().getKeyPairServices(); + } + + @Test + void testDescribeAWSKeyPairs() { + for (String region : Region.DEFAULT_REGIONS) { + + SortedSet allResults = Sets.newTreeSet(client.describeKeyPairsInRegion(region)); + assertNotNull(allResults); + if (allResults.size() >= 1) { + KeyPair pair = allResults.last(); + SortedSet result = Sets.newTreeSet(client.describeKeyPairsInRegion(region, pair.getKeyName())); + assertNotNull(result); + KeyPair compare = result.last(); + assertEquals(compare, pair); + } + } + } + + public static final String PREFIX = System.getProperty("user.name") + "-ec2"; + + @Test + void testCreateKeyPair() { + String keyName = PREFIX + "1"; + cleanupKeyPair(keyName); + try { + KeyPair keyPair = client.createKeyPairInRegion(null, keyName); + checkKeyPair(keyName, keyPair); + assertNotNull(keyPair.getKeyMaterial()); + } finally { + cleanupKeyPair(keyName); + } + } + + protected void cleanupKeyPair(String keyName) { + try { + client.deleteKeyPairInRegion(null, keyName); + } catch (Exception e) { + + } + client.deleteKeyPairInRegion(null, keyName); + } + + @Test + void testImportKeyPair() throws FileNotFoundException, IOException { + String keyName = PREFIX + "2"; + cleanupKeyPair(keyName); + Map myKey = ComputeTestUtils.setupKeyPair(); + try { + KeyPair keyPair = client.importKeyPairInRegion(null, keyName, myKey.get("public")); + checkKeyPair(keyName, keyPair); + // TODO generate correct fingerprint and check + // assertEquals(keyPair.getKeyFingerprint(), + // CryptoStreams.hex(CryptoStreams.md5(myKey.get("public").getBytes()))); + + // try again to see if there's an error + try { + client.importKeyPairInRegion(null, keyName, myKey.get("public")); + assert false; + } catch (IllegalStateException e) { + + } + } finally { + cleanupKeyPair(keyName); + } + } + + protected void checkKeyPair(String keyName, KeyPair keyPair) { + assertNotNull(keyPair); + assertNotNull(keyPair.getKeyFingerprint()); + assertEquals(keyPair.getKeyName(), keyName); + + Set twoResults = Sets.newLinkedHashSet(client.describeKeyPairsInRegion(null, keyName)); + assertNotNull(twoResults); + assertEquals(twoResults.size(), 1); + KeyPair listPair = twoResults.iterator().next(); + assertEquals(listPair.getKeyName(), keyPair.getKeyName()); + assertEquals(listPair.getKeyFingerprint(), keyPair.getKeyFingerprint()); + } + + @AfterTest + public void shutdown() { + context.close(); + } +} diff --git a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringAsyncClientTest.java b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringAsyncClientTest.java index 1a576a4e1c..0e7ccbf3e9 100644 --- a/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringAsyncClientTest.java +++ b/providers/aws-ec2/src/test/java/org/jclouds/aws/ec2/services/MonitoringAsyncClientTest.java @@ -46,7 +46,7 @@ public class MonitoringAsyncClientTest extends BaseAWSEC2AsyncClientTest