com.vmware.vchs.publicapi.samples.VMCreateSample.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.vchs.publicapi.samples.VMCreateSample.java

Source

/*
 * Copyright (c) 2013 VMware, Inc. All Rights Reserved.
 *
 * 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 com.vmware.vchs.publicapi.samples;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;

import com.vmware.vcloud.api.rest.schema.AvailableNetworksType;
import com.vmware.vcloud.api.rest.schema.DeployVAppParamsType;
import com.vmware.vcloud.api.rest.schema.InstantiateVAppTemplateParamsType;
import com.vmware.vcloud.api.rest.schema.InstantiationParamsType;
import com.vmware.vcloud.api.rest.schema.LinkType;
import com.vmware.vcloud.api.rest.schema.NetworkConfigSectionType;
import com.vmware.vcloud.api.rest.schema.NetworkConfigurationType;
import com.vmware.vcloud.api.rest.schema.NetworkConnectionSectionType;
import com.vmware.vcloud.api.rest.schema.ObjectFactory;
import com.vmware.vcloud.api.rest.schema.OrgVdcNetworkType;
import com.vmware.vcloud.api.rest.schema.QueryResultRecordType;
import com.vmware.vcloud.api.rest.schema.QueryResultRecordsType;
import com.vmware.vcloud.api.rest.schema.QueryResultVAppTemplateRecordType;
import com.vmware.vcloud.api.rest.schema.ReferenceType;
import com.vmware.vcloud.api.rest.schema.TaskType;
import com.vmware.vcloud.api.rest.schema.TasksInProgressType;
import com.vmware.vcloud.api.rest.schema.VAppNetworkConfigurationType;
import com.vmware.vcloud.api.rest.schema.VAppTemplateType;
import com.vmware.vcloud.api.rest.schema.VAppType;
import com.vmware.vcloud.api.rest.schema.VdcType;
import com.vmware.vcloud.api.rest.schema.VmType;
import com.vmware.vcloud.api.rest.schema.ovf.CimBoolean;
import com.vmware.vcloud.api.rest.schema.ovf.CimString;
import com.vmware.vcloud.api.rest.schema.ovf.MsgType;
import com.vmware.vcloud.api.rest.schema.ovf.RASDType;
import com.vmware.vcloud.api.rest.schema.ovf.SectionType;
import com.vmware.vcloud.api.rest.schema.ovf.VirtualHardwareSectionType;

/**
 * VMCreateSample
 * 
 * This sample will create a VM from a vApp template in the specified VDC, power on
 * the vApp, and print out the IPs for each VM in the vApp. A vCHS vApp template name is used to
 * specify the vApp to deploy. For example one of the templates in the vCHS System Catalog can be
 * used. This vApp template name is specified by the command line option -vchstemplatename. The name of
 * the deployed vApp is specified by the -targetvappname. The command line option -vdcname is used
 * to specify the VDC where the vApp will be deployed. The deployed vApp will be connected to the
 * network specified by the -orgnet option.
 *
 * Templates typically do not have any network configuration. Because of this, the process to
 * connect a VM (which essentially duplicates the settings of the template it is created from) to
 * the -orgnet network requires a couple of extra steps. The vApp is first created, which also
 * creates any Vms the template defines. The network configuration updates must be done while the 
 * vApp (and subsequently Vm's) are in a powered off state. After the vApp creation task is 
 * complete, the vApp network configuration will be updated. It is possible to provide the network 
 * configuration details during the create step, but because each child Vm may initially derive 
 * it's network configuration details from the template it's created from, it's possible the 
 * template network details do not match the details provided during the vApp create. Therefore the 
 * vApp is created without network details initially to avoid each Vm from failing to be created 
 * due to different network details then the vApp.
 * 
 * Once the vApp (and Vm's) are created, the vApp network details must be updated before any of the
 * Vm children can be modified. After the vApp network details have been updated, each child
 * Vm of the vApp will need it's network details updated to match that of the vApp so that the Vm
 * network can connect to the vApp network properly. This is why each Vm's network details are
 * modified too.
 * 
 * Steps: 
 *   1) Log in to vCHS
 *   2) Get the list of the compute services
 *   3) Find the VDC within the compute services
 *   4) Get the VDC's session link from vCHS
 *   5) Get a vCloud session for the VDC
 *   6) Get the vApp template from vCloud (using query service)
 *   7) Get the vApp instantiate link from the VDC
 *   8) Initialize the vApp on the VDC
 *   9) Get the vApp information to pull the VM children from
 *  10) Put the network details to the vApp with the network name specified with the command line
 *      option -orgnet
 *  11) Put the network information to each child VM with the same network details as the vApp
 *      to connect each Vm to the vApp network
 *  11) Deploy the vApp (which will also turn on each child Vm)
 *  12) Wait for the vApp to be powered on
 *  13) Once the vApp is ready, retrieve each child Vm's IP information and display it
 *  
 * Parameters:
 *   url [required] : url of the vCHS web service. 
 *   username [required] : username for the vCHS authentication.
 *   password [required] : password for the vCHS authentication.
 *   vchsversion [required] : version of vCHS API.
 *   vcloudversion [required] : version of vCloud API.
 *   vdcname [required] : name of the VDC.
 *   targetvappname [required] : name of the vApp to be created.
 *   orgnet [required] : name of the org network
 *   templatename [required] : name of the template to create the VM from
 *  
 * Argument Line: Creates and turns on a VM using the vApp template on a particular VDC and
 * displays its internal ip it is assigned.
 * 
 * --url [vchs webservice url] --username [vchs username] --password [vchs password]
 * --vchsversion [vchs version] --targetvappname [vapp name] --vdcname [vdc name]
 * --vcloudversion [vcloud version] --orgnet [org network name] --vchstemplatename [template name]
 */
