Skip to content

Commit

Permalink
add folder_template_from_vm module
Browse files Browse the repository at this point in the history
  • Loading branch information
mikemorency committed Jul 10, 2024
1 parent f7d6acb commit 657e958
Show file tree
Hide file tree
Showing 12 changed files with 629 additions and 3 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/10-rename_get_vm_by_name.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
bugfixes:
- module_utils/vmware_rest_client - rename get_vm_by_name method as there is same signature already
2 changes: 2 additions & 0 deletions changelogs/fragments/48-add_folder_template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- folder_template_from_vm - add module and tests to create a template from an existing VM in vcenter and store the template in a folder
20 changes: 20 additions & 0 deletions plugins/doc_fragments/vmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ class ModuleDocFragment(object):
- If the value is not specified in the task, the value of environment variable E(VMWARE_PORT) will be used instead.
type: int
default: 443
datacenter:
description:
- The datacenter to use when connecting to a vCenter.
type: str
aliases: [ datacenter_name ]
cluster:
description:
- The cluster to use when connecting to a vCenter.
type: str
aliases: [ cluster_name ]
proxy_host:
description:
- Address of a proxy that will receive all HTTPS requests and relay them.
Expand Down Expand Up @@ -95,6 +105,16 @@ class ModuleDocFragment(object):
- If the value is not specified in the task, the value of environment variable E(VMWARE_PORT) will be used instead.
type: int
default: 443
datacenter:
description:
- The datacenter to use when connecting to a vCenter.
type: str
aliases: [ datacenter_name ]
cluster:
description:
- The cluster to use when connecting to a vCenter.
type: str
aliases: [ cluster_name ]
proxy_host:
description:
- Address of a proxy that will receive all HTTPS requests and relay them.
Expand Down
145 changes: 144 additions & 1 deletion plugins/module_utils/vmware.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
PYVMOMI_IMP_ERR = None
try:
from pyVim import connect
from pyVmomi import vim, vmodl
from pyVmomi import vim, vmodl, VmomiSupport
HAS_PYVMOMI = True
except ImportError:
PYVMOMI_IMP_ERR = traceback.format_exc()
Expand Down Expand Up @@ -58,6 +58,12 @@ def vmware_argument_spec():
required=False,
no_log=True,
fallback=(env_fallback, ['VMWARE_PASSWORD'])),
cluster=dict(type='str',
aliases=['cluster_name'],
required=False),
datacenter=dict(type='str',
aliases=['datacenter_name'],
required=False),
port=dict(type='int',
default=443,
fallback=(env_fallback, ['VMWARE_PORT'])),
Expand Down Expand Up @@ -218,3 +224,140 @@ def is_vcenter(self):
return True
elif api_type == 'HostAgent':
return False

def __get_objs_by_name(self, vimtype, name, first_only=False):
"""
Get any vsphere object associated with a given text name and vim type.
Args:
vimtype: The type of object to search for
name: The name of the object to search for
first_only: If true, return once the first object is found.
Useful when names must be unique
Returns:
list(object) or list() if no matches are found
"""
objs = []
container = self.content.viewManager.CreateContainerView(self.content.rootFolder, vimtype, True)
for c in container.view:
if c.name == name:
if first_only:
return c
objs += [c]

return objs if objs else None

def get_vm_by_name(self, vm_name, fail_on_missing=False, name_match=None):
"""
Get the vms matching the given name. VM names may not be unique
in a given cluster.
Args:
vm_name: Name of the VM to search for
fail_on_missing: If true, an error will be thrown if no VMs are found
name_match: If provided, return one VM from the list of VMs. Either 'first' or 'last'
Returns:
list(vm), vm if name_match is provided, or None if no matches were found
"""
vms = self.__get_objs_by_name([vim.VirtualMachine], vm_name)
if not vms and fail_on_missing:
self.module.fail_json("Unable to find VM with name %s" % vm_name)
if vms and name_match:
if name_match == 'first':
return vms[0]
elif name_match == 'last':
return vms[-1]
else:
self.module.fail_json("Unrecognized name_match option '%s' in get_vm_by_name method" % name_match)

return vms

