From c0723a299a2c434cc4b1a11905a9716a61fa4f36 Mon Sep 17 00:00:00 2001 From: Ondra Machacek Date: Thu, 23 May 2024 10:32:02 +0200 Subject: [PATCH] Add content_template module Module to manage template in content library from virtual machine. Signed-off-by: Ondra Machacek --- .../23-new-module-content-create-template.yml | 2 + plugins/module_utils/vmware_rest_client.py | 381 ++++-------------- plugins/modules/content_template.py | 235 +++++++++++ .../targets/content_create_template/mock.json | 74 ++++ .../targets/content_create_template/run.yml | 12 + .../targets/content_create_template/runme.sh | 3 + .../content_create_template/tasks/main.yml | 19 + .../targets/content_create_template/vars.yml | 6 + 8 files changed, 432 insertions(+), 300 deletions(-) create mode 100644 changelogs/fragments/23-new-module-content-create-template.yml create mode 100644 plugins/modules/content_template.py create mode 100644 tests/integration/targets/content_create_template/mock.json create mode 100644 tests/integration/targets/content_create_template/run.yml create mode 100755 tests/integration/targets/content_create_template/runme.sh create mode 100644 tests/integration/targets/content_create_template/tasks/main.yml create mode 100644 tests/integration/targets/content_create_template/vars.yml diff --git a/changelogs/fragments/23-new-module-content-create-template.yml b/changelogs/fragments/23-new-module-content-create-template.yml new file mode 100644 index 00000000..04290ade --- /dev/null +++ b/changelogs/fragments/23-new-module-content-create-template.yml @@ -0,0 +1,2 @@ +minor_changes: + - content_template - Add new module to manage templates in content library diff --git a/plugins/module_utils/vmware_rest_client.py b/plugins/module_utils/vmware_rest_client.py index 4295d5d7..61eb7886 100644 --- a/plugins/module_utils/vmware_rest_client.py +++ b/plugins/module_utils/vmware_rest_client.py @@ -21,25 +21,15 @@ REQUESTS_IMP_ERR = traceback.format_exc() HAS_REQUESTS = False -PYVMOMI_IMP_ERR = None -try: - from pyVim import connect # noqa: F401, pylint: disable=unused-import - from pyVmomi import vim # noqa: F401, pylint: disable=unused-import - HAS_PYVMOMI = True -except ImportError: - PYVMOMI_IMP_ERR = traceback.format_exc() - HAS_PYVMOMI = False - VSPHERE_IMP_ERR = None 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, + VM, Cluster, Host) HAS_VSPHERE = True @@ -96,9 +86,6 @@ def check_required_library(self): if not HAS_REQUESTS: self.module.fail_json(msg=missing_required_lib('requests'), exception=REQUESTS_IMP_ERR) - if not HAS_PYVMOMI: - self.module.fail_json(msg=missing_required_lib('PyVmomi'), - exception=PYVMOMI_IMP_ERR) if not HAS_VSPHERE: self.module.fail_json( msg=missing_required_lib('vSphere Automation SDK', @@ -182,164 +169,6 @@ def connect_to_vsphere_client(self): return client - def get_tags_for_object(self, tag_service=None, tag_assoc_svc=None, dobj=None, tags=None): - """ - Return tag objects associated with an object - Args: - dobj: Dynamic object - tag_service: Tag service object - tag_assoc_svc: Tag Association object - tags: List or set to which the tag objects are being added, reference is returned by the method - Returns: Tag objects associated with the given object - """ - # This method returns tag objects only, - # Please use get_tags_for_dynamic_obj for more object details - if tags is None: - tags = [] - - if not (isinstance(tags, list) or isinstance(tags, set)): - self.module.fail_json(msg="The parameter 'tags' must be of type 'list' or 'set', but type %s was passed" % type(tags)) - - if not dobj: - return tags - - if not tag_service: - tag_service = self.api_client.tagging.Tag - - if not tag_assoc_svc: - tag_assoc_svc = self.api_client.tagging.TagAssociation - - tag_ids = tag_assoc_svc.list_attached_tags(dobj) - - add_tag = tags.append if isinstance(tags, list) else tags.add - for tag_id in tag_ids: - add_tag(tag_service.get(tag_id)) - - return tags - - def get_tags_for_dynamic_obj(self, dobj=None, tags=None): - """ - Return tag object details associated with object - Args: - mid: Dynamic object for specified object - tags: List or set to which the tag objects are being added, reference is returned by the method - - Returns: Tag object details associated with the given object - - """ - if tags is None: - tags = [] - - if not (isinstance(tags, list) or isinstance(tags, set)): - self.module.fail_json(msg="The parameter 'tags' must be of type 'list' or 'set', but type %s was passed" % type(tags)) - - if dobj is None: - return tags - - temp_tags_model = self.get_tags_for_object(dobj=dobj) - - category_service = self.api_client.tagging.Category - - add_tag = tags.append if isinstance(tags, list) else tags.add - for tag_obj in temp_tags_model: - add_tag({ - 'id': tag_obj.id, - 'category_name': category_service.get(tag_obj.category_id).name, - 'name': tag_obj.name, - 'description': tag_obj.description, - 'category_id': tag_obj.category_id, - }) - - return tags - - def get_tags_for_datacenter(self, datacenter_mid=None): - """ - Return list of tag object associated with datacenter - Args: - datacenter_mid: Dynamic object for datacenter - - Returns: List of tag object associated with the given datacenter - - """ - dobj = DynamicID(type='Datacenter', id=datacenter_mid) - return self.get_tags_for_dynamic_obj(dobj=dobj) - - def get_tags_for_datastore(self, datastore_mid=None): - """ - Return list of tag object associated with datastore - Args: - datastore_mid: Dynamic object for datacenter - - Returns: List of tag object associated with the given datastore - - """ - dobj = DynamicID(type="Datastore", id=datastore_mid) - return self.get_tags_for_dynamic_obj(dobj=dobj) - - def get_tags_for_cluster(self, cluster_mid=None): - """ - Return list of tag object associated with cluster - Args: - cluster_mid: Dynamic object for cluster - - Returns: List of tag object associated with the given cluster - - """ - dobj = DynamicID(type='ClusterComputeResource', id=cluster_mid) - return self.get_tags_for_dynamic_obj(dobj=dobj) - - def get_tags_for_hostsystem(self, hostsystem_mid=None): - """ - Return list of tag object associated with host system - Args: - hostsystem_mid: Dynamic object for host system - - Returns: List of tag object associated with the given host system - - """ - dobj = DynamicID(type='HostSystem', id=hostsystem_mid) - return self.get_tags_for_dynamic_obj(dobj=dobj) - - def get_tags_for_vm(self, vm_mid=None): - """ - Return list of tag object associated with virtual machine - Args: - vm_mid: Dynamic object for virtual machine - - Returns: List of tag object associated with the given virtual machine - - """ - dobj = DynamicID(type='VirtualMachine', id=vm_mid) - return self.get_tags_for_dynamic_obj(dobj=dobj) - - def get_vm_tags(self, tag_service=None, tag_association_svc=None, vm_mid=None): - """ - Return list of tag name associated with virtual machine - Args: - tag_service: Tag service object - tag_association_svc: Tag association object - vm_mid: Dynamic object for virtual machine - - Returns: List of tag names associated with the given virtual machine - - """ - # This API returns just names of tags - # Please use get_tags_for_vm for more tag object details - tags = [] - if vm_mid is None: - return tags - - temp_tags_model = self.get_tags_for_object( - tag_service=tag_service, - tag_assoc_svc=tag_association_svc, - dobj=vm_mid - ) - - for tag_obj in temp_tags_model: - tags.append(tag_obj.name) - - return tags - def get_library_item_by_name(self, name): """ Returns the identifier of the library item with the given name. @@ -355,6 +184,18 @@ def get_library_item_by_name(self, name): item_id = item_ids[0] if item_ids else None return item_id + def get_library_by_name(self, name): + """ + Returns the identifier of the library given by library name. + Args: + name (str): The name of the lubrary + Returns: + str: The library ID or None if the library is not found + """ + cl_find_spec = self.api_client.content.Library.FindSpec(name=name) + cl_item_ids = self.api_client.content.Library.find(cl_find_spec) + return cl_item_ids[0] if cl_item_ids else None + def get_library_item_from_content_library_name(self, name, content_library_name): """ Returns the identifier of the library item with the given name in the specified @@ -365,9 +206,7 @@ def get_library_item_from_content_library_name(self, name, content_library_name) Returns: str: The item ID or None if the item is not found """ - cl_find_spec = self.api_client.content.Library.FindSpec(name=content_library_name) - cl_item_ids = self.api_client.content.Library.find(cl_find_spec) - cl_item_id = cl_item_ids[0] if cl_item_ids else None + cl_item_id = self.get_library_by_name(content_library_name) if cl_item_id: find_spec = Item.FindSpec(name=name, library_id=cl_item_id) item_ids = self.api_client.content.library.Item.find(find_spec) @@ -381,178 +220,120 @@ 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. """ + if datacenter_name is None: + return None + 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 + return datacenter_summaries[0].datacenter if len(datacenter_summaries) > 0 else None - def get_folder_by_name(self, datacenter_name, folder_name): + def get_datacenters_set_by_name(self, datacenter_name): + datacenter = self.get_datacenter_by_name(datacenter_name) + return set([datacenter]) if datacenter else set() + + def get_folder_by_name(self, folder_name, datacenter_name=None): """ Returns the identifier of a folder with the mentioned names. """ - datacenter = self.get_datacenter_by_name(datacenter_name) - if not datacenter: + if folder_name is None: return None + datacenters = self.get_datacenters_set_by_name(datacenter_name) filter_spec = Folder.FilterSpec(type=Folder.Type.VIRTUAL_MACHINE, names=set([folder_name]), - datacenters=set([datacenter])) + datacenters=datacenters) folder_summaries = self.api_client.vcenter.Folder.list(filter_spec) - folder = folder_summaries[0].folder if len(folder_summaries) > 0 else None - return folder + return folder_summaries[0].folder if len(folder_summaries) > 0 else None - def get_resource_pool_by_name(self, datacenter_name, resourcepool_name, cluster_name=None, host_name=None): + def get_resource_pool_by_name(self, resourcepool_name, datacenter_name=None, cluster_name=None, host_name=None): """ Returns the identifier of a resource pool with the mentioned names. """ - datacenter = self.get_datacenter_by_name(datacenter_name) - if not datacenter: - return None + datacenters = self.get_datacenters_set_by_name(datacenter_name) clusters = None if cluster_name: - clusters = self.get_cluster_by_name(datacenter_name, cluster_name) + clusters = self.get_cluster_by_name(cluster_name, datacenter_name) if clusters: clusters = set([clusters]) hosts = None if host_name: - hosts = self.get_host_by_name(datacenter_name, host_name) + hosts = self.get_host_by_name(host_name, datacenter_name) if hosts: hosts = set([hosts]) names = set([resourcepool_name]) if resourcepool_name else None - filter_spec = ResourcePool.FilterSpec(datacenters=set([datacenter]), + filter_spec = ResourcePool.FilterSpec(datacenters=datacenters, names=names, clusters=clusters) 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): + def get_cluster_by_name(self, cluster_name, datacenter_name=None): """ Returns the identifier of a cluster with the mentioned names. """ - datacenter = self.get_datacenter_by_name(datacenter_name) - if not datacenter: - return None + datacenters = self.get_datacenters_set_by_name(datacenter_name) names = set([cluster_name]) if cluster_name else None - filter_spec = Cluster.FilterSpec(datacenters=set([datacenter]), - names=names) + filter_spec = Cluster.FilterSpec(datacenters=datacenters, 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 + return cluster_summaries[0].cluster if len(cluster_summaries) > 0 else None - def get_host_by_name(self, datacenter_name, host_name): + def get_host_by_name(self, host_name, datacenter_name=None): """ Returns the identifier of a Host with the mentioned names. """ - datacenter = self.get_datacenter_by_name(datacenter_name) - if not datacenter: - return None + datacenters = self.get_datacenters_set_by_name(datacenter_name) names = set([host_name]) if host_name else None - filter_spec = Host.FilterSpec(datacenters=set([datacenter]), - names=names) + filter_spec = Host.FilterSpec(datacenters=datacenters, 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 + return host_summaries[0].host if len(host_summaries) > 0 else None - @staticmethod - def search_svc_object_by_name(service, svc_obj_name=None): + def get_vm_by_name(self, vm_name, datacenter_name=None): """ - Return service object by name - Args: - service: Service object - svc_obj_name: Name of service object to find - - Returns: Service object if found else None - + Returns the identifier of a VM with the mentioned names. """ - if not svc_obj_name: - return None + datacenters = self.get_datacenters_set_by_name(datacenter_name) + names = set([vm_name]) if vm_name else None + filter_spec = VM.FilterSpec(datacenters=datacenters, names=names) + vm_summaries = self.api_client.vcenter.VM.list(filter_spec) + return vm_summaries[0].vm if len(vm_summaries) > 0 else None - for svc_object in service.list(): - svc_obj = service.get(svc_object) - if svc_obj.name == svc_obj_name: - return svc_obj - return None - - def get_tag_by_name(self, tag_name=None): + def obj_to_dict(self, vmware_obj, r): """ - Return tag object by name + Tranform VMware SDK object to dictionary. Args: - tag_name: Name of tag - - Returns: Tag object if found else None - """ - if not tag_name: - return None - - return self.search_svc_object_by_name(service=self.api_client.tagging.Tag, svc_obj_name=tag_name) - - def get_category_by_name(self, category_name=None): - """ - Return category object by name - Args: - category_name: Name of category - - Returns: Category object if found else None - """ - if not category_name: - return None - - return self.search_svc_object_by_name(service=self.api_client.tagging.Category, svc_obj_name=category_name) - - def get_tag_by_category_id(self, tag_name=None, category_id=None): - """ - Return tag object by category id - Args: - tag_name: Name of tag - category_id: Id of category - Returns: Tag object if found else None - """ - if tag_name is None: - return None - - if category_id is None: - return self.search_svc_object_by_name(service=self.api_client.tagging.Tag, svc_obj_name=tag_name) - - result = None - for tag_id in self.api_client.tagging.Tag.list_tags_for_category(category_id): - tag_obj = self.api_client.tagging.Tag.get(tag_id) - if tag_obj.name == tag_name: - result = tag_obj - break - - return result - - def get_tag_by_category_name(self, tag_name=None, category_name=None): - """ - Return tag object by category name - Args: - tag_name: Name of tag - category_id: Id of category - Returns: Tag object if found else None - """ - category_id = None - if category_name is not None: - category_obj = self.get_category_by_name(category_name=category_name) - if category_obj is not None: - category_id = category_obj.id - - return self.get_tag_by_category_id(tag_name=tag_name, category_id=category_id) + vmware_obj: Object to transform. + r: Dictionary to fill with object data. + """ + for k, v in vars(vmware_obj).items(): + if not k.startswith('_'): + if hasattr(v, '__dict__') and not isinstance(v, str): + self.obj_to_dict(v, r[k]) + elif isinstance(v, int): + r[k] = int(v) + else: + r[k] = str(v) + + def set_param(self, param, cmp_fn, set_fn): + """ + Since most of the check is similar to do. This method implement + generic call for most of the parameters. It checks if parameter + specified is different to one which is currently set and if yes, + it will update it. + + param: AnsibleModule parameter name + cmp_fn: function that compares the parameter value to any API call + set_fn: function that is called if the cmd_fn is true + """ + generic_param = self.params.get(param) + if generic_param is None: + return + + if cmp_fn(generic_param): + self.changed = True + if not self.module.check_mode: + set_fn(generic_param) + self.info[param] = generic_param diff --git a/plugins/modules/content_template.py b/plugins/modules/content_template.py new file mode 100644 index 00000000..d473cda9 --- /dev/null +++ b/plugins/modules/content_template.py @@ -0,0 +1,235 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2019, Ansible Project +# Copyright: (c) 2019, Pavan Bidkar +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: content_template +short_description: Manage template in content library from virtual machine. +description: +- Module to manage template in content library from virtual machine. +- Content Library feature is introduced in vSphere 6.0 version. +- This module does not work with vSphere version older than 67U2. +author: +- Ansible Cloud Team (@ansible-collections) +requirements: +- vSphere Automation SDK +options: + template: + description: + - The name of template to manage. + type: str + required: true + library: + description: + - The name of the content library where the template will be created. + type: str + required: true + vm_name: + description: + - The name of the VM to be used to create template. + type: str + required: true + host: + description: + - Host onto which the virtual machine template should be placed. + - If O(host) and O(resource_pool) are both specified, O(resource_pool) + must belong to O(host). + - If O(host) and O(cluster) are both specified, O(host) must be a member of O(cluster). + - This attribute was added in vSphere API 6.8. + type: str + resource_pool: + description: + - Resource pool into which the virtual machine template should be placed. + - This attribute was added in vSphere API 6.8. + - If not specified, the system will attempt to choose a suitable resource pool + for the virtual machine template; if a resource pool cannot be + chosen, the library item creation operation will fail. + type: str + cluster: + description: + - Cluster onto which the virtual machine template should be placed. + - If O(cluster) and O(resource_pool) are both specified, + O(resource_pool) must belong to O(cluster). + - If O(cluster) and O(host) are both specified, O(host) must be a member of O(cluster). + - This attribute was added in vSphere API 6.8. + type: str + folder: + description: + - Virtual machine folder into which the virtual machine template should be placed. + - This attribute was added in vSphere API 6.8. + - If not specified, the virtual machine template will be placed in the same + folder as the source virtual machine. + type: str + state: + description: + - State of the template in content library. + - If C(present), the template will be created in content library. + - If C(absent), the template will be deleted from content library. + type: str + default: present + choices: + - present + - absent +extends_documentation_fragment: +- vmware.vmware.vmware_rest_client.documentation +''' + +EXAMPLES = r''' +- name: Create template in content library from Virtual Machine + vmware.vmware.content_template: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + template: mytemplate + library: mylibrary + vm_name: myvm + host: myhost +''' + +RETURN = r''' +template_info: + description: Template creation message and template_id + returned: on success + type: dict + sample: { + "msg": "Template 'mytemplate'.", + "template_id": "template-1009" + } +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vmware.vmware.plugins.module_utils.vmware_rest_client import VmwareRestClient +from ansible.module_utils._text import to_native + +HAS_VAUTOMATION_PYTHON_SDK = False +try: + from com.vmware.vcenter.vm_template_client import LibraryItems + from com.vmware.vapi.std.errors_client import Error + HAS_VAUTOMATION_PYTHON_SDK = True +except ImportError: + pass + + +class VmwareContentTemplate(VmwareRestClient): + def __init__(self, module): + """Constructor.""" + super(VmwareContentTemplate, self).__init__(module) + + # Initialize member variables + self.module = module + self._template_service = self.api_client.vcenter.vm_template.LibraryItems + self.result = {'changed': False} + + # Get parameters + self.template = self.params.get('template') + self.host = self.params.get('host') + self.cluster = self.params.get('cluster') + self.resource_pool = self.params.get('resource_pool') + self.library = self.params.get('library') + self.folder = self.params.get('folder') + self.vm_name = self.params.get('vm_name') + + def create_template_from_vm(self): + template = self.get_library_item_from_content_library_name(self.template, self.library) + if template: + self.result['template_info'] = dict( + msg="Template '%s' already exists." % self.template, + template_id=template, + ) + return + + # Create template placement specs + placement_spec = LibraryItems.CreatePlacementSpec() + placement_spec.host = self.get_host_by_name(self.host) + placement_spec.resource_pool = self.get_resource_pool_by_name(self.resource_pool) + placement_spec.cluster = self.get_cluster_by_name(self.cluster) + placement_spec.folder = self.get_folder_by_name(self.folder) + create_spec = LibraryItems.CreateSpec( + name=self.template, + placement=placement_spec, + library=self.get_library_by_name(self.library), + source_vm=self.get_vm_by_name(self.vm_name), + ) + template_id = '' + try: + template_id = self._template_service.create(create_spec) + except Error as error: + self.module.fail_json(msg="%s" % self.get_error_message(error)) + except Exception as err: + self.module.fail_json(msg="%s" % to_native(err)) + + if not template_id: + self.result['template_info'] = dict( + msg="Template creation failed", + ) + self.module.fail_json(**self.result) + self.result['changed'] = True + self.result['template_info'] = dict( + msg="Template '%s'." % self.template, + template_id=template_id, + ) + + def delete_template(self): + template = self.get_library_item_from_content_library_name(self.template, self.library) + if template is None: + self.result['template_info'] = dict( + msg="Template '%s' doesn't exists." % self.template, + ) + return + + try: + self.api_client.content.library.Item.delete(template) + except Exception as err: + self.module.fail_json(msg="%s" % to_native(err)) + + self.result['changed'] = True + self.result['template_info'] = dict( + msg="Template '%s' has been deleted." % self.template, + template_id=template, + ) + +def main(): + argument_spec = VmwareRestClient.vmware_client_argument_spec() + argument_spec.update( + template=dict(type='str', required=True), + library=dict(type='str', required=True), + vm_name=dict(type='str', required=True), + host=dict(type='str'), + cluster=dict(type='str'), + resource_pool=dict(type='str'), + folder=dict(type='str'), + state=dict(type='str', default='present', choices=['present', 'absent']), + ) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_one_of=[('host', 'resource_pool', 'cluster')], + ) + + result = {'failed': False, 'changed': False} + vmware_contentlib = VmwareContentTemplate(module) + if module.check_mode: + result.update( + vm_name=module.params['name'], + changed=True, + desired_operation='{} template'.format(module.params.get('state')), + ) + module.exit_json(**result) + if module.params.get('state') == 'present': + vmware_contentlib.create_template_from_vm() + else: + vmware_contentlib.delete_template() + module.exit_json(**vmware_contentlib.result) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/content_create_template/mock.json b/tests/integration/targets/content_create_template/mock.json new file mode 100644 index 00000000..e523e323 --- /dev/null +++ b/tests/integration/targets/content_create_template/mock.json @@ -0,0 +1,74 @@ +[ +{ + "httpRequest": { + "method": "POST", + "path": "/rest/com/vmware/cis/session" + }, + "httpResponse": { + "statusCode": 200, + "body": {"value": "72300ca9ff16c5743fa0a6328c8570ce"} + } +}, +{ + "httpRequest": { + "method": "POST", + "path": "/api" + }, + "httpResponse": { + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": {"jsonrpc": "2.0", "result": {"output": 0}, "id": "0"} + } +}, +{ + "httpRequest": { + "method": "POST", + "path": "/api", + "body": { + "type": "JSON", + "matchType": "PARTIAL", + "json": "{\"params\":{\"serviceId\":\"com.vmware.content.library\",\"operationId\":\"find\"}}" + } + }, + "httpResponseTemplate": { + "template": "{\"statusCode\": 200, \"headers\": {\"Content-type\": \"application/json\"}, \"body\": {\"jsonrpc\": \"2.0\", \"result\": {\"output\":[\"11d399c9-92f6-49cd-abaf-ad7e4fdceb49\"]}, 'id': '${json.parse($!request.body)['id']}'}}", + "templateType": "VELOCITY" + }, + "priority": 1 +}, +{ + "httpRequest": { + "method": "POST", + "path": "/api", + "body": { + "type": "JSON", + "matchType": "PARTIAL", + "json": "{\"params\":{\"serviceId\":\"com.vmware.content.library.item\",\"operationId\":\"find\"}}" + } + }, + "httpResponseTemplate": { + "template": "{\"statusCode\": 200, \"headers\": {\"Content-type\": \"application/json\"}, \"body\": {\"jsonrpc\": \"2.0\", \"result\": {\"output\":[\"11d399c9-92f6-49cd-abaf-ad7e4fdceb49\"]}, 'id': '${json.parse($!request.body)['id']}'}}", + "templateType": "VELOCITY" + }, + "priority": 1 +}, +{ + "httpRequest": { + "method": "POST", + "path": "/api", + "body": { + "params" : { + "serviceId" : "com.vmware.cis.session", + "operationId" : "delete" + } + } + }, + "httpResponseTemplate": { + "template": "{\"statusCode\": 200, \"headers\": {\"Content-type\": \"application/json\"}, \"body\": {\"jsonrpc\": \"2.0\", \"result\": {\"output\": 0}, \"id\": '${json.parse($!request.body)['id']}'}}", + "templateType": "VELOCITY" + }, + "priority": 1 +} +] diff --git a/tests/integration/targets/content_create_template/run.yml b/tests/integration/targets/content_create_template/run.yml new file mode 100644 index 00000000..82978430 --- /dev/null +++ b/tests/integration/targets/content_create_template/run.yml @@ -0,0 +1,12 @@ +- hosts: localhost + gather_facts: no + vars_files: + - vars.yml + tasks: + - name: Prepare rest + ansible.builtin.import_role: + name: prepare_rest + + - name: Import content_create_template role + ansible.builtin.import_role: + name: content_create_template diff --git a/tests/integration/targets/content_create_template/runme.sh b/tests/integration/targets/content_create_template/runme.sh new file mode 100755 index 00000000..a4c36631 --- /dev/null +++ b/tests/integration/targets/content_create_template/runme.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +source ../init.sh +exec ansible-playbook run.yml diff --git a/tests/integration/targets/content_create_template/tasks/main.yml b/tests/integration/targets/content_create_template/tasks/main.yml new file mode 100644 index 00000000..14c4462f --- /dev/null +++ b/tests/integration/targets/content_create_template/tasks/main.yml @@ -0,0 +1,19 @@ +--- +- name: Create template from vm in content library + vmware.vmware.content_create_template: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + port: "{{ vcenter_port }}" + template: mytemplate + library: templates + vm_name: test + host: 1.2.3.4. + register: __res + +- name: Assert values + ansible.builtin.assert: + that: + - __res.changed == False + - __res.template_create_info.msg == "Template 'mytemplate' already exists." diff --git a/tests/integration/targets/content_create_template/vars.yml b/tests/integration/targets/content_create_template/vars.yml new file mode 100644 index 00000000..58b3bc0c --- /dev/null +++ b/tests/integration/targets/content_create_template/vars.yml @@ -0,0 +1,6 @@ +vcenter_hostname: "127.0.0.1" +vcenter_username: "user" +vcenter_password: "pass" +vcenter_port: 1080 + +mock_file: "content_create_template"