public class VMCreateSample {
    private static final String VM_NETWORK_URL = "/networkConnectionSection/";
    private static final int SUCCESS = 4;
    private static final int FAIL = -1;

    private Vchs vchs = null;
    private Vcd vcd = null;
    private VMCreateCommandLineOptions options = null;

    /**
     * @param args
     *            any arguments passed by the command line, if none, defaults are used where
     *            applicable.
     */
    public static void main(String[] args) {
        VMCreateSample sample = new VMCreateSample();
        sample.run(args);
    }

    /**
     * Called by the static main method on the instance of this class with the command line args
     * array.
     * 
     * @param args
     *            the arguments passed on the command line
     */
    private void run(String[] args) {
        // Create instance of CommandLineOptions for use in this sample
        options = new VMCreateCommandLineOptions();
        options.parseOptions(args);

        System.out.print("\nConnecting to vCHS...");

        // Instance of Vchs for this sample
        vchs = new Vchs();

        // Log in to vCHS, passing the command line arguments
        if (vchs.login(options)) {
            System.out.println("Success\n");

            // Retrieve a vcloudSession for the VDC specified on the command line.
            // options.vdcName contains the name of a VDC corresponding to the retrieved vCloud
            // session
            String vcdSessionHref = vchs.getVCloudDSessionHref(options);

            // Retrieve the vCloud API EndPoint for the VDC.
            vcd = HttpUtils.getVCDEndPoint(vchs, options, vcdSessionHref);

            // Retrieve the VCD hostname from the VDC href
            String vcdBaseUrl = HttpUtils.getHostname(vcd.vdcHref);

            // Retrieve the VDC object from vCloud
            VdcType vdc = getVdc(vcd.vdcHref);

            // Retrieve the VAppTemplateType that matches the command line arg --templatename
            // passed in
            VAppTemplateType template = getVAppTemplate(vcdBaseUrl);

            // Retrieve the url to perform initializevApp method
            String instantiateHref = getInstantiateVAppLink(vdc);

            // Initialize the vApp template using the vApp template
            VAppType vApp = createVApp(instantiateHref, template.getHref(), vdc);

            // Wait for the vApp creation to complete by monitoring the vApp task
            System.out.print("Waiting for vApp creation to finish...");
            waitForTasks(vApp.getTasks());
            System.out.println("Success\n");

            // After it's completed and it's task is finished, GET the vApp again to retrieve it's
            // updated state, which will add the VM as a child and more links among other things.
            System.out.print("Refreshing vApp state...");
            vApp = getVApp(vApp);
            System.out.println("Success\n");

            // Find the Vm that was created as part of the vApp
            System.out.print("Looking for vApp child Vm...");
            VmType vm = getVmFromVApp(vApp);
            System.out.println("Found\n");

            // Update the ovf:VirtualHardwareSection of the Vm to connect it to
            // the vApp network. Use the Vdc available networks to find the matching network,
            // and if found, retrieve the network details
            System.out.print("Looking up vApp VDC network details...");
            OrgVdcNetworkType network = getVAppVdcNetwork(vcdBaseUrl, vdc);
            System.out.println("Found\n");

            // Update the vApp NetworkConfigSection to use the options.networkName
            // This is to cover the scenario where a template used to create a vApp already
            // has a network associated with it. Templates will often not have a network
            // associated with the template, unless it is provisioned for a specific VDC which
            // already has an established network.
            // NOTE: The vApp must be updated first, then it's children Vms.

            // Update the vApp with same network details as the Vms
            System.out.print("Updating the vApp network...");
            TaskType vAppNetworkUpdateTask = updateVAppNetwork(vApp, vdc);
            waitForTaskCompletion(vAppNetworkUpdateTask);
            System.out.println("Success\n");

            // Update the ovf:VirtualHardwareSeciont with the network details for each Vm child
            // of the vApp, matching the network name using the command line options.networkName
            System.out.print("Updating vApp Vm network...");
            TaskType networkUpdateTask = updateVMWithNetworkDetails(vm);
            waitForTaskCompletion(networkUpdateTask);
            System.out.println("Success\n");

            // Now we need to power on the vApp, and wait on it powering on
            System.out.print("Deploying and powering on vApp...");
            TaskType deployTask = deploy(vApp);
            waitForTaskCompletion(deployTask);
            System.out.println("Success\n");

            // Get the IP info for the deployed vApp
            System.out.println("Displaying vApp " + vApp.getName() + " IP details:\n");
            displayIPDetails(vApp);
            System.out.println("Done");
        }
    }

