vmware / pyvmomi-community-samples

A place for community contributed samples for the pyVmomi library.
Apache License 2.0
1.02k stars 928 forks source link

deploy ovf template #151

Open abhi29 opened 9 years ago

abhi29 commented 9 years ago

How to deploy ovf template using pyvmomi..

hartsock commented 9 years ago

This is a popular request. I've had limited bandwidth to write this sample and would appreciate someone picking this up. Reply here for any takers, otherwise I'll put this on my back log.

abhi29 commented 9 years ago

Hi Shawn,

Can you please provide some sample script which can load ova image stored in datacenter and deploy ovf template , I'm working on this but unable to do.

thanks, Abhishek

On Thu, Jan 29, 2015 at 7:37 PM, Shawn Hartsock notifications@github.com wrote:

This is a popular request. I've had limited bandwidth to write this sample and would appreciate someone picking this up. Reply here for any takers, otherwise I'll put this on my back log.

— Reply to this email directly or view it on GitHub https://github.com/vmware/pyvmomi-community-samples/issues/151#issuecomment-72029914 .

rashrag commented 9 years ago

Hi, Has this been completed? Is there some way to do it using vim.OvfManager.CreateImportSpecParams?

hartsock commented 9 years ago

@rashrag we've not had someone contribute a version of this OVF deployment tutorial for this project. Considering that a blog post with detailed instructions exists, this should be one of the easier samples for someone to write.

pathcl commented 9 years ago

I'll attempt it :)

hartsock commented 9 years ago

@pathcl cool! :-)

prashasthip commented 9 years ago

Hi @pathcl, were you able to complete this by any chance ?

hartsock commented 9 years ago

@pathcl & @prashasthip the bar on samples is "can Shawn make it work?" ... so just be sure to submit it with sufficient comments that by reading the python script we get an idea of how to do things. Thanks.

pathcl commented 9 years ago

So finally anybody did it @hartsock ?

prashasthip commented 9 years ago

@hartsock - I'll take this up too.

prashasthip commented 9 years ago

@hartsock - how do I use CreateImportSpecParams to specify the destination folder where I want the deployed ovf to be placed ?

asonar commented 9 years ago

@prashasthip you need to specify folder location in import api,

resource_pool.ImportVApp(import_spec.importSpec, folder=datacenter.vmFolder, host=host)

prashasthip commented 9 years ago

Thanks @asonar !

gaacad commented 7 years ago

I would like to deploy OVA file on ESXI too ... need this urgently. Option is to go with powercli - which is a windows only option that does not really work for me. If anyone has a solution, please post it. Thanks.

rushikumar-vaghani commented 4 years ago

@hartsock can you share source code or link how you did deploy ovf template using pyvomi?

hartsock commented 4 years ago

@vaghani unfortunately that's part of a package that hasn't been open sourced yet. I do intend on taking that code through the fling process for official release someday but it's been years.

A quick non-working clipping of the code that I hopefully won't get into trouble for sharing looks like this:

