From e3b7d6d91c6c798ebe46311bdb7912a4f213c8bb Mon Sep 17 00:00:00 2001 From: Kedar Dave Date: Tue, 17 May 2011 23:09:28 -0500 Subject: [PATCH] implemented create multiple vm's, clonevm, capturevapptemplate(throws error from savvis side) --- .../savvis/vpdc/VPDCPropertiesBuilder.java | 4 +- .../BindCaptureVAppTemplateToXmlPayload.java | 109 +++++++++++++ .../vpdc/binders/BindCloneVMToXmlPayload.java | 117 ++++++++++++++ .../vpdc/binders/BindVMSpecToXmlPayload.java | 77 ++++++---- .../jclouds/savvis/vpdc/domain/VMSpec.java | 32 +++- .../savvis/vpdc/features/VMAsyncClient.java | 34 +++++ .../savvis/vpdc/features/VMClient.java | 37 +++++ ...ndCaptureVAppTemplateToXmlPayloadTest.java | 46 ++++++ .../BindCloneVAppToXmlPayloadTest.java | 46 ++++++ .../binders/BindVMSpecToXmlPayloadTest.java | 13 +- .../vpdc/features/BaseVPDCClientLiveTest.java | 2 +- .../vpdc/features/VMClientLiveTest.java | 144 ++++++++++++++++++ .../capture-vapp-template-default.xml | 1 + .../src/test/resources/cloneVApp-default.xml | 1 + .../test/resources/vm-multiple-default.xml | 1 + 15 files changed, 626 insertions(+), 38 deletions(-) create mode 100644 providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayload.java create mode 100644 providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCloneVMToXmlPayload.java create mode 100644 providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayloadTest.java create mode 100644 providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindCloneVAppToXmlPayloadTest.java create mode 100644 providers/savvis-symphonyvpdc/src/test/resources/capture-vapp-template-default.xml create mode 100644 providers/savvis-symphonyvpdc/src/test/resources/cloneVApp-default.xml create mode 100644 providers/savvis-symphonyvpdc/src/test/resources/vm-multiple-default.xml diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/VPDCPropertiesBuilder.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/VPDCPropertiesBuilder.java index 64c88d6e85..069bc92f6b 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/VPDCPropertiesBuilder.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/VPDCPropertiesBuilder.java @@ -35,8 +35,8 @@ public class VPDCPropertiesBuilder extends PropertiesBuilder { @Override protected Properties defaultProperties() { Properties properties = super.defaultProperties(); - properties.setProperty(PROPERTY_API_VERSION, "0.8"); - properties.setProperty(PROPERTY_ENDPOINT, "https://api.symphonyvpdc.savvis.net/rest/api"); + properties.setProperty(PROPERTY_API_VERSION, "1.0"); + properties.setProperty(PROPERTY_ENDPOINT, "https://api.symphonyvpdc.savvis.net/vpdc"); properties.setProperty(PROPERTY_VPDC_TIMEOUT_TASK_COMPLETED, 600l * 1000l + ""); return properties; } diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayload.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayload.java new file mode 100644 index 0000000000..baec78604d --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayload.java @@ -0,0 +1,109 @@ +/** + * + * Copyright (C) 2011 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.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import java.net.URI; +import java.util.Map; +import java.util.Properties; + +import javax.inject.Singleton; +import javax.ws.rs.core.MediaType; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; + +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.MapBinder; +import org.jclouds.rest.binders.BindToStringPayload; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.savvis.vpdc.domain.FirewallRule; + +import com.jamesmurty.utils.XMLBuilder; + +/** + * + * @author Kedar Dave + * + */ +@Singleton +public class BindCaptureVAppTemplateToXmlPayload extends BindToStringPayload implements MapBinder { + @Override + public R bindToRequest(R request, Object toBind) { + throw new IllegalStateException("BindFirewallRuleToXmlPayload needs parameters"); + + } + + protected URI findVAppURIInArgsOrNull(GeneratedHttpRequest gRequest) { + for (Object arg : gRequest.getArgs()) { + if (arg instanceof URI) { + return (URI) arg; + } else if (arg instanceof FirewallRule[]) { + URI[] rules = (URI[]) arg; + return (rules.length > 0) ? rules[0] : null; + } + } + return null; + } + + @Override + public R bindToRequest(R request, Map postParams) { + checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; + checkState(gRequest.getArgs() != null, "args should be initialized at this point"); + + request = super.bindToRequest(request, + generateXml(findVAppURIInArgsOrNull(gRequest))); + request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_XML); + return request; + } + + public String generateXml(URI vAppURI) { + checkNotNull(vAppURI, "vAppURI"); + + try { + XMLBuilder rootBuilder = buildRoot(); + addSourceSection(rootBuilder, vAppURI); + Properties outputProperties = new Properties(); + return rootBuilder.asString(outputProperties); + } catch (Exception e) { + return null; + } + } + + void addSourceSection(XMLBuilder rootBuilder, URI vAppURI) { + rootBuilder.e("Description").t("Save Template"); + rootBuilder.e("Source").a("href", vAppURI.toString()); + } + + protected XMLBuilder buildRoot() throws ParserConfigurationException, FactoryConfigurationError { + XMLBuilder rootBuilder = XMLBuilder.create("CaptureVAppParams") + .a("xmlns", "http://schemas.api.sandbox.symphonyVPDC.savvis.net/vpdci") + .a("name", "CaptureTemplate"); + return rootBuilder; + } + + protected String ifNullDefaultTo(String value, String defaultValue) { + return value != null ? value : checkNotNull(defaultValue, "defaultValue"); + } + +} diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCloneVMToXmlPayload.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCloneVMToXmlPayload.java new file mode 100644 index 0000000000..5326fafed0 --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindCloneVMToXmlPayload.java @@ -0,0 +1,117 @@ +/** + * + * Copyright (C) 2011 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.binders; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import java.net.URI; +import java.util.Map; +import java.util.Properties; + +import javax.inject.Singleton; +import javax.ws.rs.core.MediaType; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; + +import org.jclouds.http.HttpRequest; +import org.jclouds.rest.MapBinder; +import org.jclouds.rest.binders.BindToStringPayload; +import org.jclouds.rest.internal.GeneratedHttpRequest; +import org.jclouds.savvis.vpdc.domain.FirewallRule; + +import com.jamesmurty.utils.XMLBuilder; + +/** + * + * @author Kedar Dave + * + */ +@Singleton +public class BindCloneVMToXmlPayload extends BindToStringPayload implements MapBinder { + @Override + public R bindToRequest(R request, Object toBind) { + throw new IllegalStateException("BindFirewallRuleToXmlPayload needs parameters"); + + } + + protected URI findVAppURIInArgsOrNull(GeneratedHttpRequest gRequest) { + for (Object arg : gRequest.getArgs()) { + if (arg instanceof URI) { + return (URI) arg; + } else if (arg instanceof FirewallRule[]) { + URI[] rules = (URI[]) arg; + return (rules.length > 0) ? rules[0] : null; + } + } + return null; + } + + @Override + public R bindToRequest(R request, Map postParams) { + checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + GeneratedHttpRequest gRequest = (GeneratedHttpRequest) request; + checkState(gRequest.getArgs() != null, "args should be initialized at this point"); + + request = super.bindToRequest(request, + generateXml(findVAppURIInArgsOrNull(gRequest), postParams.get("name"), postParams.get("networkTierName"))); + request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_XML); + return request; + } + + public String generateXml(URI vAppURI, String newVAppName, String networkTierName) { + checkNotNull(vAppURI, "vAppURI"); + + try { + XMLBuilder rootBuilder = buildRoot(newVAppName); + addVAppSection(rootBuilder, vAppURI, networkTierName); + Properties outputProperties = new Properties(); + return rootBuilder.asString(outputProperties); + } catch (Exception e) { + return null; + } + } + + void addVAppSection(XMLBuilder rootBuilder, URI vAppURI, String networkTierName) { + String vAppStr = "vApp/"; + // TODO: Savvis returns network names with a - instead of space on getNetworkInVDC call, + // fix this once savvis api starts returning correctly + rootBuilder.e("Description").t(networkTierName.replace("-", " ")); + String genericVAppURI = vAppURI.toString().substring(0, vAppURI.toString().indexOf("vApp") + vAppStr.length()); + rootBuilder.e("VApp").a("href", genericVAppURI).a("type", "application/vnd.vmware.vcloud.vApp+xml"); + } + + protected XMLBuilder buildRoot(String newVAppName) throws ParserConfigurationException, FactoryConfigurationError { + XMLBuilder rootBuilder = XMLBuilder.create("CloneVAppParams") + .a("xmlns", "http://www.vmware.com/vcloud/v0.8") + .a("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") + .a("name", newVAppName) + .a("deploy" , "true") + .a("powerOn" , "true") + .a("xsi:schemaLocation", "http://www.vmware.com/vcloud/v0.8 https://api.symphonyvpdc.savvis.net/ns/vcloud.xsd"); + return rootBuilder; + } + + protected String ifNullDefaultTo(String value, String defaultValue) { + return value != null ? value : checkNotNull(defaultValue, "defaultValue"); + } + +} diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindVMSpecToXmlPayload.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindVMSpecToXmlPayload.java index 3e70e76c33..c80178d6b9 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindVMSpecToXmlPayload.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/binders/BindVMSpecToXmlPayload.java @@ -22,6 +22,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; @@ -54,13 +56,15 @@ public class BindVMSpecToXmlPayload extends BindToStringPayload implements MapBi } - protected VMSpec findSpecInArgsOrNull(GeneratedHttpRequest gRequest) { + protected List findSpecInArgsOrNull(GeneratedHttpRequest gRequest) { for (Object arg : gRequest.getArgs()) { if (arg instanceof VMSpec) { - return (VMSpec) arg; - } else if (arg instanceof VMSpec[]) { - VMSpec[] configuration = (VMSpec[]) arg; - return (configuration.length > 0) ? configuration[0] : null; + List vmSpecs = new ArrayList(); + vmSpecs.add((VMSpec) arg); + return vmSpecs; + } else if (arg instanceof ArrayList) { + List configurations = (List) arg; + return (configurations.size() > 0) ? configurations : null; } } return null; @@ -74,27 +78,32 @@ public class BindVMSpecToXmlPayload extends BindToStringPayload implements MapBi checkState(gRequest.getArgs() != null, "args should be initialized at this point"); request = super.bindToRequest(request, - generateXml(findSpecInArgsOrNull(gRequest), postParams.get("name"), postParams.get("networkName"))); + generateXml(findSpecInArgsOrNull(gRequest))); request.getPayload().getContentMetadata().setContentType(MediaType.APPLICATION_XML); return request; } - public String generateXml(VMSpec spec, String name, String networkName) { - checkNotNull(spec, "VMSpec"); - checkNotNull(name, "name"); - checkNotNull(networkName, "networkName"); - - try { - XMLBuilder rootBuilder = buildRootForName(name); - addOperatingSystemSection(rootBuilder, spec.getOperatingSystem()); - addVirtualHardwareSection(rootBuilder, name, networkName, spec); - - Properties outputProperties = new Properties(); - outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); - return rootBuilder.asString(outputProperties); - } catch (Exception e) { - return null; - } + public String generateXml(List specs) { + try { + XMLBuilder rootBuilder = buildRoot(); + XMLBuilder vAppChildrenBuilder = buildChildren(rootBuilder); + for (VMSpec spec : specs) { + checkNotNull(spec, "VMSpec"); + checkNotNull(spec.getName(), "name"); + checkNotNull(spec.getNetwork(), "network"); + checkNotNull(spec.getNetwork().getName(), "networkName"); + XMLBuilder vAppBuilder = buildRootForName(vAppChildrenBuilder, spec.getName()); + addOperatingSystemSection(vAppBuilder, spec.getOperatingSystem()); + // TODO: Savvis returns network names with a - instead of space on getNetworkInVDC call, + // fix this once savvis api starts returning correctly + addVirtualHardwareSection(vAppBuilder, spec.getName(), spec.getNetwork().getName().replace("-", " "), spec); + } + Properties outputProperties = new Properties(); + outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes"); + return rootBuilder.asString(outputProperties); + } catch (Exception e) { + return null; + } } void addVirtualHardwareSection(XMLBuilder rootBuilder, String name, String networkName, VMSpec spec) { @@ -181,17 +190,29 @@ public class BindVMSpecToXmlPayload extends BindToStringPayload implements MapBi dataDiskBuilder.e("rasd:VirtualQuantity").t(dataDisk.getValue() + ""); } } - - protected XMLBuilder buildRootForName(String name) throws ParserConfigurationException, FactoryConfigurationError { + + protected XMLBuilder buildRoot() throws ParserConfigurationException, FactoryConfigurationError { XMLBuilder rootBuilder = XMLBuilder.create("vApp:VApp") + .a("xmlns:vApp", "http://www.vmware.com/vcloud/v0.8") + .a("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1") + .a("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData") .a("xmlns:common", "http://schemas.dmtf.org/wbem/wscim/1/common") - .a("xmlns:vApp", "http://www.vmware.com/vcloud/v0.8") .a("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData") - .a("xmlns:vssd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData") - .a("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1").a("name", name) - .a("type", "application/vnd.vmware.vcloud.vApp+xml").a("href", ""); + .a("name", ""); return rootBuilder; } + + protected XMLBuilder buildChildren(XMLBuilder rootBuilder) throws ParserConfigurationException, FactoryConfigurationError { + XMLBuilder vAppChildrenBuilder = rootBuilder.e("vApp:Children"); + return vAppChildrenBuilder; + } + + protected XMLBuilder buildRootForName(XMLBuilder rootBuilder, String name) throws ParserConfigurationException, FactoryConfigurationError { + XMLBuilder vAppBuilder = rootBuilder.e("vApp:VApp") + .a("name", name) + .a("type", "application/vnd.vmware.vcloud.vApp+xml"); + return vAppBuilder; + } protected String ifNullDefaultTo(String value, String defaultValue) { return value != null ? value : checkNotNull(defaultValue, "defaultValue"); diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/domain/VMSpec.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/domain/VMSpec.java index b8426f2d2b..bce859a123 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/domain/VMSpec.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/domain/VMSpec.java @@ -40,6 +40,8 @@ public class VMSpec { } public static class Builder { + private String name; + private Network network; private CIMOperatingSystem operatingSystem; private int processorCount = 1; private int memoryInGig = 1; @@ -48,6 +50,16 @@ public class VMSpec { private int bootDriveSize = 25; private Map dataDriveDeviceNameToSizeInGig = Maps.newLinkedHashMap(); + public Builder name(String name) { + this.name = checkNotNull(name, "name"); + return this; + } + + public Builder network(Network network) { + this.network = checkNotNull(network, "network"); + return this; + } + public Builder operatingSystem(CIMOperatingSystem operatingSystem) { this.operatingSystem = checkNotNull(operatingSystem, "operatingSystem"); return this; @@ -89,7 +101,7 @@ public class VMSpec { } public VMSpec build() { - return new VMSpec(operatingSystem, processorCount, memoryInGig, bootDeviceName, bootDriveSize, + return new VMSpec(name, network, operatingSystem, processorCount, memoryInGig, bootDeviceName, bootDriveSize, dataDriveDeviceNameToSizeInGig); } @@ -106,6 +118,8 @@ public class VMSpec { checkArgument(processorCount % .5 == 0, "processorCount must be an increment of 0.5"); } + private final String name; + private final Network network; private final CIMOperatingSystem operatingSystem; private final int processorCount; private final int memoryInGig; @@ -113,8 +127,10 @@ public class VMSpec { private final int bootDriveSize; private final Map dataDriveDeviceNameToSizeInGig; - protected VMSpec(CIMOperatingSystem operatingSystem, int processorCount, int memoryInGig, String bootDeviceName, + protected VMSpec(String name, Network network, CIMOperatingSystem operatingSystem, int processorCount, int memoryInGig, String bootDeviceName, int bootDriveSize, Map dataDriveDeviceNameToSizeInGig) { + this.name = name; + this.network = network; this.operatingSystem = checkNotNull(operatingSystem, "operatingSystem not specified"); checkProcessorCount(processorCount); this.processorCount = processorCount; @@ -127,6 +143,14 @@ public class VMSpec { "dataDriveDeviceNameToSizeInGig")); } + public String getName() { + return name; + } + + public Network getNetwork() { + return network; + } + public CIMOperatingSystem getOperatingSystem() { return operatingSystem; } @@ -204,8 +228,8 @@ public class VMSpec { @Override public String toString() { - return "[operatingSystem=" + operatingSystem + ", processorCount=" + processorCount + ", memoryInGig=" - + memoryInGig + ", bootDeviceName=" + bootDeviceName + ", bootDriveSize=" + bootDriveSize + return "[name= " + name + ", operatingSystem=" + operatingSystem + ", processorCount=" + processorCount + ", memoryInGig=" + + memoryInGig + ", network=" + network.getName() + ", bootDeviceName=" + bootDeviceName + ", bootDriveSize=" + bootDriveSize + ", dataDriveDeviceNameToSizeInGig=" + dataDriveDeviceNameToSizeInGig + "]"; } diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMAsyncClient.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMAsyncClient.java index e060dbf8e4..a1175f4420 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMAsyncClient.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMAsyncClient.java @@ -19,6 +19,8 @@ package org.jclouds.savvis.vpdc.features; import java.net.URI; +import java.util.List; +import java.util.Set; import javax.annotation.Nullable; import javax.ws.rs.DELETE; @@ -35,12 +37,15 @@ 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.binders.BindCaptureVAppTemplateToXmlPayload; +import org.jclouds.savvis.vpdc.binders.BindCloneVMToXmlPayload; import org.jclouds.savvis.vpdc.binders.BindVMSpecToXmlPayload; import org.jclouds.savvis.vpdc.domain.Task; import org.jclouds.savvis.vpdc.domain.VMSpec; import org.jclouds.savvis.vpdc.filters.SetVCloudTokenCookie; import org.jclouds.savvis.vpdc.functions.DefaultOrgIfNull; import org.jclouds.savvis.vpdc.xml.TaskHandler; +import org.jclouds.savvis.vpdc.xml.TasksListHandler; import com.google.common.util.concurrent.ListenableFuture; @@ -75,7 +80,36 @@ public interface VMAsyncClient { @MapBinder(BindVMSpecToXmlPayload.class) ListenableFuture addVMIntoVDC(@EndpointParam URI vpdc, @PayloadParam("networkName") String networkTierName, @PayloadParam("name") String vAppName, VMSpec spec); + + /** + * @see VMClient#addMultipleVMsIntoVDC + */ + @GET + @XMLResponseParser(TasksListHandler.class) + @Path("vApp/") + @MapBinder(BindVMSpecToXmlPayload.class) + ListenableFuture> addMultipleVMsIntoVDC(@EndpointParam URI vpdc, List vmSpecs); + /** + * @see VMClient#captureVApp + */ + @POST + @XMLResponseParser(TaskHandler.class) + @Path("v{jclouds.api-version}/org/{billingSiteId}/vdc/{vpdcId}/action/captureVApp") + @MapBinder(BindCaptureVAppTemplateToXmlPayload.class) + ListenableFuture captureVApp(@PathParam("billingSiteId") @Nullable @ParamParser(DefaultOrgIfNull.class) String billingSiteId, + @PathParam("vpdcId") String vpdcId, URI vAppUri); + + /** + * @see VMClient#cloneVApp + */ + @POST + @XMLResponseParser(TaskHandler.class) + @Path("action/cloneVApp") + @MapBinder(BindCloneVMToXmlPayload.class) + ListenableFuture cloneVApp(@EndpointParam URI vAppUri, @PayloadParam("name") String newVAppName, + @PayloadParam("networkTierName") String networkTierName); + /** * @see VMClient#removeVMFromVDC */ diff --git a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMClient.java b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMClient.java index 57e9549b1a..086d4120b7 100644 --- a/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMClient.java +++ b/providers/savvis-symphonyvpdc/src/main/java/org/jclouds/savvis/vpdc/features/VMClient.java @@ -19,6 +19,8 @@ package org.jclouds.savvis.vpdc.features; import java.net.URI; +import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; @@ -58,7 +60,42 @@ public interface VMClient { * @see #addVMIntoVDC */ Task addVMIntoVDC(URI vpdc, String networkTierName, String name, VMSpec spec); + + /** + * + * @param vpdc + * href of the vpdc + * @param vmSpecs + * vm configurations + * @return VM's in progress + */ + Set addMultipleVMsIntoVDC(URI vpdc, List vmSpecs); + /** + * + * @param billingSiteId + * billing site Id, or null for default + * @param vpdcId + * vpdc Id + * @param vAppUri + * href of the vApp + * @return + * Task with vAppTemplate href + */ + Task captureVApp(String billingSiteId, String vpdcId, URI vAppUri); + + /** + * + * @param vAppUri + * href of the vApp + * @param newVAppName + * name for the new vApp + * @param networkTierName + * network tier name for vApp + * @return + */ + Task cloneVApp(URI vAppUri, String newVAppName, String networkTierName); + /** * Remove a VM *

diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayloadTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayloadTest.java new file mode 100644 index 0000000000..30c0a843e6 --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindCaptureVAppTemplateToXmlPayloadTest.java @@ -0,0 +1,46 @@ +/** + * + * Copyright (C) 2011 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.binders; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.URI; + +import org.jclouds.util.Strings2; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code BindVMSpecToXmlPayload} + * + * @author Kedar Dave + */ +@Test(groups = "unit") +public class BindCaptureVAppTemplateToXmlPayloadTest { + + public void test() throws IOException { + String expected = Strings2.toStringAndClose(getClass().getResourceAsStream("/capture-vapp-template-default.xml")); + + URI vAppURI = URI.create("https://api.symphonyVPDC.savvis.net/rest/api/v0.8/org/100000.0/vdc/2736/vApp/1001"); + String xml = new BindCaptureVAppTemplateToXmlPayload().generateXml(vAppURI); + System.out.println(xml); + + assertEquals(new BindCaptureVAppTemplateToXmlPayload().generateXml(vAppURI), expected); + } +} diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindCloneVAppToXmlPayloadTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindCloneVAppToXmlPayloadTest.java new file mode 100644 index 0000000000..bf76ed2f9d --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindCloneVAppToXmlPayloadTest.java @@ -0,0 +1,46 @@ +/** + * + * Copyright (C) 2011 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.binders; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.net.URI; + +import org.jclouds.util.Strings2; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code BindVMSpecToXmlPayload} + * + * @author Kedar Dave + */ +@Test(groups = "unit") +public class BindCloneVAppToXmlPayloadTest { + + public void test() throws IOException { + String expected = Strings2.toStringAndClose(getClass().getResourceAsStream("/cloneVApp-default.xml")); + URI vAppURI = URI.create("https://api.symphonyVPDC.savvis.net/rest/api/v0.8/org/100000.0/vdc/2736/vApp/1001"); + + String xml = new BindCloneVMToXmlPayload().generateXml(vAppURI, "clonedvm", "VM Tier01"); + System.out.println(xml); + + assertEquals(new BindCloneVMToXmlPayload().generateXml(vAppURI, "clonedvm", "VM Tier01"), expected); + } +} diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindVMSpecToXmlPayloadTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindVMSpecToXmlPayloadTest.java index 661b6fc48e..ad60d2bd90 100644 --- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindVMSpecToXmlPayloadTest.java +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/binders/BindVMSpecToXmlPayloadTest.java @@ -21,10 +21,13 @@ package org.jclouds.savvis.vpdc.binders; import static org.testng.Assert.assertEquals; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import org.jclouds.cim.OSType; import org.jclouds.compute.domain.CIMOperatingSystem; +import org.jclouds.savvis.vpdc.domain.Network; import org.jclouds.savvis.vpdc.domain.VMSpec; import org.jclouds.util.Strings2; import org.testng.annotations.Test; @@ -56,10 +59,14 @@ public class BindVMSpecToXmlPayloadTest { }); - String expected = Strings2.toStringAndClose(getClass().getResourceAsStream("/vm-default.xml")); + Network network = Network.builder().name("VM Tier01").build(); - VMSpec spec = VMSpec.builder().operatingSystem(os).build(); + String expected = Strings2.toStringAndClose(getClass().getResourceAsStream("/vm-multiple-default.xml")); - assertEquals(new BindVMSpecToXmlPayload().generateXml(spec, "DemoHost-1", "VM Tier01"), expected); + VMSpec spec = VMSpec.builder().name("Test VM").operatingSystem(os).network(network).build(); + List specs = new ArrayList(); + specs.add(spec); + + assertEquals(new BindVMSpecToXmlPayload().generateXml(specs), expected); } } diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java index 4bf96cd284..0080de9373 100644 --- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/BaseVPDCClientLiveTest.java @@ -89,7 +89,7 @@ public class BaseVPDCClientLiveTest { context = new ComputeServiceContextFactory().createContext(provider, ImmutableSet. of( new Log4JLoggingModule(), new JschSshClientModule()), overrides); restContext = context.getProviderSpecificContext(); - taskTester = new RetryablePredicate(new TaskSuccess(restContext.getApi()), 1200, 10, TimeUnit.SECONDS); + taskTester = new RetryablePredicate(new TaskSuccess(restContext.getApi()), 7200, 10, TimeUnit.SECONDS); } @AfterGroups(groups = "live") diff --git a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMClientLiveTest.java b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMClientLiveTest.java index 3b7a37ca54..78a5747e06 100644 --- a/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMClientLiveTest.java +++ b/providers/savvis-symphonyvpdc/src/test/java/org/jclouds/savvis/vpdc/features/VMClientLiveTest.java @@ -22,6 +22,9 @@ import static com.google.common.base.Preconditions.checkNotNull; import static org.testng.Assert.assertEquals; import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.jclouds.cim.OSType; @@ -31,6 +34,7 @@ import org.jclouds.domain.Credentials; import org.jclouds.net.IPSocket; import org.jclouds.predicates.InetSocketAddressConnect; import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.savvis.vpdc.domain.Network; import org.jclouds.savvis.vpdc.domain.Resource; import org.jclouds.savvis.vpdc.domain.Task; import org.jclouds.savvis.vpdc.domain.VDC; @@ -125,6 +129,144 @@ public class VMClientLiveTest extends BaseVPDCClientLiveTest { conditionallyCheckSSH(); } + public void testCreateMultipleVMs() throws Exception { + billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default + vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), + new Predicate() { + + // try to find the first VDC owned by the current user + // check here for what the email property might be, or in + // the jclouds-wire.log + @Override + public boolean apply(Resource arg0) { + String description = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, + arg0.getId()).getDescription(); + return description.indexOf(email) != -1; + } + + }).getId(); + + String networkTierName = Iterables.get( + restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, vpdcId).getAvailableNetworks(), 0) + .getId(); + Network networkTier = restContext.getApi().getBrowsingClient().getNetworkInVDC(billingSiteId, vpdcId, networkTierName); + + String name = prefix; + + // delete any old VM + VDC vpdc = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, vpdcId); + CIMOperatingSystem os = Iterables.find(restContext.getApi().listPredefinedOperatingSystems(), + new Predicate() { + + @Override + public boolean apply(CIMOperatingSystem arg0) { + return arg0.getOsType() == OSType.RHEL_64; + } + + }); + + // TODO: Savvis returns network names with a - instead of space on getNetworkInVDC call, + // fix this once savvis api starts returning correctly + System.out.printf("vpdcId %s, vpdcName %s, networkName %s, name %s, os %s%n", vpdcId, vpdc.getName(), networkTier.getName().replace("-", " "), name, os); + + List vmSpecs = new ArrayList(); + int noOfVms = 2; + for (int i = 0; i < noOfVms; i++) { + // TODO: determine the sizes available in the VDC, for example there's + // a minimum size of boot disk, and also a preset combination of cpu count vs ram + VMSpec vmSpec = VMSpec.builder().name(name + i).operatingSystem(os).memoryInGig(2).network(networkTier).addDataDrive("/data01", 25).build(); + vmSpecs.add(vmSpec); + } + + Set tasks = client.addMultipleVMsIntoVDC(vpdc.getHref(), vmSpecs); + + for (Task task : tasks) { + // make sure there's no error + assert task.getId() != null && task.getError() == null : task; + + assert this.taskTester.apply(task.getId()); + } + } + + public void testCaptureVAppTemplate() throws Exception { + billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default + vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), + new Predicate() { + + // try to find the first VDC owned by the current user + // check here for what the email property might be, or in + // the jclouds-wire.log + @Override + public boolean apply(Resource arg0) { + String description = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, + arg0.getId()).getDescription(); + return description.indexOf(email) != -1; + } + + }).getId(); + + VDC vpdc = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, vpdcId); + + for (Resource vApp : Iterables.filter(vpdc.getResourceEntities(), new Predicate() { + + @Override + public boolean apply(Resource arg0) { + return VCloudMediaType.VAPP_XML.equals(arg0.getType()); + } + + })) { + + System.out.printf("Capturing VAppTemplate for vApp - %s%n", vApp.getName()); + Task task = client.captureVApp(billingSiteId, vpdcId, vApp.getHref()); + + // make sure there's no error + assert task.getId() != null && task.getError() == null : task; + + assert this.taskTester.apply(task.getId()); + } + } + + public void testCloneVApp() throws Exception { + billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default + vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), + new Predicate() { + + // try to find the first VDC owned by the current user + // check here for what the email property might be, or in + // the jclouds-wire.log + @Override + public boolean apply(Resource arg0) { + String description = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, + arg0.getId()).getDescription(); + return description.indexOf(email) != -1; + } + + }).getId(); + + VDC vpdc = restContext.getApi().getBrowsingClient().getVDCInOrg(billingSiteId, vpdcId); + + String networkTierName = Iterables.get(vpdc.getAvailableNetworks(), 0).getId(); + + for (Resource vApp : Iterables.filter(vpdc.getResourceEntities(), new Predicate() { + + @Override + public boolean apply(Resource arg0) { + return VCloudMediaType.VAPP_XML.equals(arg0.getType()); + } + + })) { + + System.out.printf("Cloning VApp - %s%n", vApp.getName()); + + Task task = client.cloneVApp(vApp.getHref(), "clonedvm", networkTierName); + + // make sure there's no error + assert task.getId() != null && task.getError() == null : task; + + assert this.taskTester.apply(task.getId()); + } + } + private void conditionallyCheckSSH() { String ip = Iterables.get(vm.getNetworkConnectionSections(), 0).getIpAddress(); assert HostSpecifier.isValid(ip); @@ -152,6 +294,7 @@ public class VMClientLiveTest extends BaseVPDCClientLiveTest { } } + @Test(enabled = false) public void testPowerOffVM() throws Exception { billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), @@ -193,6 +336,7 @@ public class VMClientLiveTest extends BaseVPDCClientLiveTest { assert this.taskTester.apply(task.getId()); } + @Test(enabled = false) public void testPowerOnVM() throws Exception { billingSiteId = restContext.getApi().getBrowsingClient().getOrg(null).getId();// default vpdcId = Iterables.find(restContext.getApi().getBrowsingClient().getOrg(billingSiteId).getVDCs(), diff --git a/providers/savvis-symphonyvpdc/src/test/resources/capture-vapp-template-default.xml b/providers/savvis-symphonyvpdc/src/test/resources/capture-vapp-template-default.xml new file mode 100644 index 0000000000..6d873ac2b6 --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/test/resources/capture-vapp-template-default.xml @@ -0,0 +1 @@ +Save Template \ No newline at end of file diff --git a/providers/savvis-symphonyvpdc/src/test/resources/cloneVApp-default.xml b/providers/savvis-symphonyvpdc/src/test/resources/cloneVApp-default.xml new file mode 100644 index 0000000000..2718f219f0 --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/test/resources/cloneVApp-default.xml @@ -0,0 +1 @@ +VM Tier01 \ No newline at end of file diff --git a/providers/savvis-symphonyvpdc/src/test/resources/vm-multiple-default.xml b/providers/savvis-symphonyvpdc/src/test/resources/vm-multiple-default.xml new file mode 100644 index 0000000000..c3f8c4da5c --- /dev/null +++ b/providers/savvis-symphonyvpdc/src/test/resources/vm-multiple-default.xml @@ -0,0 +1 @@ +Specifies the operating system installedRed Hat Enterprise Linux 5.x 64bitVirtual HardwareVirtual Hardware FamilyTest VM1Test VM3 GHzNumber of Virtual CPUs1 CPU131GigabytesMemory SizeMemory241falseVM Tier01Network3101GigabytesHard Disk/boot42725 \ No newline at end of file