    /**
     * This method will update the passed in vApp network by adding an additional
     * NetworkConfigSection that uses the command line options.networkName. It makes a PUT call
     * to the edit link of the NetworkConfigSection section of the vApp passed in. The
     * passed in VdcType is used to look up the parent network which is required for the update
     * process of the vApp NetworkConfigSection.
     * 
     * NOTE: This step, updating the vApp network section, must be done BEFORE any of the vApp's
     * children Vm's network sections are updated.
     * 
     * @param vApp the vApp instance to update network configuration for
     * @param vdc the VDC this vApp is deployed to
     * @return a TaskType instance if successful, otherwise an exception is thrown
     */
    private TaskType updateVAppNetwork(VAppType vApp, VdcType vdc) {
        List<JAXBElement<? extends SectionType>> sections = vApp.getSection();
        for (JAXBElement<? extends SectionType> section : sections) {
            if (section.getName().toString().contains("NetworkConfigSection")) {
                NetworkConfigSectionType ncst = (NetworkConfigSectionType) section.getValue();
                // Find the EDIT link to make a PUT call to to update the network config info
                List<LinkType> links = ncst.getLink();
                String editHref = null;
                for (LinkType link : links) {
                    if (link.getRel().equalsIgnoreCase("edit")) {
                        editHref = link.getHref();
                        break;
                    }
                }

                if (null != editHref) {
                    VAppNetworkConfigurationType vappNet = new VAppNetworkConfigurationType();

                    // Use the network name passed on the command line, the same name that will
                    // be applied to each vApp Vm child network configuration so they match
                    vappNet.setNetworkName(options.networkName);

                    // Newly constructed network configuration
                    NetworkConfigurationType networkConfiguration = new NetworkConfigurationType();
                    ReferenceType networkReference = new ReferenceType();

                    // Get the parent network that the VDC refers to
                    networkReference.setHref(getParentNetworkHrefFromVdc(vdc));
                    networkConfiguration.setParentNetwork(networkReference);

                    // hard coded.. 'bridged', 'natRouted' or 'isolated'
                    networkConfiguration.setFenceMode("bridged");
                    vappNet.setConfiguration(networkConfiguration);

                    // Add the newly configured network to the existing vApp configuration
                    ncst.getNetworkConfig().add(vappNet);

                    // Make the PUT call to update the vApp network configuration
                    HttpPut updateVAppNetwork = vcd.put(editHref, options);
                    OutputStream os = null;
                    ObjectFactory objectFactory = new ObjectFactory();
                    JAXBElement<NetworkConfigSectionType> networkConfigSectionType = objectFactory
                            .createNetworkConfigSection(ncst);

                    JAXBContext jaxbContexts = null;
                    try {
                        jaxbContexts = JAXBContext.newInstance(NetworkConfigSectionType.class);
                    } catch (JAXBException ex) {
                        throw new RuntimeException("Problem creating JAXB Context: ", ex);
                    }

                    try {
                        javax.xml.bind.Marshaller marshaller = jaxbContexts.createMarshaller();
                        marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                        os = new ByteArrayOutputStream();
                        // Marshal the object via JAXB to XML
                        marshaller.marshal(networkConfigSectionType, os);
                    } catch (JAXBException e) {
                        throw new RuntimeException("Problem marshalling VirtualHardwareSection", e);
                    }

                    // Set the Content-Type header for NetworkConfigSection
                    ContentType contentType = ContentType
                            .create("application/vnd.vmware.vcloud.networkConfigSection+xml", "ISO-8859-1");
                    StringEntity update = new StringEntity(os.toString(), contentType);
                    updateVAppNetwork.setEntity(update);

                    // Invoke the HttoPut to update the VirtualHardwareSection of the Vm
                    HttpResponse response = HttpUtils.httpInvoke(updateVAppNetwork);

                    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED) {
                        TaskType taskType = HttpUtils.unmarshal(response.getEntity(), TaskType.class);
                        return taskType;
                    }
                }
            }
        }