def get_folder_by_name(self, folder_name, fail_on_missing=False):
"""
Get all folders with the given name. Names are not unique
in a given cluster, so multiple folder objects can be returned
Args:
folder_name: Name of the folder to search for
fail_on_missing: If true, an error will be thrown if no folders are found
Returns:
list(folder object) or None
"""
folder = self.__get_objs_by_name([vim.Folder], folder_name)
if not folder and fail_on_missing:
self.module.fail_json("Unable to find folder with name %s" % folder_name)
return folder

def get_folder_by_absolute_path(self, folder_path, fail_on_missing=False):
"""
Get a folders with the given absolute path. Paths are unique so only
one folder can be returned at most. An absolute path might look like
'Datacenter Name/vm/my/folder/structure'
Args:
folder_path: The absolute path to a folder to search for
fail_on_missing: If true, an error will be thrown if no folders are found
Returns:
folder object or None
"""
folder = self.si.content.searchIndex.FindByInventoryPath(folder_path)
if not folder and fail_on_missing:
self.module.fail_json("Unable to find folder with absolute path %s" % folder_path)
return folder

def get_datastore_by_name(self, ds_name, fail_on_missing=False):
"""
Get the datastore matching the given name. Datastore names must be unique
in a given cluster, so only one object is returned at most.
Args:
ds_name: Name of the datastore to search for
fail_on_missing: If true, an error will be thrown if no datastores are found
Returns:
datastore object or None
"""
ds = self.__get_objs_by_name([vim.Datastore], ds_name, first_only=True)
if not ds and fail_on_missing:
self.module.fail_json("Unable to find datastore with name %s" % ds_name)
return ds

def get_resource_pool_by_name(self, pool_name, fail_on_missing=False):
"""
Get the resource pool matching the given name. Pool names must be unique
in a given cluster, so only one object is returned at most.
Args:
pool_name: Name of the pool to search for
fail_on_missing: If true, an error will be thrown if no pools are found
Returns:
resource pool object or None
"""
pool = self.__get_objs_by_name([vim.ResourcePool], pool_name, first_only=True)
if not pool and fail_on_missing:
self.module.fail_json("Unable to find resource pool with name %s" % pool_name)
return pool

def get_vm_by_uuid(self, vm_uuid, use_instance_uuid=True, fail_on_missing=False):
"""
Search for a VM using the instance UUID or BIOS UUID. BIOS UUID is considered an older
approach and is not garunteed to be unique.
Args:
vm_uuid: The uuid of the instance to search for
use_instance_uuid: If false, search for the BIOS UUID instead of the instance UUID
fail_on_missing: If true, an error will be thrown if no VMs are found
"""
vm = self.si.content.searchIndex.FindByUuid(
instanceUuid=use_instance_uuid,
uuid=vm_uuid,
vmSearch=True
)
if not vm and fail_on_missing:
self.module.fail_json("Unable to find VM with UUID %s" % vm_uuid)
return vm

def get_vm_by_moid(self, vm_moid, fail_on_missing=False):
"""
Search for a VM using the instance MOID or moREF. MOID is common in the VMWare REST
API but still usable in the SOAP API.
Args:
vm_moid: The uuid of the instance to search for
fail_on_missing: If true, an error will be thrown if no VMs are found
"""
vm = VmomiSupport.templateOf('VirtualMachine')(vm_moid, self.si._stub)
if not vm and fail_on_missing:
self.module.fail_json("Unable to find VM with MOID %s" % vm_moid)
return vm
2 changes: 1 addition & 1 deletion plugins/module_utils/vmware_rest_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ def get_host_by_name(self, host_name, datacenter_name=None):
host_summaries = self.api_client.vcenter.Host.list(filter_spec)
return host_summaries[0].host if len(host_summaries) > 0 else None

def get_vm_by_name(self, vm_name, datacenter_name=None):
def get_vm_obj_by_name(self, vm_name, datacenter_name=None):
"""
Returns the identifier of a VM with the mentioned names.
"""
Expand Down
2 changes: 1 addition & 1 deletion plugins/modules/content_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def create_template_from_vm(self):
name=self.template,
placement=placement_spec,
library=self.get_library_by_name(self.library),
source_vm=self.get_vm_by_name(self.vm_name),
source_vm=self.get_vm_obj_by_name(self.vm_name),
)
template_id = ''
try:
Expand Down
Loading

0 comments on commit 657e958

Please sign in to comment.