VMware: New module for deploy VM from content library template (#60203)

* Fixed format issues reported by pre-check tests

* Changes as per review comments. using single api_client between util class and module

* re-running the task to check the idempotency of module object
pull/60995/head
Pavan Bidkar 5 years ago committed by Abhijeet Kasurde
parent 32b0a72547
commit b75fc87a86

@ -30,6 +30,13 @@ try:
from com.vmware.vapi.std_client import DynamicID
from vmware.vapi.vsphere.client import create_vsphere_client
from com.vmware.vapi.std.errors_client import Unauthorized
from com.vmware.content.library_client import Item
from com.vmware.vcenter_client import (Folder,
Datacenter,
ResourcePool,
Datastore,
Cluster,
Host)
HAS_VSPHERE = True
except ImportError:
VSPHERE_IMP_ERR = traceback.format_exc()
@ -243,6 +250,106 @@ class VmwareRestClient(object):
return tags
def get_library_item_by_name(self, name):
"""
Returns the identifier of the library item with the given name.
Args:
name (str): The name of item to look for
Returns:
str: The item ID or None if the item is not found
"""
find_spec = Item.FindSpec(name=name)
item_ids = self.api_client.content.library.Item.find(find_spec)
item_id = item_ids[0] if item_ids else None
return item_id
def get_datacenter_by_name(self, datacenter_name):
"""
Returns the identifier of a datacenter
Note: The method assumes only one datacenter with the mentioned name.
"""
filter_spec = Datacenter.FilterSpec(names=set([datacenter_name]))
datacenter_summaries = self.api_client.vcenter.Datacenter.list(filter_spec)
datacenter = datacenter_summaries[0].datacenter if len(datacenter_summaries) > 0 else None
return datacenter
def get_folder_by_name(self, datacenter_name, folder_name):
"""
Returns the identifier of a folder
with the mentioned names.
"""
datacenter = self.get_datacenter_by_name(datacenter_name)
if not datacenter:
return None
filter_spec = Folder.FilterSpec(type=Folder.Type.VIRTUAL_MACHINE,
names=set([folder_name]),
datacenters=set([datacenter]))
folder_summaries = self.api_client.vcenter.Folder.list(filter_spec)
folder = folder_summaries[0].folder if len(folder_summaries) > 0 else None
return folder
def get_resource_pool_by_name(self, datacenter_name, resourcepool_name):
"""
Returns the identifier of a resource pool
with the mentioned names.
"""
datacenter = self.get_datacenter_by_name(datacenter_name)
if not datacenter:
return None
names = set([resourcepool_name]) if resourcepool_name else None
filter_spec = ResourcePool.FilterSpec(datacenters=set([datacenter]),
names=names)
resource_pool_summaries = self.api_client.vcenter.ResourcePool.list(filter_spec)
resource_pool = resource_pool_summaries[0].resource_pool if len(resource_pool_summaries) > 0 else None
return resource_pool
def get_datastore_by_name(self, datacenter_name, datastore_name):
"""
Returns the identifier of a datastore
with the mentioned names.
"""
datacenter = self.get_datacenter_by_name(datacenter_name)
if not datacenter:
return None
names = set([datastore_name]) if datastore_name else None
filter_spec = Datastore.FilterSpec(datacenters=set([datacenter]),
names=names)
datastore_summaries = self.api_client.vcenter.Datastore.list(filter_spec)
datastore = datastore_summaries[0].datastore if len(datastore_summaries) > 0 else None
return datastore
def get_cluster_by_name(self, datacenter_name, cluster_name):
"""
Returns the identifier of a cluster
with the mentioned names.
"""
datacenter = self.get_datacenter_by_name(datacenter_name)
if not datacenter:
return None
names = set([cluster_name]) if cluster_name else None
filter_spec = Cluster.FilterSpec(datacenters=set([datacenter]),
names=names)
cluster_summaries = self.api_client.vcenter.Cluster.list(filter_spec)
cluster = cluster_summaries[0].cluster if len(cluster_summaries) > 0 else None
return cluster
def get_host_by_name(self, datacenter_name, host_name):
"""
Returns the identifier of a Host
with the mentioned names.
"""
datacenter = self.get_datacenter_by_name(datacenter_name)
if not datacenter:
return None
names = set([host_name]) if host_name else None
filter_spec = Host.FilterSpec(datacenters=set([datacenter]),
names=names)
host_summaries = self.api_client.vcenter.Host.list(filter_spec)
host = host_summaries[0].host if len(host_summaries) > 0 else None
return host
@staticmethod
def search_svc_object_by_name(service, svc_obj_name=None):
"""

@ -0,0 +1,271 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Ansible Project
# Copyright: (c) 2019, Pavan Bidkar <pbidkar@vmware.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = r'''
---
module: vmware_content_deploy_template
short_description: Deploy Virtual Machine from template stored in content library.
description:
- Module to deploy virtual machine from template in content library.
- Content Library feature is introduced in vSphere 6.0 version, so this module is not supported in the earlier versions of vSphere.
- All variables and VMware object names are case sensitive.
version_added: '2.9'
author:
- Pavan Bidkar (@pgbidkar)
notes:
- Tested on vSphere 6.5, 6.7
requirements:
- python >= 2.6
- PyVmomi
- vSphere Automation SDK
options:
template:
description:
- The name of template from which VM to be deployed.
type: str
required: True
aliases: ['template_src']
name:
description:
- The name of the VM to be deployed.
type: str
required: True
aliases: ['vm_name']
datacenter:
description:
- Name of the datacenter, where VM to be deployed.
type: str
required: True
datastore:
description:
- Name of the datastore to store deployed VM and disk.
type: str
required: True
folder:
description:
- Name of the folder in datacenter in which to place deployed VM.
type: str
required: True
host:
description:
- Name of the ESX Host in datacenter in which to place deployed VM.
type: str
required: True
resource_pool:
description:
- Name of the resourcepool in datacenter in which to place deployed VM.
type: str
required: False
cluster:
description:
- Name of the cluster in datacenter in which to place deployed VM.
type: str
required: False
state:
description:
- The state of Virutal Machine deployed from template in content library.
- If set to C(present) and VM does not exists, then VM is created.
- If set to C(present) and VM exists, no action is taken.
- If set to C(poweredon) and VM does not exists, then VM is created with powered on state.
- If set to C(poweredon) and VM exists, no action is taken.
type: str
required: False
default: 'present'
choices: [ 'present', 'poweredon' ]
extends_documentation_fragment: vmware_rest_client.documentation
'''
EXAMPLES = r'''
- name: Deploy Virtual Machine from template in content library
vmware_content_deploy_template:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
template: rhel_test_template
datastore: Shared_NFS_Volume
folder: vm
datacenter: Sample_DC_1
name: Sample_VM
resource_pool: test_rp
validate_certs: False
state: present
delegate_to: localhost
- name: Deploy Virtual Machine from template in content library with PowerON State
vmware_content_deploy_template:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
template: rhel_test_template
datastore: Shared_NFS_Volume
folder: vm
datacenter: Sample_DC_1
name: Sample_VM
resource_pool: test_rp
validate_certs: False
state: poweredon
delegate_to: localhost
'''
RETURN = r'''
vm_deploy_info:
description: Virtual machine deployment message and vm_id
returned: on success
type: dict
sample: {
"msg": "Deployed Virtual Machine 'Sample_VM'.",
"vm_id": "vm-1009"
}
'''
import uuid
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.vmware_rest_client import VmwareRestClient
from ansible.module_utils.vmware import PyVmomi
from ansible.module_utils._text import to_native
HAS_VAUTOMATION_PYTHON_SDK = False
try:
from com.vmware.vcenter.vm_template_client import LibraryItems
HAS_VAUTOMATION_PYTHON_SDK = True
except ImportError:
pass
class VmwareContentDeployTemplate(VmwareRestClient):
def __init__(self, module):
"""Constructor."""
super(VmwareContentDeployTemplate, self).__init__(module)
self.template_service = self.api_client.vcenter.vm_template.LibraryItems
self.template_name = self.params.get('template')
self.vm_name = self.params.get('name')
self.datacenter = self.params.get('datacenter')
self.datastore = self.params.get('datastore')
self.folder = self.params.get('folder')
self.resourcepool = self.params.get('resource_pool')
self.cluster = self.params.get('cluster')
self.host = self.params.get('host')
def deploy_vm_from_template(self, power_on=False):
# Find the datacenter by the given datacenter name
self.datacenter_id = self.get_datacenter_by_name(datacenter_name=self.datacenter)
if not self.datacenter_id:
self.module.fail_json(msg="Failed to find the datacenter %s" % self.datacenter)
# Find the datastore by the given datastore name
self.datastore_id = self.get_datastore_by_name(self.datacenter, self.datastore)
if not self.datastore_id:
self.module.fail_json(msg="Failed to find the datastore %s" % self.datastore)
# Find the LibraryItem (Template) by the given LibraryItem name
self.library_item_id = self.get_library_item_by_name(self.template_name)
if not self.library_item_id:
self.module.fail_json(msg="Failed to find the library Item %s" % self.template_name)
# Find the folder by the given folder name
self.folder_id = self.get_folder_by_name(self.datacenter, self.folder)
if not self.folder_id:
self.module.fail_json(msg="Failed to find the folder %s" % self.folder)
# Find the Host by given HostName
self.host_id = self.get_host_by_name(self.datacenter, self.host)
if not self.host_id:
self.module.fail_json(msg="Failed to find the Host %s" % self.host)
# Find the resourcepool by the given resourcepool name
self.resourcepool_id = None
if self.resourcepool:
self.resourcepool_id = self.get_resource_pool_by_name(self.datacenter, self.resourcepool)
if not self.resourcepool_id:
self.module.fail_json(msg="Failed to find the resource_pool %s" % self.resourcepool)
# Find the Cluster by the given Cluster name
self.cluster_id = None
if self.cluster:
self.cluster_id = self.get_resource_pool_by_name(self.datacenter, self.resourcepool)
if not self.cluster_id:
self.module.fail_json(msg="Failed to find the Cluster %s" % self.cluster)
# Create VM placement specs
self.placement_spec = LibraryItems.DeployPlacementSpec(folder=self.folder_id,
host=self.host_id
)
if self.resourcepool_id or self.cluster_id:
self.placement_spec.resource_pool = self.resourcepool_id
self.placement_spec.cluster = self.cluster_id
self.vm_home_storage_spec = LibraryItems.DeploySpecVmHomeStorage(datastore=to_native(self.datastore_id))
self.disk_storage_spec = LibraryItems.DeploySpecDiskStorage(datastore=to_native(self.datastore_id))
self.deploy_spec = LibraryItems.DeploySpec(name=self.vm_name,
placement=self.placement_spec,
vm_home_storage=self.vm_home_storage_spec,
disk_storage=self.disk_storage_spec,
powered_on=power_on
)
vm_id = self.template_service.deploy(self.library_item_id, self.deploy_spec)
if vm_id:
self.module.exit_json(
changed=True,
vm_deploy_info=dict(
msg="Deployed Virtual Machine '%s'." % self.vm_name,
vm_id=vm_id,
)
)
self.module.exit_json(changed=False,
vm_deploy_info=dict(msg="Virtual Machine deployment failed", vm_id=''))
def main():
argument_spec = VmwareRestClient.vmware_client_argument_spec()
argument_spec.update(
state=dict(type='str', default='present',
choices=['present', 'poweredon']),
template=dict(type='str', aliases=['template_src'], required=True),
name=dict(type='str', required=True, aliases=['vm_name']),
datacenter=dict(type='str', required=True),
datastore=dict(type='str', required=True),
folder=dict(type='str', required=True),
host=dict(type='str', required=True),
resource_pool=dict(type='str', required=False),
cluster=dict(type='str', required=False),
)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
result = {'failed': False, 'changed': False}
pyv = PyVmomi(module=module)
vm = pyv.get_vm()
if vm:
module.exit_json(
changed=False,
vm_deploy_info=dict(
msg="Virtual Machine '%s' already Exists." % module.params['name'],
vm_id=vm._moId,
)
)
vmware_contentlib_create = VmwareContentDeployTemplate(module)
if module.params['state'] in ['present']:
if module.check_mode:
result.update(
vm_name=module.params['name'],
changed=True,
desired_operation='Create VM with PowerOff State',
)
module.exit_json(**result)
vmware_contentlib_create.deploy_vm_from_template()
if module.params['state'] == 'poweredon':
if module.check_mode:
result.update(
vm_name=module.params['name'],
changed=True,
desired_operation='Create VM with PowerON State',
)
module.exit_json(**result)
vmware_contentlib_create.deploy_vm_from_template(power_on=True)
if __name__ == '__main__':
main()

@ -0,0 +1,3 @@
cloud/vcenter
unsupported
needs/target/prepare_vmware_tests

@ -0,0 +1,37 @@
# Test code for the deploy VM from content library template.
# Copyright: (c) 2019, Pavan Bidkar <pbidkar@vmware.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- import_role:
name: prepare_vmware_tests
vars:
setup_datacenter: true
- when: vcsim is not defined
block:
- &deploy_vm_from_content_library_template
vmware_content_deploy_template:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
template: '{{ test_vm_temp }}'
datastore: '{{ ds1 }}'
datacenter: '{{ dc1 }}'
folder: '{{ f0 }}'
host: '{{ esx1 }}'
name: 'test_content_deploy_vm'
state: poweredon
validate_certs: false
register: template_deploy
- name: Check VM deployed successfully
assert:
that:
- template_deploy.changed
- <<: *deploy_vm_from_content_library_template
name: Deploy VM from template again
- name: Check VM with same name is deployed
assert:
that:
- not template_deploy.changed
Loading…
Cancel
Save