        throw new RuntimeException("Problem trying to update vApp " + vApp.getName()
                + " network config section with " + options.networkName + " as the network name");
    }

    /**
     * This will use the passed in vm  to find the VirtualHardwareSection and modify it to attach
     * the Vm network to the vApp network the Vm is a child of. The first step is to search the
     * Vm section types for VirtualHardwareSection. If found, look in the attributes of the section
     * to find the VirtualHardwareSection Href value. The VirtualHardwareSection found in the Vm
     * has a number of extra LinkType links that can not be part of the request body when the PUT
     * call to update the network settings is made. Therefore, the Href is used to first GET the
     * VirtualHardwareSection again which responds without the links so it can be used to PUT back
     * to the same Href to update the Vm network settings. With the newly acquired 
     * VirtualHardwareSection, a search through the items is done to find the network item. This
     * is denoted by a resource type value of "10". If found the command line options.networkName
     * is set as the value of the connection, and the value POOL is assigned to the ipAddressingMode
     * attribute. With those values set, the updated VirtualHardwareSection is then sent via a PUT
     * request to change the Vm network settings.
     * 
     * @param vm the Vm to change network settings on
     */
    private TaskType updateVMWithNetworkDetails(VmType vm) {
        // the Href to use for GET and PUT calls for the VirtualHardwareSection
        String hardwareHref = null;
        // With the VDC network details, we can now update the Vm network section to connect the Vm to the vApp network
        for (JAXBElement<? extends SectionType> st : vm.getSection()) {
            if (st.getName().toString().contains("VirtualHardwareSection")) {
                VirtualHardwareSectionType hardware = (VirtualHardwareSectionType) st.getValue();

                // Try to find the Href attribute which is used for GET and PUT
                Map<QName, String> map = hardware.getOtherAttributes();
                Set<QName> keys = map.keySet();
                for (QName key : keys) {
                    if (key.toString().endsWith("href")) {
                        hardwareHref = map.get(key);
                        break;
                    }
                }

                // Make sure VirtualHardwareSection href was found. This is used for the GET below
                // and PUT to later update the network settings of the Vm.
                if (null != hardwareHref) {
                    // It's necessary to GET the VirtualHardwareSection again because the current
                    // hardware variables from the Vm contains links that can not be sent as part
                    // of the PUT body. This GET call will only get the details without the links
                    // that the Vm section provides.
                    HttpResponse response = HttpUtils.httpInvoke(vcd.get(hardwareHref, options));
                    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                        hardware = HttpUtils.unmarshal(response.getEntity(), VirtualHardwareSectionType.class);

                        // Find the RASDType that has a ResourceType of 10, which indicates the
                        // VMs network info
                        for (RASDType rasType : hardware.getItem()) {
                            if (rasType.getResourceType().getValue().equals("10")) {
                                CimBoolean c = new CimBoolean();
                                c.setValue(true);

                                rasType.setAutomaticAllocation(c);
                                // Get the first CimString
                                CimString cs = rasType.getConnection().get(0);

                                // Set the network name
                                cs.setValue(options.networkName);

                                // Look in the list of attributes for the ip addressing mode
                                map = cs.getOtherAttributes();
                                keys = map.keySet();
                                for (QName key : keys) {
                                    if (key.toString().endsWith("ipAddressingMode")) {
                                        // Set it to POOL
                                        map.put(key, "POOL");
                                        break;
                                    }
                                }

                                break;
                            }
                        }
                    }

                    // Now do a PUT with update data
                    com.vmware.vcloud.api.rest.schema.ovf.ObjectFactory objectFactory = new com.vmware.vcloud.api.rest.schema.ovf.ObjectFactory();
                    JAXBElement<VirtualHardwareSectionType> hardwareSection = objectFactory
                            .createVirtualHardwareSection(hardware);

                    JAXBContext jaxbContexts = null;
                    try {
                        jaxbContexts = JAXBContext.newInstance(VirtualHardwareSectionType.class);
                    } catch (JAXBException ex) {
                        throw new RuntimeException("Problem creating JAXB Context: ", ex);
                    }

                    // Create HttpPut request to update the VirtualHardwareSection
                    HttpPut updateVmNetwork = vcd.put(hardwareHref, options);
                    OutputStream os = null;

                    try {
                        javax.xml.bind.Marshaller marshaller = jaxbContexts.createMarshaller();
                        marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                        os = new ByteArrayOutputStream();
                        // Marshal the object via JAXB to XML
                        marshaller.marshal(hardwareSection, os);
                    } catch (JAXBException e) {
                        throw new RuntimeException("Problem marshalling VirtualHardwareSection", e);
                    }

                    // Set the Content-Type header for VirtualHardwareSection
                    ContentType contentType = ContentType
                            .create("application/vnd.vmware.vcloud.virtualHardwareSection+xml", "ISO-8859-1");
                    StringEntity update = new StringEntity(os.toString(), contentType);
                    updateVmNetwork.setEntity(update);

                    // Invoke the HttoPut to update the VirtualHardwareSection of the Vm
                    response = HttpUtils.httpInvoke(updateVmNetwork);

                    if (response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED) {
                        // Update was good, return the TaskType
                        TaskType taskType = HttpUtils.unmarshal(response.getEntity(), TaskType.class);
                        return taskType;
                    }

                    break;
                }
            }
        }

        throw new RuntimeException("Could not update Vm VirtualHardwareSection");
    }

    /**
     * Uses the passed in VdcType to search the available networks of the Vdc for a matching
     * network with the options.networkName command line argument. If one is found, a GET
     * request is made to retrieve the network details and returned.
     * 
     * @param vcdBaseUrl the vcd href
     * @param vdc the Vdc containing the available networks to search
     * @return the matched OrgVdcNetworkType instance
     */
    private OrgVdcNetworkType getVAppVdcNetwork(String vcdBaseUrl, VdcType vdc) {
        AvailableNetworksType l = vdc.getAvailableNetworks();
        List<ReferenceType> networks = l.getNetwork();

        for (ReferenceType rt : networks) {
            if (rt.getName().equalsIgnoreCase(options.networkName)) {
                HttpResponse response = HttpUtils.httpInvoke(vcd.get(rt.getHref(), options));

                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                    OrgVdcNetworkType network = HttpUtils.unmarshal(response.getEntity(), OrgVdcNetworkType.class);
                    return network;
                }
            }
        }

        throw new RuntimeException("Could not find a matching Org network for the Vdc");
    }

    /**
     * Searches through the passed in vApp children element for a VmType Vm that matches the name
     * of the command line options.vappName. If found it is returned, otherwise a RuntimeException
     * is thrown. 
     * 
     * @param vApp the vApp to search for a matching Vm
     * @return the VmType instance
     */
    private VmType getVmFromVApp(VAppType vApp) {
        // Get the status of initialization operation and IP details
        if (null != vApp.getChildren()) {
            List<VmType> vms = vApp.getChildren().getVm();

            for (VmType vm : vms) {
                List<LinkType> links = vm.getLink();

                for (LinkType link : links) {
                    // If there is a rel="up", we use that to get the ID and match it to the
                    // passed in vApp id
                    if (link.getRel().equalsIgnoreCase("up")) {
                        // make GET request to get the up vApp to compare it to the
                        // passed in vApp
                        HttpResponse response = HttpUtils.httpInvoke(vcd.get(vApp.getHref(), options));
                        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                            VAppType upVApp = HttpUtils.unmarshal(response.getEntity(), VAppType.class);
                            if (upVApp.getName().equalsIgnoreCase(options.vappName)) {
                                return vm;
                            }
                        }
                    }
                }

                if (vm.getName().equalsIgnoreCase(options.vappName)) {
                    return vm;
                }
            }
        }

        throw new RuntimeException("Could not find a matching Vm in vApp " + vApp.getName());
    }

    /**
     * Uses the passed in vAppType to make a GET request to the vCloud API to retrieve an updated
     * copy of itself. This is typically done after a task completes on the vApp which may result
     * in a state change of the vApp, such as creating a vApp.
     * 
     * @param vApp the vApp to refresh from the API
     * @return a new instance of VAppType
     */
    private VAppType getVApp(VAppType vApp) {
        HttpResponse response = HttpUtils.httpInvoke(vcd.get(vApp.getHref(), options));
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            VAppType updatedVApp = HttpUtils.unmarshal(response.getEntity(), VAppType.class);
            return updatedVApp;
        }

        throw new RuntimeException(vApp.getName() + " with Href " + vApp.getHref() + " are invalid.");
    }

    /**
     * This will retrieve the VdcType from vCloud via the provided href
     * 
     * @param href
     *            the vCloud href
     * @return a VdcType instance
     */
    private VdcType getVdc(String href) {
        // Request the VDC details using the vCloud API End point for the VDC
        HttpResponse response = HttpUtils.httpInvoke(vcd.get(href, options));

        // Make sure response status is 200
        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            throw new RuntimeException("\nFailed : HTTP error code : " + response.getStatusLine().getStatusCode());
        }

        VdcType vdc = HttpUtils.unmarshal(response.getEntity(), VdcType.class);

        if (null == vdc) {
            throw new RuntimeException("Problem retreiving the VdcType from: " + href);
        }

        return vdc;
    }

    /**
     * This method will search through the VdcType instance passed in to find the network that
     * matches the command line argument provided --orgnetwork. This is set on the VM to be created
     * as the parent network.
     * 
     * @param vdc
     *            the VdcType instance to find the network within.
     */
    private String getParentNetworkHrefFromVdc(VdcType vdc) {
        AvailableNetworksType networks = vdc.getAvailableNetworks();
        List<ReferenceType> refs = networks.getNetwork();
        String parentNetworkHref = null;

        for (ReferenceType ref : refs) {
            // For each ReferenceType, check if it is the same name as the options.networkName
            // (command line option --networkname)
            if (ref.getName().equalsIgnoreCase(options.networkName)) {
                // Found it, break from loop
                parentNetworkHref = ref.getHref();
                break;
            }
        }

        if (null == parentNetworkHref) {
            throw new RuntimeException("Could not find parent network for Vdc: " + vdc.getName());
        }

        return parentNetworkHref;
    }

    /**
     * Retrieves the VAppTemplateType from the vCloud using the provided baseVdcUrl. It does so by
     * using the vCloud Query service API to search for vAppTemplate and filter on the name of the
     * template, provided by the templateName passed in.
     * 
     * @param baseVdcUrl
     *            the base url to the vCloud API to make REST calls to
     * @return the instance of VAppTemplateType if found, null if not
     */
    private VAppTemplateType getVAppTemplate(String baseVcdUrl) {
        System.out.print("Searching for template " + options.templateName + "...");

        // Query the vCloud Query API to search for a vAppTemplate matching the
        // options.templateName (command line option --templatename)
        QueryResultRecordsType queryResults = HttpUtils.getQueryResults(baseVcdUrl,
                "type=vAppTemplate&filter=name==" + options.templateName, options, vcd.vcdToken);

        List<JAXBElement<? extends QueryResultRecordType>> rslt = queryResults.getRecord();

        VAppTemplateType vat = null;

        // We should have only one record with the name matching templateName
        if (rslt.size() == 1) {
            QueryResultVAppTemplateRecordType qrrt = (QueryResultVAppTemplateRecordType) rslt.get(0).getValue();
            String templateHref = qrrt.getHref();

            // invoke the GET request to the template href to get the VAppTemplateType
            HttpGet httpGet = vcd.get(templateHref, options);
            HttpResponse response = HttpUtils.httpInvoke(httpGet);

            // make surethe status is 200 OK
            if (null != response && response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                // unmarshal the response entity into a VAppTemplateType
                vat = HttpUtils.unmarshal(response.getEntity(), VAppTemplateType.class);
                System.out.println("found.\n");
            }
        }

        if (null == vat) {
            throw new RuntimeException("Template not found: " + options.templateName);
        }

        return vat;
    }

    /**
     * This method is to get the href for instantiateVApp
     * 
     * @param vdcHref
     *            the href for the vCloud API End point for the VDC.
     * @return href to perform instantiate vApp action
     */
    private String getInstantiateVAppLink(VdcType vdc) {
        // List of links associated with the VDC
        List<com.vmware.vcloud.api.rest.schema.LinkType> linklist = vdc.getLink();
        String instantiateHref = null;

        // Iterating through the Links to find the instantiateVAppTemplate action Link
        for (com.vmware.vcloud.api.rest.schema.LinkType link : linklist) {
            if (link.getType().contains("instantiateVAppTemplate")) {
                instantiateHref = link.getHref();
                break;
            }
        }

        if (null == instantiateHref) {
            throw new RuntimeException("Could not find instantiateHref for VDC: " + vdc.getName());
        }

        return instantiateHref;
    }

    /**
     * This method will initialize and deploy a vApp using the instantiationHref, vappTemplateHref,
     * vmNetworkName and vdc provided.
     * 
     * @param instantiateHref
     *            the href to instantiatevApp action
     * @param vappTempalteHref
     *            the href to the vApp Template to be used to create vApp
     * @param vmNetworkName
     *            the network name for VM
     * @param VdcType
     *            the vdc
     * @return VappType if the initialize vapp succeeds, null otherwise
     */
    private VAppType createVApp(final String instantiateHref, final String vappTempalteHref, VdcType vdc) {
        System.out.print("Attempting to create vApp...");
        ReferenceType vappReference = new ReferenceType();
        vappReference.setHref(vappTempalteHref);

        // Create an InstantiateVAppTemplateParamsType object and initialize it
        InstantiateVAppTemplateParamsType instvApp = new InstantiateVAppTemplateParamsType();

        // Set the name of vApp using the options.vappname (command line option --targetvappname)
        instvApp.setName(options.vappName);

        // do not deploy this vApp.. we still need to update network info which requires the
        // vApp to be undeployed and not powered on.
        instvApp.setDeploy(Boolean.FALSE);
        instvApp.setPowerOn(Boolean.FALSE);

        // vApp reference to be used
        instvApp.setSource(vappReference);
        instvApp.setDescription("VM creation using VMCreateSample");
        instvApp.setAllEULAsAccepted(Boolean.TRUE);

        InstantiationParamsType instParams = new InstantiationParamsType();

        instvApp.setInstantiationParams(instParams);

        JAXBContext jaxbContexts = null;
        try {
            jaxbContexts = JAXBContext.newInstance(InstantiateVAppTemplateParamsType.class);
        } catch (JAXBException ex) {
            throw new RuntimeException("Problem creating JAXB Context: ", ex);
        }

        // Create HttpPost request to perform InstantiatevApp action
        HttpPost instantiateVAppPost = vcd.post(instantiateHref, options);
        com.vmware.vcloud.api.rest.schema.ObjectFactory obj = new com.vmware.vcloud.api.rest.schema.ObjectFactory();
        JAXBElement<InstantiateVAppTemplateParamsType> instvAppTemplate = obj
                .createInstantiateVAppTemplateParams(instvApp);
        OutputStream os = null;

        try {
            javax.xml.bind.Marshaller marshaller = jaxbContexts.createMarshaller();
            marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            os = new ByteArrayOutputStream();
            // Marshal the object via JAXB to XML
            marshaller.marshal(instvAppTemplate, os);
        } catch (JAXBException e) {
            throw new RuntimeException("Problem marshalling instantiation vApp template", e);
        }

        // Set the Content-Type header for the VM vApp template parameters
        ContentType contentType = ContentType
                .create("application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml", "ISO-8859-1");
        StringEntity vapp = new StringEntity(os.toString(), contentType);
        instantiateVAppPost.setEntity(vapp);

        // Invoke the HttoPost to initiate the VM creation process
        HttpResponse response = HttpUtils.httpInvoke(instantiateVAppPost);

        VAppType vApp = null;

        // Make sure response status is 201 Created
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) {
            vApp = HttpUtils.unmarshal(response.getEntity(), VAppType.class);
        }

        if (null == vApp) {
            throw new RuntimeException("Could not instatiate or deploy the vApp.");
        }

        return vApp;
    }

    /**
     * This method will make a POST call to the vApp deploy URL to deploy the vApp and it's
     * children Vms.
     * 
     * @param vApp the vApp instance to deploy
     * @return a TaskType that can be used to monitor the status of the task
     */
    private TaskType deploy(VAppType vApp) {
        String deployHref = null;

        // Search the list of links for the vApp rel="deploy" to get the correct Href
        for (LinkType link : vApp.getLink()) {
            if (link.getRel().equalsIgnoreCase("deploy")) {
                deployHref = link.getHref();
                break;
            }
        }

        // Only proceed if we found a valid deploy Href
        if (null != deployHref) {
            DeployVAppParamsType deployParams = new DeployVAppParamsType();
            deployParams.setPowerOn(Boolean.TRUE);

            // Create HttpPost request to perform InstantiatevApp action
            HttpPost instantiateVAppPost = vcd.post(deployHref, options);

            ObjectFactory objFactory = new ObjectFactory();
            JAXBElement<DeployVAppParamsType> deployParamsType = objFactory.createDeployVAppParams(deployParams);

            OutputStream os = null;

            JAXBContext jaxbContexts = null;
            try {
                jaxbContexts = JAXBContext.newInstance(DeployVAppParamsType.class);
            } catch (JAXBException ex) {
                throw new RuntimeException("Problem creating JAXB Context: ", ex);
            }

            try {
                javax.xml.bind.Marshaller marshaller = jaxbContexts.createMarshaller();
                marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                os = new ByteArrayOutputStream();
                // Marshal the object via JAXB to XML
                marshaller.marshal(deployParamsType, os);
            } catch (JAXBException e) {
                throw new RuntimeException("Problem marshalling instantiation vApp template", e);
            }

            HttpPost deployPost = vcd.post(deployHref, options);
            ;
            // Set the Content-Type header for the VM vApp template parameters
            ContentType contentType = ContentType.create("application/vnd.vmware.vcloud.deployVAppParams+xml",
                    "ISO-8859-1");
            StringEntity deployEntity = new StringEntity(os.toString(), contentType);
            deployPost.setEntity(deployEntity);

            // Invoke the HttoPost to initiate the VM creation process
            HttpResponse response = HttpUtils.httpInvoke(deployPost);

            // Make sure response status is 201 Created
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_ACCEPTED) {
                TaskType taskType = HttpUtils.unmarshal(response.getEntity(), TaskType.class);
                return taskType;
            }
        }

        throw new RuntimeException("Could not deploy " + vApp.getName());
    }

    /**
     * This method will retrieve the internal ip value for the passed in Vm
     * using the Vms NetworkConfigSection to obtain the ip.
     * 
     * @param vm the VmType to obtain the internal ip from
     * @return the internal ip if found, otherwise "none" is returned
     */
    private String getIpForVm(VmType vm) {
        // Request the NetworkConnection information for the VM to extract IP from it.
        HttpResponse response = HttpUtils.httpInvoke(vcd.get(vm.getHref() + VM_NETWORK_URL, options));

        // Make sure response is ok
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            NetworkConnectionSectionType nwsc = HttpUtils.unmarshal(response.getEntity(),
                    NetworkConnectionSectionType.class);
            if (null != nwsc) {
                String ip = nwsc.getNetworkConnection().get(0).getIpAddress();
                return ip;
            }
        }

        return "none";
    }

    /**
     * Iteratres through the passed in VAppType's children Vms, attempting to display
     * each Vm's internal IP information.
     * 
     * @param vApp the VAppType to display it's children Vm's internal ip
     */
    private void displayIPDetails(VAppType vApp) {
        List<VmType> vms = vApp.getChildren().getVm();
        for (VmType vm : vms) {
            // Try to get the IP
            String ip = getIpForVm(vm);
            System.out.println("Vm " + vm.getName() + " internal ip: " + ip + "\n");
        }
    }

    /**
     * Continually makes a GET request to the passed in Taks's Href with a 10 second delay between
     * each request to avoid sending too many requests to the API too fast.
     * 
     * @param task the to wait on
     */
    public void waitForTaskCompletion(TaskType task) {
        int retry = 0;

        while ((!task.getStatus().equals("success") && !task.getStatus().equals("error")) && retry++ < 10) {
            HttpResponse response = HttpUtils.httpInvoke(vcd.get(task.getHref(), options));
            task = HttpUtils.unmarshal(response.getEntity(), TaskType.class);

            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        // If the retry count reached 10 or the status is error, display task details to help
        // with figuring out what went wrong.
        if (retry == 10 || task.getStatus().equals("error")) {
            System.out.println("\n         Task details : " + task.getDetails());
            System.out.println("      Task description : " + task.getDescription());
            System.out.println("        Task Operation : " + task.getOperation());
            System.out.println("        Task error msg : " + task.getError().getMessage());
            System.out.println(" Task major error code : " + task.getError().getMajorErrorCode());
            System.out.println("Task error stack trace : " + task.getError().getStackTrace());

            throw new RuntimeException("Could not complete creation of vApp");
        }
    }

    /**
     * Loops through the list of tasks provided by the tasksInProgressType parameter, calling
     * the waitForTaskCompletion method with each individual task.
     * 
     * @param tasksInProgressType a collection of tasks to wait for
     */
    public void waitForTasks(TasksInProgressType tasksInProgressType) {
        List<TaskType> tasks = tasksInProgressType.getTask();
        for (TaskType task : tasks) {
            this.waitForTaskCompletion(task);
        }
    }
}