vCloud enhancements [see also STRATOS-1192] - added support for using multiple NICs. We use the NIC type of the first NIC for all the new ones. That should be a reasonable approximation of what the user wants and what their machine supports, UNLESS he/she is for example doing some funky nested virtualisation in the cartridge. Fixed IP support is not supported yet. - vCloud guest customization script enhancements: rather than running customization scripts via SSH (this is used e.g. to inject metadata into the cartridge), use the vCloud guest customization script facility if at all possible. We don't want to always use ssh, since that imposes more requirements on the cartridge (root password change by vCloud, password-based SSH login enabled, firewall rules/network setup); the guest customization script is the preferable method. - Added rudimentary disk attach support to vCloud, in jcloud and Stratos.
Project: http://git-wip-us.apache.org/repos/asf/stratos/repo Commit: http://git-wip-us.apache.org/repos/asf/stratos/commit/ed952ac3 Tree: http://git-wip-us.apache.org/repos/asf/stratos/tree/ed952ac3 Diff: http://git-wip-us.apache.org/repos/asf/stratos/diff/ed952ac3 Branch: refs/heads/master-deployment-policy-fix-merge Commit: ed952ac390cdd3140fd76f8fd047991b9d2b1740 Parents: eb091c3 Author: Martin Eppel <[email protected]> Authored: Thu Mar 12 12:07:41 2015 -0700 Committer: Martin Eppel <[email protected]> Committed: Thu Mar 12 12:07:41 2015 -0700 ---------------------------------------------------------------------- .../controller/iaases/vcloud/VCloudIaas.java | 157 +++++++++- .../org/jclouds/vcloud/VCloudMediaType.java | 4 + ...indDiskAttachOrDetachParamsToXmlPayload.java | 100 +++++++ .../BindVCloudNetworkAdapterToXmlPayload.java | 141 +++++++++ ...EnvelopeOrThrowIllegalArgumentException.java | 3 - .../compute/options/VCloudTemplateOptions.java | 37 +-- ...edIntoNameThenCustomizeDeployAndPowerOn.java | 288 ++++++++++++++++--- .../vcloud/domain/DiskAttachOrDetachParams.java | 173 +++++++++++ .../java/org/jclouds/vcloud/features/VmApi.java | 67 +++-- .../jclouds/vcloud/xml/OrgNetworkHandler.java | 4 +- .../options/VCloudTemplateOptionsTest.java | 20 +- ...ThenCustomizeDeployAndPowerOnExpectTest.java | 14 +- 12 files changed, 900 insertions(+), 108 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/vcloud/VCloudIaas.java ---------------------------------------------------------------------- diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/vcloud/VCloudIaas.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/vcloud/VCloudIaas.java index 6a91823..b1f74b9 100644 --- a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/vcloud/VCloudIaas.java +++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/vcloud/VCloudIaas.java @@ -28,6 +28,7 @@ import org.apache.stratos.cloud.controller.util.ComputeServiceBuilderUtil; import org.apache.stratos.cloud.controller.domain.IaasProvider; import org.apache.stratos.cloud.controller.iaases.PartitionValidator; import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceContext; import org.jclouds.compute.domain.NodeMetadata; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; @@ -38,7 +39,34 @@ import org.wso2.carbon.utils.CarbonUtils; import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Hashtable; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; +import java.net.URI; + +import org.apache.stratos.cloud.controller.domain.IaasProvider; +import org.apache.stratos.cloud.controller.domain.NetworkInterface; +import org.apache.stratos.cloud.controller.iaases.vcloud.VCloudPartitionValidator; +import org.apache.stratos.cloud.controller.iaases.PartitionValidator; +import org.jclouds.ContextBuilder; +import org.jclouds.rest.RestContext; +import org.jclouds.vcloud.VCloudApi; +import org.jclouds.vcloud.domain.NetworkConnectionSection; +import org.jclouds.vcloud.domain.ovf.VCloudHardDisk; +import org.jclouds.vcloud.domain.NetworkConnection; +import org.jclouds.vcloud.domain.Task; +import org.jclouds.vcloud.domain.Vm; +import org.jclouds.vcloud.domain.network.OrgNetwork; +import org.jclouds.vcloud.domain.ovf.VCloudVirtualHardwareSection; +import org.jclouds.vcloud.features.VAppApi; +import org.jclouds.vcloud.features.VmApi; +import org.jclouds.vcloud.domain.VApp; +import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; +import org.jclouds.vcloud.domain.DiskAttachOrDetachParams; + + public class VCloudIaas extends JcloudsIaas { @@ -74,6 +102,12 @@ public class VCloudIaas extends JcloudsIaas { log.fatal(msg); throw new CloudControllerException(msg); } + + //get 'native' version of jclouds' vCloud API. + ComputeServiceContext context = iaasInfo.getComputeService().getContext(); + VCloudApi api = context.unwrapApi(VCloudApi.class); + + TemplateBuilder templateBuilder = iaasInfo.getComputeService() .templateBuilder(); @@ -98,9 +132,49 @@ public class VCloudIaas extends JcloudsIaas { // groups by Jclouds. template.getOptions().as(TemplateOptions.class) .inboundPorts(22, 80, 8080, 443, 8243); - - template.getOptions().as(VCloudTemplateOptions.class) - .ipAddressAllocationMode(IpAddressAllocationMode.POOL); + + if (iaasInfo.getNetworkInterfaces() != null) { + Set<String> networksSet = new LinkedHashSet<String>(iaasInfo.getNetworkInterfaces().length); + Hashtable<String, NetworkConnection> vcloudNetworkOptions = new Hashtable<String, NetworkConnection>(iaasInfo.getNetworkInterfaces().length); + + int i = 0; + for (NetworkInterface ni : iaasInfo.getNetworkInterfaces()) { + + String networkUuid = ni.getNetworkUuid(); + String networkName = null; + IpAddressAllocationMode ipAllocMode = IpAddressAllocationMode.NONE; + if (ni.getFixedIp() != null && !ni.getFixedIp().equals("")) { + ipAllocMode = IpAddressAllocationMode.MANUAL; + } else { + ipAllocMode = IpAddressAllocationMode.POOL; + } + + //fetch network name. + try { + OrgNetwork orgNet = api.getNetworkApi().getNetwork(new URI(networkUuid)); + networkName = orgNet.getName(); + } catch (URISyntaxException e) { + log.error("Network UUID '" + networkUuid + "' is not a URI/href."); + } + NetworkConnection nc = new NetworkConnection(networkName, i, ni.getFixedIp(), null, true, + null, //TODO: support fixed Mac addrs. + ipAllocMode); + networksSet.add(networkUuid); + vcloudNetworkOptions.put(networkUuid, nc); + + i++; + } + //new NetworkConnectionSection() + + //VmApi vmApi = api.getVmApi(); + //vmApi.updateNetworkConnectionOfVm(); + + template.getOptions().networks(networksSet); + template.getOptions().as(VCloudTemplateOptions.class).networkConnections(vcloudNetworkOptions); + } + + //template.getOptions().as(VCloudTemplateOptions.class) + // .ipAddressAllocationMode(IpAddressAllocationMode.POOL); // set Template iaasInfo.setTemplate(template); @@ -111,7 +185,7 @@ public class VCloudIaas extends JcloudsIaas { // in vCloud case we need to run a script IaasProvider iaasProvider = getIaasProvider(); - if (iaasProvider.getTemplate() == null) { + if (iaasProvider.getTemplate() == null || payload == null) { if (log.isDebugEnabled()) { log.debug("Payload for vCloud not found"); } @@ -177,9 +251,23 @@ public class VCloudIaas extends JcloudsIaas { if (log.isDebugEnabled()) { log.debug(String.format("The vCloud Customization script\n%s", customizationScript)); } + + // Ensure the script is run. + if (customizationScript.length() >= 49000) { + log.warn("The vCloud customization script is >=49000 bytes in size; uploading dummy script and running real script via ssh."); + String dummyCustomizationScript = "#!/bin/sh\n" + + "#STRATOS: the real customization script will be invoked via ssh, since it exceeds the 49000 byte limit " + + "imposed by vCloud;\n" + + "#see " + + "http://pubs.vmware.com/vcd-55/topic/com.vmware.vcloud.api.doc_55/GUID-1BA3B7C5-B46C-48F7-8704-945BC47A940D.html\n"; + template.getOptions().as(VCloudTemplateOptions.class).customizationScript(dummyCustomizationScript); + template.getOptions().runScript(customizationScript); + } else { + template.getOptions().as(VCloudTemplateOptions.class).customizationScript(customizationScript); + } // Run the script - template.getOptions().runScript(customizationScript); + //template.getOptions().runScript(customizationScript); } @Override @@ -238,8 +326,63 @@ public class VCloudIaas extends JcloudsIaas { @Override public String attachVolume(String instanceId, String volumeId, String deviceName) { - // TODO Auto-generated method stub - return null; + IaasProvider iaasInfo = getIaasProvider(); + + if (StringUtils.isEmpty(volumeId)) { + log.error("Volume provided to attach can not be null"); + } + + if (StringUtils.isEmpty(instanceId)) { + log.error("Instance provided to attach can not be null"); + } + + URI instanceIdHref = null; + URI volumeIdHref = null; + try { + //the instanceId format is a bit silly for vCloud. + instanceIdHref = new URI("https:/" + instanceId); + } catch (URISyntaxException e) { + log.error("Failed to attach volume, because the instance id cannot be converted into a url by concatenating " + + "'https:/' with " + instanceId + ". Full stacktrace: " + e.toString()); + return null; + } + try { + volumeIdHref = new URI(volumeId); + } catch (URISyntaxException e) { + log.error("Failed to attach voluume, because the volume id '" + volumeId + "' is not a valid href (URI))" + + e.toString()); + } + + //get 'native' version of jclouds' vCloud API. + ComputeServiceContext context = iaasInfo.getComputeService() + .getContext(); + + VCloudApi api = context.unwrapApi(VCloudApi.class); + + //Disks need to be attached to individual VMs, not vApps. The instanceId is the VApp. + VAppApi vAppApi = api.getVAppApi(); + Set<Vm> vmsInVapp = vAppApi.getVApp(instanceIdHref).getChildren(); + //Each vApp today has just 1 VM in it. Validate assumption. + assert(vmsInVapp.size() == 1); + Vm vm = vmsInVapp.iterator().next(); + URI vmHref = vm.getHref(); + VmApi vmApi = api.getVmApi(); + + // invest + /* + VCloudHardDisk.Builder hardDiskBuilder = new VCloudHardDisk.Builder(); + VCloudHardDisk hardDisk = hardDiskBuilder.instanceID(volumeId).build(); + VCloudVirtualHardwareSection vvhs = vm.getVirtualHardwareSection(); + VCloudHardDisk.Builder Vchd = new VCloudHardDisk.Builder(); + vvhs.toBuilder().item(Vchd.capacity(3).instanceID("hgfhgf").build()).build(); + VApp va = vAppApi.getVApp(instanceIdHref); + + */ //EO invest + DiskAttachOrDetachParams params = new DiskAttachOrDetachParams(volumeIdHref); + Task t = vmApi.attachDisk(vmHref, params); + + log.info(String.format("Volume [id]: %s attachment for instance [id]: %s was successful [status]: Attaching. Iaas : %s, Task: %s", volumeId, instanceId, iaasInfo, t)); + return "Attaching"; } @Override http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/VCloudMediaType.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/VCloudMediaType.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/VCloudMediaType.java index 67701fb..7126701 100644 --- a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/VCloudMediaType.java +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/VCloudMediaType.java @@ -205,6 +205,10 @@ public interface VCloudMediaType { */ public static final String RASDITEM_XML = "application/vnd.vmware.vcloud.rasdItem+xml"; /** + * "application/vnd.vmware.vcloud.rasdItemsList+xml" + */ + public static final String RASDITEMLIST_XML = "application/vnd.vmware.vcloud.rasdItemsList+xml"; + /** * "application/vnd.vmware.vcloud.rasdItem+xml" */ public static final MediaType RASDITEM_XML_TYPE = new MediaType("application", "vnd.vmware.vcloud.rasdItem+xml"); http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/binders/BindDiskAttachOrDetachParamsToXmlPayload.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/binders/BindDiskAttachOrDetachParamsToXmlPayload.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/binders/BindDiskAttachOrDetachParamsToXmlPayload.java new file mode 100644 index 0000000..7e198aa --- /dev/null +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/binders/BindDiskAttachOrDetachParamsToXmlPayload.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.vcloud.binders; + +import com.google.common.base.Throwables; +import com.google.inject.Inject; +import com.jamesmurty.utils.XMLBuilder; +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.vcloud.domain.DiskAttachOrDetachParams; + +import javax.inject.Named; +import javax.inject.Singleton; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.util.Map; +import java.util.Properties; + +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; + +/** + * Created by mblokzij on 29/10/2014. + */ +@Singleton +public class BindDiskAttachOrDetachParamsToXmlPayload implements MapBinder { + + protected final String ns; + protected final String schema; + + protected final BindToStringPayload stringBinder; + + @Inject + public BindDiskAttachOrDetachParamsToXmlPayload(BindToStringPayload stringBinder, + @Named(PROPERTY_VCLOUD_XML_NAMESPACE) String ns, @Named(PROPERTY_VCLOUD_XML_SCHEMA) String schema) { + this.ns = ns; + this.schema = schema; + this.stringBinder = stringBinder; + } + + @Override + public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) { + checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + DiskAttachOrDetachParams params = (DiskAttachOrDetachParams) checkNotNull(postParams.remove("params"), "params"); + XMLBuilder diskAttachOrDetachParams; + String xml = null; + try { + diskAttachOrDetachParams = XMLBuilder.create("DiskAttachOrDetachParams").a("xmlns", ns); + XMLBuilder disk = diskAttachOrDetachParams.e("Disk"); + if (params.getHref() != null) { + disk.a("href", params.getHref().toString()); + } + if (params.getId() != null) { + disk.a("id", params.getId().toString()); + } + if (params.getName() != null) { + disk.a("name", params.getName()); + } + if (params.getType() != null) { + disk.a("type", params.getType()); + } + if (params.getBusNumber() != null) { + diskAttachOrDetachParams.e("BusNumber").t(params.getBusNumber().toString()); + } + if (params.getUnitNumber() != null) { + diskAttachOrDetachParams.e("UnitNumber").t(params.getUnitNumber().toString()); + } + + xml = diskAttachOrDetachParams.asString(); + } catch (Exception e) { + Throwables.propagate(e); + } + return stringBinder.bindToRequest(request, xml); + } + + @Override + public <R extends HttpRequest> R bindToRequest(R request, Object postParams) { + throw new IllegalStateException("BindDiskAttachOrDetachParamsToXmlPayload needs parameters"); + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/binders/BindVCloudNetworkAdapterToXmlPayload.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/binders/BindVCloudNetworkAdapterToXmlPayload.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/binders/BindVCloudNetworkAdapterToXmlPayload.java new file mode 100644 index 0000000..b3fe262 --- /dev/null +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/binders/BindVCloudNetworkAdapterToXmlPayload.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.vcloud.binders; + +import com.google.common.base.Throwables; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import com.jamesmurty.utils.XMLBuilder; +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.vcloud.domain.ovf.VCloudNetworkAdapter; + +import javax.inject.Singleton; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import java.util.Map; + +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; + +/** + * Created by michiel on 17/11/2014. + */ +@Singleton +public class BindVCloudNetworkAdapterToXmlPayload implements MapBinder { + + protected final String ns; + protected final String schema; + + protected final BindToStringPayload stringBinder; + + @Inject + public BindVCloudNetworkAdapterToXmlPayload(BindToStringPayload stringBinder, + @Named(PROPERTY_VCLOUD_XML_NAMESPACE) String ns, + @Named(PROPERTY_VCLOUD_XML_SCHEMA) String schema) { + this.ns = ns; + this.schema = schema; + this.stringBinder = stringBinder; + } + + @Override + public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) { + checkArgument(checkNotNull(request, "request") instanceof GeneratedHttpRequest, + "this binder is only valid for GeneratedHttpRequests!"); + + Iterable<VCloudNetworkAdapter> networkCards = (Iterable<VCloudNetworkAdapter>) checkNotNull(postParams.remove("params"), "params"); + + /* + * The Iterable<VCloudNetworkAdapter> needs to be turned into a RasdItemList. + */ + XMLBuilder rasdItemListBuilder; + String xml = null; + try { + rasdItemListBuilder = XMLBuilder.create("RasdItemsList").a("xmlns", ns); + //all sorts of other crazy XML attributes + rasdItemListBuilder.a("xmlns:rasd", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"); + rasdItemListBuilder.a("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + rasdItemListBuilder.a("type", "application/vnd.vmware.vcloud.rasdItemsList+xml"); + //rasdItemListBuilder.a("href", "https://<vcloud>/api/vApp/vm-<UUID>/virtualHardwareSection/networkCards"); + //rasdItemListBuilder.a("xsi:schemaLocation", "http://www.vmware.com/vcloud/v1.5 http://172.28.44.90/api/v1.5/schema/master.xsd http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.22.0/CIM_ResourceAllocationSettingData.xsd"); + + for (VCloudNetworkAdapter nic: networkCards) { + /* + * NOTE: the order of these items IS important. + */ + XMLBuilder rasdItem = rasdItemListBuilder.elem("Item"); + if (nic.getAddress() != null) + rasdItem.elem("rasd:Address").text(nic.getAddress()); + if (nic.getAddressOnParent() != null) + rasdItem.elem("rasd:AddressOnParent").text(nic.getAddressOnParent()); + if (nic.isAutomaticAllocation() != null) + rasdItem.elem("rasd:AutomaticAllocation").text(String.valueOf(nic.isAutomaticAllocation())); + + //the connection handling is a little bit more involved. + if (nic.getConnections().size() > 1) { + /* + * The IP address is an attribute of the <rasd:Connection /> element, and we only store + * 1 IP address for the whole NIC. It's not clear to me why the nic.getConnections() returns + * a list anyway. + */ + throw new UnsupportedOperationException("Currently we only support 1 connection per NIC."); + } + if (nic.getConnections() != null) { + for (String connection: nic.getConnections()) { + XMLBuilder c = rasdItem.elem("rasd:Connection").a("xmlns:vcloud", ns); + if (nic.getIpAddress() != null) + c.a("vcloud:ipAddress", nic.getIpAddress()); + + c.a("vcloud:primaryNetworkConnection", String.valueOf(nic.isPrimaryNetworkConnection())); + if (nic.getIpAddressingMode() != null) + c.a("vcloud:ipAddressingMode", nic.getIpAddressingMode()); + c.text(connection); + } + } + + if (nic.getDescription() != null) + rasdItem.elem("rasd:Description").text(nic.getDescription()); + if (nic.getElementName() != null) + rasdItem.elem("rasd:ElementName").text(nic.getElementName()); + if (nic.getInstanceID() != null) + rasdItem.elem("rasd:InstanceID").text(nic.getInstanceID()); + if (nic.getResourceSubType() != null) + rasdItem.elem("rasd:ResourceSubType").text(nic.getResourceSubType()); + if (nic.getResourceType() != null) + rasdItem.elem("rasd:ResourceType").text(nic.getResourceType().value()); + + //TODO: remaining items + } + xml = rasdItemListBuilder.asString(); + } catch (ParserConfigurationException e) { + Throwables.propagate(e); + } catch (TransformerException e) { + Throwables.propagate(e); + } + + return stringBinder.bindToRequest(request, xml); + } + + @Override + public <R extends HttpRequest> R bindToRequest(R request, Object postParams) { + throw new IllegalStateException("BindVCloudNetworkAdapterToXmlPayload needs parameters"); + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/functions/ValidateVAppTemplateAndReturnEnvelopeOrThrowIllegalArgumentException.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/functions/ValidateVAppTemplateAndReturnEnvelopeOrThrowIllegalArgumentException.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/functions/ValidateVAppTemplateAndReturnEnvelopeOrThrowIllegalArgumentException.java index dce8306..c08a622 100644 --- a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/functions/ValidateVAppTemplateAndReturnEnvelopeOrThrowIllegalArgumentException.java +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/functions/ValidateVAppTemplateAndReturnEnvelopeOrThrowIllegalArgumentException.java @@ -51,9 +51,6 @@ public class ValidateVAppTemplateAndReturnEnvelopeOrThrowIllegalArgumentExceptio public Envelope apply(VAppTemplate from) { checkArgument(from.getChildren().size() == 1, "multiple vms are not supported: %s", from); - checkArgument(from.getNetworkSection().getNetworks().size() == 1, - "multiple network connections are not supported: %s", from); - checkArgument(from.isOvfDescriptorUploaded(), "ovf descriptor is not uploaded: %s", from); Envelope ovf = getOVFForVAppTemplateAndValidate(from); return ovf; http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java index 03b12fe..0910db4 100644 --- a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptions.java @@ -21,9 +21,11 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.emptyToNull; import java.net.URI; +import java.util.Hashtable; import java.util.Map; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.vcloud.domain.NetworkConnection; import org.jclouds.vcloud.domain.network.FenceMode; import org.jclouds.vcloud.domain.network.IpAddressAllocationMode; @@ -62,10 +64,8 @@ public class VCloudTemplateOptions extends TemplateOptions implements Cloneable eTo.customizationScript(getCustomizationScript()); if (getDescription() != null) eTo.description(getDescription()); - if (getIpAddressAllocationMode() != null) - eTo.ipAddressAllocationMode(getIpAddressAllocationMode()); - if (getIpAddressAllocationMode() != null) - eTo.ipAddressAllocationMode(getIpAddressAllocationMode()); + if (getNetworkConnections() != null) + eTo.networkConnections(getNetworkConnections()); if (getParentNetwork() != null) eTo.parentNetwork(getParentNetwork()); if (getFenceMode() != null) @@ -75,7 +75,9 @@ public class VCloudTemplateOptions extends TemplateOptions implements Cloneable private String description = null; private String customizationScript = null; - private IpAddressAllocationMode ipAddressAllocationMode = null; + // This Hashtable maps TemplateOptions.networks[i] to each networks' specific vCloud options + // We index by network 'name' (href in vCloud) + private Hashtable<String, NetworkConnection> networkConnections = null; private URI parentNetwork = null; private FenceMode fenceMode = null; @@ -88,20 +90,20 @@ public class VCloudTemplateOptions extends TemplateOptions implements Cloneable VCloudTemplateOptions that = VCloudTemplateOptions.class.cast(o); return super.equals(that) && equal(this.description, that.description) && equal(this.customizationScript, that.customizationScript) - && equal(this.ipAddressAllocationMode, that.ipAddressAllocationMode) + && equal(this.networkConnections, that.networkConnections) && equal(this.parentNetwork, that.parentNetwork); } @Override public int hashCode() { - return Objects.hashCode(super.hashCode(), description, customizationScript, ipAddressAllocationMode, + return Objects.hashCode(super.hashCode(), description, customizationScript, networkConnections, parentNetwork); } @Override public ToStringHelper string() { return super.string().add("description", description).add("customizationScript", customizationScript) - .add("ipAddressAllocationMode", ipAddressAllocationMode).add("parentNetwork", parentNetwork); + .add("networkConnections", networkConnections).add("parentNetwork", parentNetwork); } /** @@ -122,11 +124,12 @@ public class VCloudTemplateOptions extends TemplateOptions implements Cloneable } /** - * Specifies the ipAddressAllocationMode used to for network interfaces on - * the VMs + * Specifies the networkConnections settings used to for network interfaces on + * the VMs. This lets you specify things like ip address allocation mode, + * the actual IP (in case of fixed IP) etc. */ - public VCloudTemplateOptions ipAddressAllocationMode(IpAddressAllocationMode ipAddressAllocationMode) { - this.ipAddressAllocationMode = ipAddressAllocationMode; + public VCloudTemplateOptions networkConnections(Hashtable<String, NetworkConnection> networkConnections) { + this.networkConnections = networkConnections; return this; } @@ -167,10 +170,10 @@ public class VCloudTemplateOptions extends TemplateOptions implements Cloneable } /** - * @see VCloudTemplateOptions#ipAddressAllocationMode + * @see VCloudTemplateOptions#networkConnections */ - public static VCloudTemplateOptions ipAddressAllocationMode(IpAddressAllocationMode ipAddressAllocationMode) { - return new VCloudTemplateOptions().ipAddressAllocationMode(ipAddressAllocationMode); + public static VCloudTemplateOptions networkConnections(Hashtable<String, NetworkConnection> networkConnections) { + return new VCloudTemplateOptions().networkConnections(networkConnections); } /** @@ -255,9 +258,7 @@ public class VCloudTemplateOptions extends TemplateOptions implements Cloneable /** * @return ipAddressAllocationMode on the vms */ - public IpAddressAllocationMode getIpAddressAllocationMode() { - return ipAddressAllocationMode; - } + public Hashtable<String, NetworkConnection> getNetworkConnections() { return networkConnections; } /** * @return parentNetwork to connect to the vms http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java index e8b9fcd..73868fb 100644 --- a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.java @@ -22,17 +22,22 @@ import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.get; import static org.jclouds.compute.util.ComputeServiceUtils.getCores; import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsCommaDelimitedValue; +import static org.jclouds.util.Predicates2.retry; import static org.jclouds.vcloud.compute.util.VCloudComputeUtils.getCredentialsFrom; -import static org.jclouds.vcloud.options.InstantiateVAppTemplateOptions.Builder.addNetworkConfig; +import static java.util.concurrent.TimeUnit.SECONDS; import java.net.URI; -import java.util.Map; +import java.net.URISyntaxException; +import java.util.*; import javax.annotation.Resource; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import org.jclouds.cim.ResourceAllocationSettingData; import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials; import org.jclouds.compute.domain.Template; import org.jclouds.compute.reference.ComputeServiceConstants; @@ -44,16 +49,12 @@ import org.jclouds.rest.annotations.BuildVersion; import org.jclouds.vcloud.TaskStillRunningException; import org.jclouds.vcloud.VCloudApi; 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.*; import org.jclouds.vcloud.domain.NetworkConnectionSection.Builder; -import org.jclouds.vcloud.domain.Task; -import org.jclouds.vcloud.domain.VApp; -import org.jclouds.vcloud.domain.VAppTemplate; -import org.jclouds.vcloud.domain.Vm; +import org.jclouds.vcloud.domain.internal.VmImpl; import org.jclouds.vcloud.domain.network.IpAddressAllocationMode; import org.jclouds.vcloud.domain.network.NetworkConfig; +import org.jclouds.vcloud.domain.ovf.VCloudNetworkAdapter; import org.jclouds.vcloud.options.InstantiateVAppTemplateOptions; import com.google.common.annotations.VisibleForTesting; @@ -61,6 +62,7 @@ import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableSet; +import org.jclouds.vcloud.predicates.TaskSuccess; @Singleton public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn { @@ -132,7 +134,7 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA waitForTask(updateVmWithNameAndCustomizationScript(vm, name, vOptions.getCustomizationScript())); logger.trace("<< updated customization vm(%s)", name); - ensureVmHasAllocationModeOrPooled(vAppResponse, vOptions.getIpAddressAllocationMode()); + ensureVmHasDesiredNetworkConnectionSettings(vAppResponse, vOptions); int cpuCount = (int) getCores(template.getHardware()); logger.trace(">> updating cpuCount(%d) vm(%s)", cpuCount, vm.getName()); @@ -169,17 +171,85 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA throw new UnsupportedOperationException("we currently do not support multiple vms in a vAppTemplate " + vAppTemplate); - if (vAppTemplate.getNetworkSection().getNetworks().size() > 1) - throw new UnsupportedOperationException( - "we currently do not support multiple network connections in a vAppTemplate " + vAppTemplate); + VmImpl vmTemplate = VmImpl.class.cast(vAppTemplate.getChildren().iterator().next()); - Network networkToConnect = get(vAppTemplate.getNetworkSection().getNetworks(), 0); + String description = VCloudTemplateOptions.class.cast(template.getOptions()).getDescription(); + if (description == null) { + Map<String, String> md = metadataAndTagsAsCommaDelimitedValue(template.getOptions()); + description = Joiner.on('\n').withKeyValueSeparator("=").join(md); + } - - NetworkConfig config = networkConfigurationForNetworkAndOptions.apply(networkToConnect, vOptions); + InstantiateVAppTemplateOptions options = InstantiateVAppTemplateOptions.Builder.description(description); + + /* + * Match up networks in the vApp template with the ones in the options we got passed in, so that + * the right ones can be wired together. + * Note that in the end the order of the network interfaces is what's important, not the names. The names + * might not match up. In the worst case, maybe someone called their vApp networks A and B, but really wants + * them cross-connected, i.e. wired up to B and A respectively. + * The only potential issue here is that the networks in the vOptions are stored as a Set, which makes little + * sense. While the API is what it is, we need to rely on people higher up using sensible underlying + * datastructures (that conform to the set interface), which preserve their order. + */ + + int vmTemplateNumNetworks = vmTemplate.getNetworkConnectionSection().getConnections().size(); + + /* + * Backwards-compatibility hack: we might get passed in a parent network URI and an empty vOptions.networks list. + * In that case, move the parent network URI into the vOptions.networks list, and remove the parent URI. + */ + if (vOptions.getNetworks().size() == 0 && vmTemplateNumNetworks == 1 && vOptions.getParentNetwork() != null) { + ArrayList<String> netlist = new ArrayList<String>(); + netlist.add(vOptions.getParentNetwork().toASCIIString()); + vOptions.networks(netlist); + vOptions.parentNetwork(null); + } + + URI[] vOptionsNetworkIdList = new URI[vOptions.getNetworks().size()]; + NetworkConnection[] vAppTemplateNetworkList = + new NetworkConnection[vmTemplateNumNetworks]; + + //hopefully this preserves the original order, assuming the original underlying datastructure was ordered. + int i = 0; + for (String network: vOptions.getNetworks()) { + try { + vOptionsNetworkIdList[i] = new URI(network); + } catch (URISyntaxException e) { + logger.error(e, "Failed to convert href '" + network + "' to URI. We expect a href to a network to be " + + "passed in, not a name for example."); + return null; + } + i++; + } + + //iterate over the NetworkConnectionSection, and put them in order of their connection indices + // into the vAppTemplateNetworkList. + for (NetworkConnection netCon: vmTemplate.getNetworkConnectionSection().getConnections()) { + vAppTemplateNetworkList[netCon.getNetworkConnectionIndex()] = netCon; + } + + for (i = 0; i < vOptionsNetworkIdList.length; i++) { + URI parentNetwork = vOptionsNetworkIdList[i]; + NetworkConnection networkConnectionParams = vAppTemplateNetworkList.length > i ? vAppTemplateNetworkList[i] : null; + //hook 'em up. + + //use network name from vAppTemplate if possible + String networkName; + if (networkConnectionParams != null) { + networkName = networkConnectionParams.getNetwork(); + } else { + networkName = "jclouds-net-" + String.valueOf(i); + } - // note that in VCD 1.5, the network name after instantiation will be the same as the parent - InstantiateVAppTemplateOptions options = addNetworkConfig(config); + Network n = new Network(networkName, null); // ignore description, not needed here. + VCloudTemplateOptions networkTemplateOptions = vOptions.clone(); //we'll modify bits here + networkTemplateOptions.parentNetwork(parentNetwork); + + NetworkConfig config = networkConfigurationForNetworkAndOptions.apply(n, networkTemplateOptions); + // note that in VCD 1.5, the network name after instantiation will be the same as the parent + options.addNetworkConfig(config); + logger.debug("Connecting vApp network " + n.getName() + " to org network " + parentNetwork + "."); + } // TODO make disk size specifiable // disk((long) ((template.getHardware().getVolumes().get(0).getSize()) * @@ -187,13 +257,6 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA - String description = VCloudTemplateOptions.class.cast(template.getOptions()).getDescription(); - if (description == null) { - Map<String, String> md = metadataAndTagsAsCommaDelimitedValue(template.getOptions()); - description = Joiner.on('\n').withKeyValueSeparator("=").join(md); - } - - options.description(description); options.deploy(false); options.powerOn(false); @@ -245,37 +308,161 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA return client.getVmApi().updateGuestCustomizationOfVm(guestConfiguration, vm.getHref()); } - public void ensureVmHasAllocationModeOrPooled(VApp vApp, @Nullable IpAddressAllocationMode ipAllocationMode) { + public void ensureVmHasDesiredNetworkConnectionSettings(VApp vApp, VCloudTemplateOptions vOptions) { Network networkToConnect = find(vApp.getNetworkSection().getNetworks(), not(networkWithNoIpAllocation)); Vm vm = get(vApp.getChildren(), 0); - NetworkConnectionSection net = vm.getNetworkConnectionSection(); - checkArgument(net.getConnections().size() > 0, "no connections on vm %s", vm); - - NetworkConnection toConnect = findWithPoolAllocationOrFirst(net); + NetworkConnectionSection nets = vm.getNetworkConnectionSection(); + checkArgument(nets.getConnections().size() > 0, "no connections on vm %s", vm); + + /* + * Here we want to build the NetworkConnectionSection. + * This is not required if: + * - the user didn't pass in any vCloud specific NetworkConnection settings, and + * - there exist enough NetworkConnectionSections to cover the networks we need to + * wire up to the VM + * + * In case of modifying the existing network connection, its important that + * those optional parameters whose value is not being changed needs to be returned + * with the existing values or vcloud puts default values for them. When mac address is not + * modified then it needs to be returned else vclouds changes the adapter type of the NIC to E1000. + * + * There are a couple of things that might require changes: + * - different parameters (e.g. ip address allocation mode) + * - insufficient NetworkConnection items (we need to add more to trigger the + * creation of more NICs) + * - if the user didn't pass in any vCloud specific NetworkConnections, we might need to + * create new ones here. + * It's easier to just unconditionally rewrite the network connection section, + * than to write some bug-prone code that checks all of the above conditions. + */ + + /* + * We also need to add NICs. If we don't do this, we'll get new NICs, but they'll all be E1000s. + * We really want to replicate the type of any existing adapters. + * + * We add the NICs only when it is needed. In case of vapp template having vm with multiple NICs + * and if we update only the existing NICs then vcloud throws error that primary NIC not found. + */ + Set<? extends ResourceAllocationSettingData> allVirtualHWItems = vm.getVirtualHardwareSection().getItems(); + Iterable<VCloudNetworkAdapter> existingNics = Iterables.filter(allVirtualHWItems, VCloudNetworkAdapter.class); + // we want to program all existing nics. + ArrayList<VCloudNetworkAdapter> nicsToProgram = Lists.newArrayList(existingNics); + + //the first adapter type will be used as default for newly added ones. + String firstAdapterType = "E1000"; + int nextInstanceID = 1; + if (nicsToProgram.size() >= 1) { + firstAdapterType = nicsToProgram.get(0).getResourceSubType(); + nextInstanceID = 1 + Integer.valueOf(nicsToProgram.get(nicsToProgram.size() - 1).getInstanceID()); + } - if (ipAllocationMode == null) - ipAllocationMode = toConnect.getIpAddressAllocationMode(); + int i = 0; + LinkedHashSet<NetworkConnection> connectionsToProgram = new LinkedHashSet<NetworkConnection>(); + for (String netUuid: vOptions.getNetworks()) { + NetworkConnection desiredNC = vOptions.getNetworkConnections().get(netUuid); + String netName; + String macAddr; + NetworkConnection vappNC = findNetworkConnectionByIndexOrNull(nets, i); + if (vappNC != null && vappNC.getNetwork() != null) { + netName = vappNC.getNetwork(); + } else { + netName = null; + } - // make sure that we are in fact allocating ips - if (ipAllocationMode == IpAddressAllocationMode.NONE) - ipAllocationMode = IpAddressAllocationMode.POOL; + if (vappNC != null && vappNC.getMACAddress() != null) { + macAddr = vappNC.getMACAddress(); + } else { + macAddr = null; + } - if (toConnect.isConnected() && toConnect.getIpAddressAllocationMode() == ipAllocationMode - && toConnect.getNetwork().equals(networkToConnect.getName())) { - // then we don't need to change the network settings, and can save a call - } else { - Builder builder = net.toBuilder(); - builder.connections(ImmutableSet.of(toConnect.toBuilder().network(networkToConnect.getName()).connected(true) - .ipAddressAllocationMode(ipAllocationMode).build())); - logger.trace(">> updating networkConnection vm(%s)", vm.getName()); + if (desiredNC != null + && desiredNC.getNetworkConnectionIndex() != i) { + logger.error("Data consistency error: the network '" + netUuid + "'s connection index has been specified " + + "in the vCloud specific NetworkConnection settings in VCloudTemplateOptions.networkConnections, but " + + "the connection index does not match the " + + "position of the network in the TemplateOptions.networks object. Ignoring vCloud specific options for this net."); + desiredNC = null; + } + /* + Its not yet clear why the mac address in desiredNC is null. This needs to be explored. + This special handling is needed as the mac address is null and if it is not set, it results in NIC + type as E1000 + */ + if (desiredNC != null && macAddr != null && desiredNC.getMACAddress() == null){ + NetworkConnection.Builder desiredNCBuilder = desiredNC.toBuilder(); + desiredNCBuilder.MACAddress(macAddr); + desiredNC = desiredNCBuilder.build(); + } + if (desiredNC != null && desiredNC.getIpAddressAllocationMode() == null + || desiredNC.getIpAddressAllocationMode() == IpAddressAllocationMode.NONE) { + logger.error("Data consistency error: the network '" + netUuid + "'s IP address allocation mode" + + "has been set to 'none' or null in the vCloud specific NetworkConnection settings in VCloudTemplateOptions.networkConnections. " + + "This is invalid. Ignoring vCloud specific options for this net."); + desiredNC = null; + } - waitForTask(client.getVmApi().updateNetworkConnectionOfVm(builder.build(), vm.getHref())); - logger.trace("<< updated networkConnection vm(%s)", vm.getName()); + NetworkConnection ncToAdd = null; + if (desiredNC == null) { + // use default settings + ncToAdd = new NetworkConnection(netName, i, null, null, true, null, + IpAddressAllocationMode.POOL); + } else { + if (netName != null && !netName.equals(desiredNC.getNetwork())) { + //something's probably wrong. + logger.warn("vcloud overridden network name '" + desiredNC.getNetwork() + "' doesn't match the vApp's " + + " network with index " + i + " name '" + netName + "'"); + } + + if (netName == null && desiredNC.getNetwork() == null) { + //ok we need to come up with some network name. + netName = "jclouds-net-" + String.valueOf(i); + NetworkConnection.Builder ncBuilder = desiredNC.toBuilder(); + ncToAdd = ncBuilder.network(netName).connected(desiredNC.isConnected()) + .externalIpAddress(desiredNC.getExternalIpAddress()) + .ipAddress(desiredNC.getIpAddress()).ipAddressAllocationMode(desiredNC.getIpAddressAllocationMode()) + .MACAddress(desiredNC.getMACAddress()).networkConnectionIndex(desiredNC.getNetworkConnectionIndex()).build(); + } else { + ncToAdd = desiredNC; + } + } + connectionsToProgram.add(ncToAdd); + + //OK, we've now setup the network connection. Now we want to check if we need to add a new NIC for it. + if (nicsToProgram.size() < connectionsToProgram.size()) { + VCloudNetworkAdapter.Builder nicBuilder = VCloudNetworkAdapter.builder(); + //interesting values + nicBuilder.addressOnParent(String.valueOf(i)); + nicBuilder.automaticAllocation(true); + nicBuilder.connection(ncToAdd.getNetwork()); + nicBuilder.ipAddressingMode(ncToAdd.getIpAddressAllocationMode().toString()); + nicBuilder.elementName("Network adapter " + String.valueOf(i)); + nicBuilder.instanceID(String.valueOf(nextInstanceID)); + nextInstanceID += 1; + nicBuilder.resourceSubType(firstAdapterType); + nicBuilder.resourceType(ResourceAllocationSettingData.ResourceType.ETHERNET_ADAPTER); + + VCloudNetworkAdapter newNic = nicBuilder.build(); + nicsToProgram.add(newNic); + } + i++; + } + // Add new nics only if they are needed + if (nicsToProgram.size() < connectionsToProgram.size()) { + logger.debug("Programming NICs: %s", nicsToProgram); + Task t = client.getVmApi().updateNetworkCardsOfVm(nicsToProgram, vm.getHref()); + waitForTask(t); } + // update the NetworkConnectionSection. + Builder builder = nets.toBuilder(); + builder.connections(connectionsToProgram); + logger.trace(">> updating networkConnection vm(%s)", vm.getName()); + logger.debug("New NetworkConnectionSection for VM %s: %s", vm.getName(), builder.build().toString()); + waitForTask(client.getVmApi().updateNetworkConnectionOfVm(builder.build(), vm.getHref())); + logger.trace("<< updated networkConnection vm(%s)", vm.getName()); } private NetworkConnection findWithPoolAllocationOrFirst(NetworkConnectionSection net) { @@ -289,6 +476,17 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA }, get(net.getConnections(), 0)); } + private NetworkConnection findNetworkConnectionByIndexOrNull(NetworkConnectionSection net, final int index) { + return find(net.getConnections(), new Predicate<NetworkConnection>() { + + @Override + public boolean apply(NetworkConnection input) { + return input.getNetworkConnectionIndex() == index; + } + + }, null); + } + public Task updateCPUCountOfVm(Vm vm, int cpuCount) { return client.getVmApi().updateCPUCountOfVm(cpuCount, vm.getHref()); } http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/domain/DiskAttachOrDetachParams.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/domain/DiskAttachOrDetachParams.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/domain/DiskAttachOrDetachParams.java new file mode 100644 index 0000000..f3403d9 --- /dev/null +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/domain/DiskAttachOrDetachParams.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.vcloud.domain; + +import java.net.URI; + +/** + * The DiskAttachOrDetachParams element forms the body of a disk attach/detach request + * see http://pubs.vmware.com/vchs/index.jsp?topic=%2Fcom.vmware.vcloud.api.reference.doc_56%2Fdoc%2Ftypes%2FDiskAttachOrDetachParamsType.html + */ +public class DiskAttachOrDetachParams { + protected final URI href; + protected final String type; + protected final String id; + protected final String name; + protected final Integer BusNumber; + protected final Integer UnitNumber; + + /** + * "application/vnd.vmware.vcloud.disk+xml" + */ + //TODO: is this the right place? + public static final String DISK_XML = "application/vnd.vmware.vcloud.disk+xml"; + + public DiskAttachOrDetachParams(URI href) { + this.href = href; + this.type = DISK_XML; + this.id = null; + this.name = null; + this.BusNumber = null; + this.UnitNumber = null; + } + + public DiskAttachOrDetachParams(URI href, String name, + String id, Integer BusNumber, Integer UnitNumber) { + this.href = href; + this.type = DISK_XML; + this.id = id; + this.name = name; + this.BusNumber = BusNumber; + this.UnitNumber = UnitNumber; + } + + /** + * get href + * @return href + */ + public URI getHref() { + return href; + } + + /** + * get type + * @return type + */ + public String getType() { + return type; + } + + /** + * get id + * @return id + */ + public String getId() { + return id; + } + + /** + * get name + * @return name + */ + public String getName() { + return name; + } + + /** + * get bus nr + * @return bus nr + */ + public Integer getBusNumber() { + return BusNumber; + } + + /** + * get unit number + * @return unit number + */ + public Integer getUnitNumber() { + return UnitNumber; + } + + @Override + public String toString() { + return "[href=" + getHref() + ", type=" + getType() + ", id=" + getId() + ", name=" + getName() + + ", BusNumber=" + getBusNumber() + ", UnitNumber=" + getUnitNumber() + "]"; + } + + @Override + public int hashCode() { + int prime = 31; + int result = 1; + result = prime * result + ((href == null) ? 0 : href.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((BusNumber == null) ? 0 : BusNumber.hashCode()); + result = prime * result + ((UnitNumber == null) ? 0 : UnitNumber.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DiskAttachOrDetachParams other = (DiskAttachOrDetachParams)obj; + + if (href == null) { + if (other.href != null) + return false; + } else if (!href.equals(other.href)) { + return false; + } + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) { + return false; + } + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) { + return false; + } + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) { + return false; + } + if (BusNumber == null) { + if (other.BusNumber != null) + return false; + } else if (!BusNumber.equals(other.BusNumber)) { + return false; + } + if (UnitNumber == null) { + if (other.UnitNumber != null) + return false; + } else if (!UnitNumber.equals(other.UnitNumber)) { + return false; + } + return true; + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/features/VmApi.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/features/VmApi.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/features/VmApi.java index 8aa186e..40b7e43 100644 --- a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/features/VmApi.java +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/features/VmApi.java @@ -20,6 +20,7 @@ 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.RASDITEM_XML; +import static org.jclouds.vcloud.VCloudMediaType.RASDITEMLIST_XML; import static org.jclouds.vcloud.VCloudMediaType.TASK_XML; import static org.jclouds.vcloud.VCloudMediaType.UNDEPLOYVAPPPARAMS_XML; import static org.jclouds.vcloud.VCloudMediaType.VM_XML; @@ -35,23 +36,10 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import org.jclouds.Fallbacks; -import org.jclouds.rest.annotations.BinderParam; -import org.jclouds.rest.annotations.EndpointParam; -import org.jclouds.rest.annotations.Fallback; -import org.jclouds.rest.annotations.MapBinder; -import org.jclouds.rest.annotations.PayloadParams; -import org.jclouds.rest.annotations.RequestFilters; -import org.jclouds.rest.annotations.XMLResponseParser; -import org.jclouds.vcloud.binders.BindCPUCountToXmlPayload; -import org.jclouds.vcloud.binders.BindDeployVAppParamsToXmlPayload; -import org.jclouds.vcloud.binders.BindGuestCustomizationSectionToXmlPayload; -import org.jclouds.vcloud.binders.BindMemoryToXmlPayload; -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.Task; -import org.jclouds.vcloud.domain.Vm; +import org.jclouds.rest.annotations.*; +import org.jclouds.vcloud.binders.*; +import org.jclouds.vcloud.domain.*; +import org.jclouds.vcloud.domain.ovf.VCloudNetworkAdapter; import org.jclouds.vcloud.filters.AddVCloudAuthorizationAndCookieToRequest; import org.jclouds.vcloud.xml.TaskHandler; import org.jclouds.vcloud.xml.VmHandler; @@ -60,6 +48,8 @@ import org.jclouds.vcloud.xml.VmHandler; /** * Provides access to VM functionality in vCloud * <p/> + * + * @author Adrian Cole */ @RequestFilters(AddVCloudAuthorizationAndCookieToRequest.class) public interface VmApi { @@ -161,6 +151,32 @@ public interface VmApi { @XMLResponseParser(TaskHandler.class) Task powerOffVm(@EndpointParam URI href); + /** + * Detach disk + */ + @POST + @Consumes(TASK_XML) + @Produces(DiskAttachOrDetachParams.DISK_XML) + @Path("/disk/action/detach") + @MapBinder(BindDiskAttachOrDetachParamsToXmlPayload.class) + @XMLResponseParser(TaskHandler.class) + Task detachDisk( + @EndpointParam URI href, + @PayloadParam("params") DiskAttachOrDetachParams diskAttachOrDetachParams); + + /** + * Attach disk + */ + @POST + @Consumes(TASK_XML) + @Produces("application/vnd.vmware.vcloud.diskAttachOrDetachParams+xml") + @Path("/disk/action/attach") + @MapBinder(BindDiskAttachOrDetachParamsToXmlPayload.class) + @XMLResponseParser(TaskHandler.class) + Task attachDisk( + @EndpointParam URI href, + @PayloadParam("params") DiskAttachOrDetachParams diskAttachOrDetachParams); + /** * A shutdown request to a vApp URL shuts down all of the virtual machines in the vApp, as * specified in its StartupSection field. @@ -290,4 +306,21 @@ public interface VmApi { @XMLResponseParser(TaskHandler.class) Task updateMemoryMBOfVm(@BinderParam(BindMemoryToXmlPayload.class) int memoryInMB, @EndpointParam URI href); + + /** + * update the networkCards of an existing VM + * + * @param href + * to update + * @param networkCards + * new list of Network Interface Cards (NICs) + */ + @PUT + @Consumes(TASK_XML) + @Produces(RASDITEMLIST_XML) + @Path("/virtualHardwareSection/networkCards") + @MapBinder(BindVCloudNetworkAdapterToXmlPayload.class) + @XMLResponseParser(TaskHandler.class) + Task updateNetworkCardsOfVm(@PayloadParam("params") Iterable<VCloudNetworkAdapter> networkCards, + @EndpointParam URI href); } http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java index 521f086..bd0a4d1 100644 --- a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/main/java/org/jclouds/vcloud/xml/OrgNetworkHandler.java @@ -251,7 +251,7 @@ public class OrgNetworkHandler extends ParseSax.HandlerWithResult<OrgNetwork> { natType = NatType.fromValue(currentOrNull()); } else if (SaxUtils.equalsOrSuffix(qName, "MappingMode")) { mappingMode = MappingMode.fromValue(currentOrNull()); - } else if (qName.equalsIgnoreCase("ExternalIP")) { + } else if (qName.equalsIgnoreCase("ExternalIPAddress")) { externalIP = currentOrNull(); } else if (qName.equalsIgnoreCase("VAppScopedVmId")) { vAppScopedVmId = currentOrNull(); @@ -267,7 +267,7 @@ public class OrgNetworkHandler extends ParseSax.HandlerWithResult<OrgNetwork> { this.vmNicId = -1; } else if (qName.equalsIgnoreCase("ExternalPort")) { externalPort = Integer.parseInt(currentOrNull()); - } else if (qName.equalsIgnoreCase("InternalIP")) { + } else if (qName.equalsIgnoreCase("InternalIPAddress")) { internalIP = currentOrNull(); } else if (qName.equalsIgnoreCase("InternalPort")) { internalPort = Integer.parseInt(currentOrNull()); http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java index 4677835..f43a0e9 100644 --- a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/options/VCloudTemplateOptionsTest.java @@ -20,15 +20,17 @@ 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.description; import static org.jclouds.vcloud.compute.options.VCloudTemplateOptions.Builder.inboundPorts; -import static org.jclouds.vcloud.compute.options.VCloudTemplateOptions.Builder.ipAddressAllocationMode; import static org.jclouds.vcloud.compute.options.VCloudTemplateOptions.Builder.parentNetwork; import static org.testng.Assert.assertEquals; import java.io.IOException; import java.net.URI; +import java.util.Hashtable; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.vcloud.domain.NetworkConnection; import org.jclouds.vcloud.domain.network.IpAddressAllocationMode; +import org.jclouds.vcloud.endpoints.Network; import org.testng.annotations.Test; /** @@ -37,16 +39,14 @@ import org.testng.annotations.Test; */ public class VCloudTemplateOptionsTest { @Test - public void testipAddressAllocationMode() { + public void testnetworkConnections() { 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); + String netUuid = "https://myfunvcloud.com/api/admin/network/aaaabbbb-cccc-1122-3344-1234567890ab"; + Hashtable<String, NetworkConnection> nets = new Hashtable<String, NetworkConnection>(1); + NetworkConnection nc = new NetworkConnection(netUuid, 0, null, null, true, null, IpAddressAllocationMode.POOL); + nets.put(netUuid, nc); + options.networkConnections(nets); + assertEquals(options.getNetworkConnections().get(netUuid), nc); } public void testAs() { http://git-wip-us.apache.org/repos/asf/stratos/blob/ed952ac3/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOnExpectTest.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOnExpectTest.java b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOnExpectTest.java index 2608021..2050e3f 100644 --- a/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOnExpectTest.java +++ b/dependencies/jclouds/apis/vcloud/1.8.1-stratos/src/test/java/org/jclouds/vcloud/compute/strategy/InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOnExpectTest.java @@ -113,11 +113,11 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA public void testInstantiateVAppFromTemplateWhenUsingOverriddenNetworkAndFenceMode() throws Exception { - + String name = "group-abcd"; FenceMode fenceMode = FenceMode.NAT_ROUTED; URI parentNetwork = URI.create(ENDPOINT + "/v1.0/network/" + "foooooooo"); - + String instantiateXML = XMLBuilder.create("InstantiateVAppTemplateParams") .a("xmlns", ns).a("xmlns:ovf", "http://schemas.dmtf.org/ovf/envelope/1") .a("deploy", "false").a("name", name).a("powerOn", "false") @@ -136,14 +136,14 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA .e("Source").a("href", ENDPOINT + "/v1.0/vAppTemplate/" + templateId).up() .e("AllEULAsAccepted").t("true").up() .asString(outputProperties); - + HttpRequest version1_0InstantiateWithCustomizedNetwork = HttpRequest.builder().method("POST") .endpoint(ENDPOINT + "/v1.0/vdc/" + vdcId + "/action/instantiateVAppTemplate") .addHeader(HttpHeaders.ACCEPT, "application/vnd.vmware.vcloud.vApp+xml;version=1.0") .addHeader("x-vcloud-authorization", sessionToken) .addHeader(HttpHeaders.COOKIE, "vcloud-token=" + sessionToken) .payload(payloadFromStringWithContentType(instantiateXML, "application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml")).build(); - + ComputeService compute = requestsSendResponses(ImmutableMap.<HttpRequest, HttpResponse> builder() .put(versionsRequest, versionsResponseFromVCD1_5) .put(version1_0LoginRequest, successfulVersion1_0LoginResponseFromVCD1_5WithSingleOrg) @@ -154,13 +154,15 @@ public class InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployA .put(version1_0GetVAppTemplateRequest, successfulVersion1_0GetVAppTemplateResponseFromVCD1_5WithSingleVMAndVDCParent) .put(version1_0GetOVFForVAppTemplateRequest, successfulVersion1_0GetOVFForVAppTemplateResponseFromVCD1_5WithSingleVM) .put(version1_0InstantiateWithCustomizedNetwork, successfulVersion1_0InstantiatedVApp).build()); - + InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn starter = compute.getContext() .utils().injector().getInstance( InstantiateVAppTemplateWithGroupEncodedIntoNameThenCustomizeDeployAndPowerOn.class); Template template = compute.templateBuilder().build(); - template.getOptions().as(VCloudTemplateOptions.class).parentNetwork(parentNetwork).fenceMode(fenceMode); + String[] netlist = new String[1]; + netlist[0] = parentNetwork.toASCIIString(); + template.getOptions().as(VCloudTemplateOptions.class).fenceMode(fenceMode).networks(netlist); starter.instantiateVAppFromTemplate(name, template); }