class VirtualMachineDeployer(object):
    def deploy_ova(self, ova_file, vm_name=None, datastore_name=None, accept_all=True):
        # deal with archive data format here, then call down to actual deployment method
        out = False
        ova_tar = tarfile.open(ova_file, 'r')
        if ova_tar is not None:
            ovf = None
            members = []
            total_size = 0
            for member in ova_tar.getmembers():
                logging.debug("Reading OVA member {}".format(member.name))
                total_size += member.size
                if str(member.name).endswith('.ovf'):
                    f = ova_tar.extractfile(member)
                    content = f.read()
                    # TODO: by convention OVF files are encoded in utf-8 ... this might not always be true
                    ovf = ovf_transcoder(content)
                    f.close()
                else:
                    f = ova_tar.extractfile(member)
                    members.append(TarFileMemberReader(member, f, "uploading "))

            out = self._deploy(TarEnvelope(ovf, members), vm_name, datastore_name, accept_all, total_size)
        ova_tar.close()
        return out

    def _deploy(self, env, vm_name, datastore_name, accept_all=True, total_size=None):
        self._read_bits = 0

        if datastore_name is not None:
            self.datastore = datastore_name
        else:
            raise ValueError("A datastore must be specified!")

        lease = None
        with self.connection as si:
            manager = si.content.ovfManager
            datastore = si.datastore(datastore_name)
            data_center = si.data_center(self.data_center)
            resource_pools = si.find_all_by_type(vim.ResourcePool)
            if len(resource_pools) == 0:
                raise EnvironmentError("Could not find a default resource pool!")
            resource_pool = resource_pools[0]

            host_systems = si.find_all_by_type(vim.HostSystem)
            if len(host_systems) == 0:
                raise EnvironmentError("Could not find a host system on host!")
            host_system = host_systems[0]

            if si.virtual_machine(vm_name):
                logging.error("Virtual Machine named {} already exists!".format(vm_name))
                return False

            disk_provisioning = vim.OvfManager.CreateImportSpecParams.DiskProvisioningType.thin
            spec_params = vim.OvfManager.CreateImportSpecParams(entityName=vm_name,
                                                                diskProvisioning=disk_provisioning,
                                                                hostSystem=host_system)
            ovf_str = env.ovf
            if six.PY2:
                ovf_str = _pyvmomi_translate(env.ovf.decode('utf-8'))
            result = manager.CreateImportSpec(ovf_str, resource_pool, datastore, spec_params)

            # build first layer of lookup tables for file transfers
            self._import_file_mapping(result)

            lease = resource_pool.ImportVApp(result.importSpec,
                                             data_center.vmFolder,
                                             host_system)

            # build second layer of lookup tables for file transfers
            self.wait_on_lease(lease)
            self._lease_device_mapping(lease)

            self._read_bits = 0

            for member_reader in env.members:
                if lease.error:
                    logging.error(lease.error)

                url = self._nfc_url(member_reader.name)
                if url is not None:
                    if str(url).startswith('https://*/'):
                        path = str(url)[len('https://*/'):]
                        url = 'https://{host}/{path}'.format(
                            host=self.connection.host, path=path)
                    logging.debug("Cleaned URL is: {}".format(url))
                    member_reader.start(
                        call_back=lambda: lease.HttpNfcLeaseProgress(
                            member_reader.percentage()
                        )
                    )
                    content_type = None
                    if str(member_reader.name).endswith('.vmdk'):
                        content_type = 'application/x-vnd.vmware-streamVmdk'
                    logging.info("Starting upload {url} host time is {time} ...".format(
                        time=si.CurrentTime(), url=url))
                    lease.HttpNfcLeaseProgress(0)
                    self.file_manager.upload_nfc(url, member_reader, False, content_type)
                    lease.HttpNfcLeaseProgress(100)
                    logging.info("Finished upload {url} host time is {time} ...".format(
                        time=si.CurrentTime(), url=url))
                    member_reader.finish()
                else:
                    logging.debug("No upload URL for OVA file member {} skipping file!".format(member_reader.name))
            if lease.error:
                logging.error(lease.error)

            if lease is not None:
                lease.HttpNfcLeaseComplete()

        # presume things worked if we get this far TODO: actual troubleshooting logic
        return True

... there's some problematic transcoding happening in there in these helpers ...


def _pyvmomi_translate(contents):
    """translates out the problematic unicode EULA quotes found in VMware EULA
    only a problem for Python 2.x, you don't need this for Python 3.x"""

    # This is a hacky work-around for a pyvmomi on python 2.7 problem that does
    # not happen on python 3.x becuase python 3 handles internationalization much
    # better than python 2.x does due to string data encoding improvements.
    return contents.replace(
        u'\u2018', '\''
    ).replace(
        u'\u2019', '\''
    ).replace(
        u'\u201c', "\""
    ).replace(
        u'\u201d', "\""
    )

# noinspection PyCompatibility
def ovf_transcoder(content, codec='utf-8'):
    # unfortunately, this is one of those places where a weaker type system
    # causes big problems. The content object may be of multiple data types
    # which we will need to detect and handle differently in different
    # python runtimes. This is made harder by trying to maintain such a
    # wide spread in runtime-compatibilities.
    if isinstance(content, str):
        return str(content)
    if six.PY3:
        return str(content.decode(codec))
    if six.PY2:
        temp = _pyvmomi_translate(content.decode(codec))
        return temp.encode('ascii', 'ignore')

... note that there's use of unreleased python enhancements for example si.virtual_machine(vm_name) is an enhancement to normal pyVmomi I wrote in pyvmomi-tools which adds a "find VM by name" feature to pyVmomi's normal si

But, if you keep in mind there's a lot of helpers you should be able to read that and get the general idea how to not only deploy a VM but also update the vCenter task progress bar as you do it.