diff --git a/meta/runtime.yml b/meta/runtime.yml index e8821255..307f59e9 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -1,2 +1,2 @@ --- -requires_ansible: '>=2.11.0' # Use '>=2.9.10' instead, if needed +requires_ansible: '>=2.11.0' \ No newline at end of file diff --git a/plugins/modules/vmware_guest.py b/plugins/modules/vmware_guest.py new file mode 100644 index 00000000..0805be3d --- /dev/null +++ b/plugins/modules/vmware_guest.py @@ -0,0 +1,3632 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# This module is also sponsored by E.T.A.I. (www.etai.fr) +# 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: vmware_guest +short_description: Manages virtual machines in vCenter +description: > + This module can be used to create new virtual machines from templates or other virtual machines, + manage power state of virtual machine such as power on, power off, suspend, shutdown, reboot, restart etc., + modify various virtual machine components like network, disk, customization etc., + rename a virtual machine and remove a virtual machine with associated components. +author: +- Loic Blot (@nerzhul) +- Philippe Dellaert (@pdellaert) +- Abhijeet Kasurde (@Akasurde) +notes: + - Please make sure that the user used for M(vmware.vmware.vmware_guest) has the correct level of privileges. + - For example, following is the list of minimum privileges required by users to create virtual machines. + - " DataStore > Allocate Space" + - " Virtual Machine > Configuration > Add New Disk" + - " Virtual Machine > Configuration > Add or Remove Device" + - " Virtual Machine > Inventory > Create New" + - " Network > Assign Network" + - " Resource > Assign Virtual Machine to Resource Pool" + - "Module may require additional privileges as well, which may be required for gathering facts - e.g. ESXi configurations." + - Use SCSI disks instead of IDE when you want to expand online disks by specifying a SCSI controller. + - Uses SysPrep for Windows VM (depends on 'guest_id' parameter match 'win') with PyVmomi. + - In order to change the VM's parameters (e.g. number of CPUs), the VM must be powered off unless the hot-add + support is enabled and the O(state=present) must be used to apply the changes. + - "For additional information please visit Ansible VMware community wiki - U(https://github.com/ansible/community/wiki/VMware)." +options: + state: + description: + - Specify the state the virtual machine should be in. + - If V(present) and virtual machine exists, ensure the virtual machine configurations conforms to task arguments. + - If V(absent) and virtual machine exists, then the specified virtual machine is removed with it's associated components. + - If set to one of V(poweredon), V(powered-on), V(poweredoff), V(powered-off), + V(present), V(restarted), V(suspended) and virtual machine does not exists, virtual machine is deployed with the given parameters. + - If set to V(poweredon) or V(powered-on) and virtual machine exists with powerstate other than powered on, + then the specified virtual machine is powered on. + - If set to V(poweredoff) or V(powered-off) and virtual machine exists with powerstate other than powered off, + then the specified virtual machine is powered off. + - If set to V(restarted) and virtual machine exists, then the virtual machine is restarted. + - If set to V(suspended) and virtual machine exists, then the virtual machine is set to suspended mode. + - If set to V(shutdownguest) or V(shutdown-guest) and virtual machine exists, then the virtual machine is shutdown. + - If set to V(rebootguest) or V(reboot-guest) and virtual machine exists, then the virtual machine is rebooted. + default: present + type: str + choices: [ absent, poweredon, powered-on, poweredoff, powered-off, present, rebootguest, reboot-guest, restarted, suspended, shutdownguest, shutdown-guest] + name: + description: + - Name of the virtual machine to work with. + - Virtual machine names in vCenter are not necessarily unique, which may be problematic, see O(name_match). + - If multiple virtual machines with same name exists, then O(folder) is required parameter to + identify uniqueness of the virtual machine. + - This parameter is required, if O(state=poweredon), O(state=powered-on), O(state=poweredoff), O(state=powered-off), + O(state=present), O(state=restarted), O(state=suspended) and virtual machine does not exists. + type: str + name_match: + description: + - If multiple virtual machines matching the name, use the first or last found. + default: 'first' + choices: [ 'first', 'last' ] + type: str + uuid: + description: + - UUID of the virtual machine to manage if known, this is VMware's unique identifier. + - This is required if O(name) is not supplied. + - If virtual machine does not exists, then this parameter is ignored. + - Please note that a supplied UUID will be ignored on virtual machine creation, as VMware creates the UUID internally. + type: str + use_instance_uuid: + description: + - Whether to use the VMware instance UUID rather than the BIOS UUID. + default: false + type: bool + template: + description: + - Template or existing virtual machine used to create new virtual machine. + - If this value is not set, virtual machine is created without using a template. + - If the virtual machine already exists, this parameter will be ignored. + - From version 2.8 onwards, absolute path to virtual machine or template can be used. + aliases: [ 'template_src' ] + type: str + is_template: + description: + - Flag the instance as a template. + - This will mark the given virtual machine as template. + - Note, this may need to be done in a dedicated task invocation that is not making + any other changes. For example, user cannot change the state from powered-on to + powered-off AND save as template in the same task. + - See M(vmware.vmware.vmware_guest) source for more details. + default: false + type: bool + folder: + description: + - Destination folder, absolute path to find an existing guest or create the new guest. + - "The folder should include the datacenter. ESXi's datacenter is ha-datacenter." + - 'If multiple machines are found with same name, this parameter is used to identify' + - 'uniqueness of the virtual machine.' + - 'Examples:' + - ' folder: /ha-datacenter/vm' + - ' folder: ha-datacenter/vm' + - ' folder: /datacenter1/vm' + - ' folder: datacenter1/vm' + - ' folder: /datacenter1/vm/folder1' + - ' folder: datacenter1/vm/folder1' + - ' folder: /folder1/datacenter1/vm' + - ' folder: folder1/datacenter1/vm' + - ' folder: /folder1/datacenter1/vm/folder2' + type: str + hardware: + type: dict + default: {} + description: + - "Manage virtual machine's hardware attributes." + suboptions: + hotadd_cpu: + type: bool + description: Allow virtual CPUs to be added while the virtual machine is running. + hotremove_cpu: + type: bool + description: Allow virtual CPUs to be removed while the virtual machine is running. + hotadd_memory: + type: bool + description: Allow memory to be added while the virtual machine is running. + memory_mb: + type: int + description: Amount of memory in MB. + num_cpus: + type: int + description: + - Number of CPUs. + - Must be a multiple of O(hardware.num_cpu_cores_per_socket). + - For example, to create a VM with 2 sockets of 4 cores, specify O(hardware.num_cpus) as 8 and O(hardware.num_cpu_cores_per_socket) as 4. + num_cpu_cores_per_socket: + type: int + description: Number of Cores Per Socket. + cpu_shares_level: + type: str + choices: [ 'low', 'normal', 'high', 'custom' ] + description: + - The allocation level of CPU resources for the virtual machine. + version_added: '3.2.0' + cpu_shares: + type: int + description: + - The number of shares of CPU allocated to this virtual machine + - cpu_shares_level will automatically be set to 'custom' + version_added: '3.2.0' + vpmc_enabled: + version_added: '3.2.0' + type: bool + description: Enable virtual CPU Performance Counters. + scsi: + type: str + description: + - Valid values are V(buslogic), V(lsilogic), V(lsilogicsas) and V(paravirtual). + - V(paravirtual) is default. + choices: [ 'buslogic', 'lsilogic', 'lsilogicsas', 'paravirtual' ] + secure_boot: + type: bool + description: Whether to enable or disable (U)EFI secure boot. + memory_reservation_lock: + type: bool + description: + - If set V(true), memory resource reservation for the virtual machine. + max_connections: + type: int + description: + - Maximum number of active remote display connections for the virtual machines. + mem_limit: + type: int + description: + - The memory utilization of a virtual machine will not exceed this limit. + - Unit is MB. + mem_reservation: + type: int + description: The amount of memory resource that is guaranteed available to the virtual machine. + aliases: [ 'memory_reservation' ] + mem_shares_level: + type: str + description: + - The allocation level of memory resources for the virtual machine. + choices: [ 'low', 'normal', 'high', 'custom' ] + version_added: '3.2.0' + mem_shares: + type: int + description: + - The number of shares of memory allocated to this virtual machine + - mem_shares_level will automatically be set to 'custom' + version_added: '3.2.0' + cpu_limit: + type: int + description: + - The CPU utilization of a virtual machine will not exceed this limit. + - Unit is MHz. + cpu_reservation: + type: int + description: The amount of CPU resource that is guaranteed available to the virtual machine. + version: + type: str + description: + - The Virtual machine hardware versions. + - Default is 10 (ESXi 5.5 and onwards). + - If set to V(latest), the specified virtual machine will be upgraded to the most current hardware version supported on the host. + - Please check VMware documentation for correct virtual machine hardware version. + - Incorrect hardware version may lead to failure in deployment. If hardware version is already equal to the given. + boot_firmware: + type: str + description: Choose which firmware should be used to boot the virtual machine. + choices: [ 'bios', 'efi' ] + nested_virt: + type: bool + description: + - Enable nested virtualization. + virt_based_security: + type: bool + description: + - Enable Virtualization Based Security feature for Windows on ESXi 6.7 and later, from hardware version 14. + - Supported Guest OS are Windows 10 64 bit, Windows Server 2016, Windows Server 2019 and later. + - The firmware of virtual machine must be EFI and secure boot must be enabled. + - Virtualization Based Security depends on nested virtualization and Intel Virtualization Technology for Directed I/O. + - Deploy on unsupported ESXi, hardware version or firmware may lead to failure or deployed VM with unexpected configurations. + iommu: + type: bool + description: Flag to specify if I/O MMU is enabled for this virtual machine. + encryption: + type: dict + default: {} + description: + - Manage virtual machine encryption settings + version_added: '3.9.0' + suboptions: + encrypted_vmotion: + type: str + description: Controls encryption for live migrations with vmotion + choices: ['disabled', 'opportunistic', 'required'] + encrypted_ft: + type: str + description: Controls encryption for fault tolerance replication + choices: ['disabled', 'opportunistic', 'required'] + guest_id: + type: str + description: + - Set the guest ID. + - This field is required when creating a virtual machine, not required when creating from the template. + - > + Valid values are referenced here: + U(https://code.vmware.com/apis/358/doc/vim.vm.GuestOsDescriptor.GuestOsIdentifier.html) + disk: + description: + - A list of disks to add. + - Shrinking disks is not supported. + - Removing existing disks of the virtual machine is not supported. + - 'Attributes O(disk.controller_type), O(disk.controller_number), O(disk.unit_number) are used to configure multiple types of disk + controllers and disks for creating or reconfiguring virtual machine.' + type: list + default: [] + elements: dict + suboptions: + size: + description: + - Disk storage size. + - Please specify storage unit like [kb, mb, gb, tb]. + type: str + size_kb: + description: Disk storage size in kb. + type: int + size_mb: + description: Disk storage size in mb. + type: int + size_gb: + description: Disk storage size in gb. + type: int + size_tb: + description: Disk storage size in tb. + type: int + type: + description: + - Type of disk. + - If not specified, disk type is inherited from the source VM or template when cloned and thick disk, no eagerzero otherwise. + type: str + choices: [ 'thin', 'thick', 'eagerzeroedthick' ] + datastore: + type: str + description: + - The name of datastore which will be used for the disk. + - If O(disk.autoselect_datastore) is set to True, will select the less used datastore whose name contains this "disk.datastore" string. + filename: + type: str + description: + - Existing disk image to be used. + - Filename must already exist on the datastore. + - Specify filename string in C([datastore_name] path/to/file.vmdk) format. + autoselect_datastore: + type: bool + description: + - Select the less used datastore. + - O(disk.datastore) and O(disk.autoselect_datastore) will not be used if O(datastore) is specified outside this O(disk) configuration. + disk_mode: + type: str + choices: ['persistent', 'independent_persistent', 'independent_nonpersistent'] + description: + - Type of disk mode. + - If V(persistent) specified, changes are immediately and permanently written to the virtual disk. This is default. + - If V(independent_persistent) specified, same as persistent, but not affected by snapshots. + - If V(independent_nonpersistent) specified, changes to virtual disk are made to a redo log and discarded at power off, + but not affected by snapshots. + controller_type: + type: str + choices: ['buslogic', 'lsilogic', 'lsilogicsas', 'paravirtual', 'sata', 'nvme'] + description: + - Type of disk controller. + Set this type on not supported ESXi or VM hardware version will lead to failure in deployment. + - When set to V(sata), please make sure O(disk.unit_number) is correct and not used by SATA CDROMs. + - If set to V(sata) type, please make sure O(disk.controller_number) and O(disk.unit_number) are set correctly when O(cdrom=sata). + controller_number: + type: int + choices: [0, 1, 2, 3] + description: + - Disk controller bus number. + - The maximum number of same type controller is 4 per VM. + unit_number: + type: int + description: + - Disk Unit Number. + - Valid value range from 0 to 15 for SCSI controller, except 7. + - Valid value range from 0 to 14 for NVME controller. + - Valid value range from 0 to 29 for SATA controller. + - O(disk.controller_type), O(disk.controller_number) and O(disk.unit_number) are required when creating or reconfiguring VMs + with multiple types of disk controllers and disks. + - When creating new VM, the first configured disk in the O(disk) list will be "Hard Disk 1". + nvdimm: + description: + - Add or remove a virtual NVDIMM device to the virtual machine. + - VM virtual hardware version must be 14 or higher on vSphere 6.7 or later. + - Verify that guest OS of the virtual machine supports PMem before adding virtual NVDIMM device. + - Verify that you have the I(Datastore.Allocate) space privilege on the virtual machine. + - Make sure that the host or the cluster on which the virtual machine resides has available PMem resources. + - To add or remove virtual NVDIMM device to the existing virtual machine, it must be in power off state. + type: dict + default: {} + suboptions: + state: + type: str + description: + - If set to V(absent), then the NVDIMM device with specified O(nvdimm.label) will be removed. + choices: ['present', 'absent'] + size_mb: + type: int + description: Virtual NVDIMM device size in MB. + default: 1024 + label: + type: str + description: + - The label of the virtual NVDIMM device to be removed or configured, e.g., "NVDIMM 1". + - 'This parameter is required when O(nvdimm.state=absent), or O(nvdimm.state=present) to reconfigure NVDIMM device + size. When add a new device, please do not set.' + cdrom: + description: + - A list of CD-ROM configurations for the virtual machine. + - For V(ide) controller, hot-add or hot-remove CD-ROM is not supported. + type: list + default: [] + elements: dict + suboptions: + type: + type: str + description: + - The type of CD-ROM. + - With V(none) the CD-ROM will be disconnected but present. + choices: [ 'none', 'client', 'iso' ] + default: client + iso_path: + type: str + description: + - The datastore path to the ISO file to use, in the form of C([datastore1] path/to/file.iso). + - Required if type is set V(iso). + controller_type: + type: str + description: + - When set to V(sata), please make sure O(cdrom.unit_number) is correct and not used by SATA disks. + choices: [ 'ide', 'sata' ] + default: ide + controller_number: + type: int + description: + - For O(cdrom.controller_type=ide), valid value is 0 or 1. + - For O(cdrom.controller_type=sata), valid value is 0 to 3. + unit_number: + type: int + description: + - For O(cdrom.controller_type=ide), valid value is 0 or 1. + - For O(cdrom.controller_type=sata), valid value is 0 to 29. + - O(cdrom.controller_number) and O(cdrom.unit_number) are mandatory attributes. + state: + type: str + description: + - If set to V(absent), then the specified CD-ROM will be removed. + choices: [ 'present', 'absent' ] + default: present + resource_pool: + description: + - Use the given resource pool for virtual machine operation. + - Resource pool should be child of the selected host parent. + - When not specified I(Resources) is taken as default value. + type: str + wait_for_ip_address: + description: + - Wait until vCenter detects an IP address for the virtual machine. + - This requires vmware-tools (vmtoolsd) to properly work after creation. + - "vmware-tools needs to be installed on the given virtual machine in order to work with this parameter." + default: false + type: bool + wait_for_ip_address_timeout: + description: + - Define a timeout (in seconds) for the wait_for_ip_address parameter. + default: '300' + type: int + wait_for_customization_timeout: + description: + - Define a timeout (in seconds) for the wait_for_customization parameter. + - Be careful when setting this value since the time guest customization took may differ among guest OSes. + default: '3600' + type: int + wait_for_customization: + description: + - Wait until vCenter detects all guest customizations as successfully completed. + - When enabled, the VM will automatically be powered on. + - "If vCenter does not detect guest customization start or succeed, failed events after time + O(wait_for_customization_timeout) parameter specified, warning message will be printed and task result is fail." + default: false + type: bool + state_change_timeout: + description: + - If the O(state=shutdownguest), by default the module will return immediately after sending the shutdown signal. + - If this argument is set to a positive integer, the module will instead wait for the virtual machine to reach the poweredoff state. + - The value sets a timeout in seconds for the module to wait for the state change. + default: 0 + type: int + snapshot_src: + description: + - Name of the existing snapshot to use to create a clone of a virtual machine. + - While creating linked clone using O(linked_clone) parameter, this parameter is required. + type: str + linked_clone: + description: + - Whether to create a linked clone from the snapshot specified. + - If specified, then O(snapshot_src) is required parameter. + default: false + type: bool + force: + description: + - Ignore warnings and complete the actions. + - This parameter is useful while removing virtual machine which is powered on state. + - 'This module reflects the VMware vCenter API and UI workflow, as such, in some cases the `force` flag will + be mandatory to perform the action to ensure you are certain the action has to be taken, no matter what the consequence. + This is specifically the case for removing a powered on the virtual machine when O(state=absent).' + default: false + type: bool + delete_from_inventory: + description: + - Whether to delete Virtual machine from inventory or delete from disk. + default: false + type: bool + datacenter: + description: + - Destination datacenter for the deploy operation. + default: ha-datacenter + type: str + cluster: + description: + - The cluster name where the virtual machine will run. + - This is a required parameter, if O(esxi_hostname) is not set. + - O(esxi_hostname) and O(cluster) are mutually exclusive parameters. + type: str + esxi_hostname: + description: + - The ESXi hostname where the virtual machine will run. + - This is a required parameter, if O(cluster) is not set. + - O(esxi_hostname) and O(cluster) are mutually exclusive parameters. + type: str + advanced_settings: + description: + - Define a list of advanced settings to be added to the VMX config. + - An advanced settings object takes the two fields C(key) and C(value). + - Incorrect key and values will be ignored. + elements: dict + type: list + default: [] + annotation: + description: + - A note or annotation to include in the virtual machine. + type: str + aliases: [ 'notes' ] + customvalues: + description: + - Define a list of custom values to set on virtual machine. + - A custom value object takes the two fields C(key) and C(value). + - Incorrect key and values will be ignored. + elements: dict + type: list + default: [] + networks: + description: + - A list of networks (in the order of the NICs). + - Removing NICs is not allowed, while reconfiguring the virtual machine. + - The I(type), I(ip), I(netmask), I(gateway), I(domain), I(dns_servers) options don't set to a guest when creating a blank new virtual machine. + They are set by the customization via vmware-tools. + If you want to set the value of the options to a guest, you need to clone from a template with installed OS and vmware-tools(also Perl when Linux). + type: list + default: [] + elements: dict + suboptions: + name: + type: str + description: + - Name of the portgroup or distributed virtual portgroup for this interface. + - Required per entry. + - When specifying distributed virtual portgroup make sure given O(esxi_hostname) or O(cluster) is associated with it. + vlan: + type: int + description: + - VLAN number for this interface. + - Required per entry. + device_type: + type: str + description: + - Virtual network device. + - Valid value can be one of C(e1000), C(e1000e), C(pcnet32), C(vmxnet2), C(vmxnet3), C(sriov). + - C(vmxnet3) is default. + - Optional per entry. + - Used for virtual hardware. + mac: + type: str + description: + - Customize MAC address. + - Optional per entry. + - Used for virtual hardware. + dvswitch_name: + type: str + description: + - Name of the distributed vSwitch. + - Optional per entry. + - Used for virtual hardware. + type: + type: str + description: + - Type of IP assignment. + - Valid values are one of C(dhcp), C(static). + - C(dhcp) is default. + - Optional per entry. + - Used for OS customization. + ip: + type: str + description: + - Static IP address. Implies C(type=static). + - Optional per entry. + - Used for OS customization. + netmask: + type: str + description: + - Static netmask required for C(ip). + - Optional per entry. + - Used for OS customization. + gateway: + type: str + description: + - Static gateway. + - Optional per entry. + - Used for OS customization. + typev6: + version_added: '4.1.0' + type: str + description: + - Type of IP assignment. + - Valid values are one of C(dhcp), C(static). + - C(dhcp) is default. + - Optional per entry. + - Used for OS customization. + ipv6: + version_added: '4.1.0' + type: str + description: + - Static IP address. Implies C(type=static). + - Optional per entry. + - Used for OS customization. + netmaskv6: + version_added: '4.1.0' + type: str + description: + - Static netmask required for C(ip). + - Optional per entry. + - Used for OS customization. + gatewayv6: + version_added: '4.1.0' + type: str + description: + - Static gateway. + - Optional per entry. + - Used for OS customization. + dns_servers: + type: str + description: + - DNS servers for this network interface (Windows). + - Optional per entry. + - Used for OS customization. + domain: + type: str + description: + - Domain name for this network interface (Windows). + - Optional per entry. + - Used for OS customization. + connected: + type: bool + description: + - Indicates whether the NIC is currently connected. + start_connected: + type: bool + description: + - Specifies whether or not to connect the device when the virtual machine starts. + customization: + description: + - Parameters for OS customization when cloning from the template or the virtual machine, or apply to the existing virtual machine directly. + - Not all operating systems are supported for customization with respective vCenter version, + please check VMware documentation for respective OS customization. + - For supported customization operating system matrix, (see U(http://partnerweb.vmware.com/programs/guestOS/guest-os-customization-matrix.pdf)) + - Linux based OSes requires Perl package to be installed for OS customizations. + suboptions: + existing_vm: + type: bool + description: + - If set to V(true), do OS customization on the specified virtual machine directly. + - Common for Linux and Windows customization. + dns_servers: + type: list + elements: str + description: + - List of DNS servers to configure. + - Common for Linux and Windows customization. + dns_suffix: + type: list + elements: str + description: + - List of domain suffixes, also known as DNS search path. + - Default C(domain) parameter. + - Common for Linux and Windows customization. + domain: + type: str + description: + - DNS domain name to use. + - Common for Linux and Windows customization. + hostname: + type: str + description: + - Computer hostname. + - Default is shortened O(name) parameter. + - Allowed characters are alphanumeric (uppercase and lowercase) and minus, rest of the characters are dropped as per RFC 952. + - Common for Linux and Windows customization. + timezone: + type: str + description: + - Timezone. + - See List of supported time zones for different vSphere versions in Linux/Unix. + - Common for Linux and Windows customization. + - L(Windows, https://msdn.microsoft.com/en-us/library/ms912391.aspx). + hwclockUTC: + type: bool + description: + - Specifies whether the hardware clock is in UTC or local time. + - Specific to Linux customization. + script_text: + type: str + description: + - Script to run with shebang. + - Needs to be enabled in vmware tools with vmware-toolbox-cmd config set deployPkg enable-custom-scripts true + - https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-9A5093A5-C54F-4502-941B-3F9C0F573A39.html + - Specific to Linux customization. + version_added: '3.1.0' + autologon: + type: bool + description: + - Auto logon after virtual machine customization. + - Specific to Windows customization. + autologoncount: + type: int + description: + - Number of autologon after reboot. + - Specific to Windows customization. + - Ignored if O(customization.autologon) is unset or set to O(customization.autologon=false). + - If unset, 1 will be used. + domainadmin: + type: str + description: + - User used to join in AD domain. + - Required if O(customization.joindomain) specified. + - Specific to Windows customization. + domainadminpassword: + type: str + description: + - Password used to join in AD domain. + - Required if O(customization.joindomain) specified. + - Specific to Windows customization. + fullname: + type: str + description: + - Server owner name. + - Specific to Windows customization. + - If unset, "Administrator" will be used as a fall-back. + joindomain: + type: str + description: + - AD domain to join. + - Not compatible with O(customization.joinworkgroup). + - Specific to Windows customization. + joinworkgroup: + type: str + description: + - Workgroup to join. + - Not compatible with O(customization.joindomain). + - Specific to Windows customization. + - If unset, "WORKGROUP" will be used as a fall-back. + orgname: + type: str + description: + - Organisation name. + - Specific to Windows customization. + - If unset, "ACME" will be used as a fall-back. + password: + type: str + description: + - Local administrator password. + - If not defined, the password will be set to blank (that is, no password). + - Specific to Windows customization. + productid: + type: str + description: + - Product ID. + - Specific to Windows customization. + runonce: + type: list + elements: str + description: + - List of commands to run at first user logon. + - Specific to Windows customization. + type: dict + default: {} + vapp_properties: + description: + - A list of vApp properties. + - 'For full list of attributes and types refer to: U(https://code.vmware.com/apis/704/vsphere/vim.vApp.PropertyInfo.html)' + type: list + default: [] + elements: dict + suboptions: + id: + type: str + description: + - Property ID. + - Required per entry. + value: + type: str + description: + - Property value. + type: + type: str + description: + - Value type, string type by default. + operation: + type: str + description: + - The C(remove) attribute is required only when removing properties. + customization_spec: + description: + - Unique name identifying the requested customization specification. + - If set, then overrides O(customization) parameter values. + type: str + datastore: + description: + - Specify datastore or datastore cluster to provision virtual machine. + - This parameter takes precedence over O(disk.datastore) parameter. + - This parameter can be used to override datastore or datastore cluster setting + of the virtual machine when deployed from the template. + - Please see example for more usage. + type: str + convert: + description: + - Specify convert disk type while cloning template or virtual machine. + choices: [ 'thin', 'thick', 'eagerzeroedthick' ] + type: str +extends_documentation_fragment: +- community.vmware.vmware.documentation + +''' + +EXAMPLES = r''' +- name: Create a virtual machine on given ESXi hostname + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: /DC1/vm/ + name: test_vm_0001 + state: poweredon + guest_id: centos64Guest + # This is hostname of particular ESXi server on which user wants VM to be deployed + esxi_hostname: "{{ esxi_hostname }}" + disk: + - size_gb: 10 + type: thin + datastore: datastore1 + hardware: + memory_mb: 512 + num_cpus: 4 + scsi: paravirtual + networks: + - name: VM Network + mac: aa:bb:dd:aa:00:14 + ip: 10.10.10.100 + netmask: 255.255.255.0 + device_type: vmxnet3 + wait_for_ip_address: true + wait_for_ip_address_timeout: 600 + delegate_to: localhost + register: deploy_vm + +- name: Create a virtual machine from a template + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: /testvms + name: testvm_2 + state: poweredon + template: template_el7 + disk: + - size_gb: 10 + type: thin + datastore: g73_datastore + # Add another disk from an existing VMDK + - filename: "[datastore1] testvms/testvm_2_1/testvm_2_1.vmdk" + hardware: + memory_mb: 512 + num_cpus: 6 + num_cpu_cores_per_socket: 3 + scsi: paravirtual + memory_reservation_lock: true + mem_limit: 8096 + mem_reservation: 4096 + cpu_shares_level: "high" + mem_shares_level: "high" + cpu_limit: 8096 + cpu_reservation: 4096 + max_connections: 5 + hotadd_cpu: true + hotremove_cpu: true + hotadd_memory: false + version: 12 # Hardware version of virtual machine + boot_firmware: "efi" + cdrom: + - controller_number: 0 + unit_number: 0 + state: present + type: iso + iso_path: "[datastore1] livecd.iso" + networks: + - name: VM Network + mac: aa:bb:dd:aa:00:14 + wait_for_ip_address: true + delegate_to: localhost + register: deploy + +- name: Clone a virtual machine from Windows template and customize + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: datacenter1 + cluster: cluster + name: testvm-2 + template: template_windows + networks: + - name: VM Network + ip: 192.168.1.100 + netmask: 255.255.255.0 + gateway: 192.168.1.1 + mac: aa:bb:dd:aa:00:14 + domain: my_domain + dns_servers: + - 192.168.1.1 + - 192.168.1.2 + - vlan: 1234 + type: dhcp + customization: + autologon: true + dns_servers: + - 192.168.1.1 + - 192.168.1.2 + domain: my_domain + password: new_vm_password + runonce: + - powershell.exe -ExecutionPolicy Unrestricted -File C:\Windows\Temp\ConfigureRemotingForAnsible.ps1 -ForceNewSSLCert -EnableCredSSP + delegate_to: localhost + +- name: Clone a virtual machine from Linux template and customize + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ datacenter }}" + state: present + folder: /DC1/vm + template: "{{ template }}" + name: "{{ vm_name }}" + cluster: DC1_C1 + networks: + - name: VM Network + ip: 192.168.10.11 + netmask: 255.255.255.0 + wait_for_ip_address: true + customization: + domain: "{{ guest_domain }}" + dns_servers: + - 8.9.9.9 + - 7.8.8.9 + dns_suffix: + - example.com + - example2.com + script_text: | + #!/bin/bash + touch /tmp/touch-from-playbook + delegate_to: localhost + +- name: Rename a virtual machine (requires the virtual machine's uuid) + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + uuid: "{{ vm_uuid }}" + name: new_name + state: present + delegate_to: localhost + +- name: Remove a virtual machine by uuid + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + uuid: "{{ vm_uuid }}" + state: absent + delegate_to: localhost + +- name: Remove a virtual machine from inventory + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: vm_name + delete_from_inventory: true + state: absent + delegate_to: localhost + +- name: Manipulate vApp properties + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: vm_name + state: present + vapp_properties: + - id: remoteIP + category: Backup + label: Backup server IP + type: string + value: 10.10.10.1 + - id: old_property + operation: remove + delegate_to: localhost + +- name: Set powerstate of a virtual machine to poweroff by using UUID + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + uuid: "{{ vm_uuid }}" + state: poweredoff + delegate_to: localhost + +- name: Deploy a virtual machine in a datastore different from the datastore of the template + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ vm_name }}" + state: present + template: "{{ template_name }}" + # Here datastore can be different which holds template + datastore: "{{ virtual_machine_datastore }}" + hardware: + memory_mb: 512 + num_cpus: 2 + scsi: paravirtual + delegate_to: localhost + +- name: Create a diskless VM + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + state: poweredoff + cluster: "{{ ccr1 }}" + name: diskless_vm + folder: /Asia-Datacenter1/vm + guest_id: centos64Guest + datastore: "{{ ds1 }}" + hardware: + memory_mb: 1024 + num_cpus: 2 + num_cpu_cores_per_socket: 1 + +- name: Create a VM with multiple disks of different disk controller types + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: /DC1/vm/ + name: test_vm_multi_disks + state: poweredoff + guest_id: centos64Guest + datastore: datastore1 + disk: + - size_gb: 10 + controller_type: 'nvme' + controller_number: 0 + unit_number: 0 + - size_gb: 10 + controller_type: 'paravirtual' + controller_number: 0 + unit_number: 1 + - size_gb: 10 + controller_type: 'sata' + controller_number: 0 + unit_number: 2 + hardware: + memory_mb: 512 + num_cpus: 4 + version: 14 + networks: + - name: VM Network + device_type: vmxnet3 + delegate_to: localhost + register: deploy_vm + +- name: Create a VM with NVDIMM device + vmware.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: /DC1/vm/ + name: test_vm_nvdimm + state: poweredoff + guest_id: centos7_64Guest + datastore: datastore1 + hardware: + memory_mb: 512 + num_cpus: 4 + version: 14 + networks: + - name: VM Network + device_type: vmxnet3 + nvdimm: + state: present + size_mb: 2048 + delegate_to: localhost + register: deploy_vm +''' + +RETURN = r''' +instance: + description: metadata about the new virtual machine + returned: always + type: dict + sample: None +''' + +import re +import time +import string + +HAS_PYVMOMI = False +try: + from pyVmomi import vim, vmodl + HAS_PYVMOMI = True +except ImportError: + pass + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.network import is_mac +from ansible.module_utils._text import to_text, to_native +from ansible_collections.vmware.vmware.plugins.module_utils.vmware import ( + find_obj, + gather_vm_facts, + get_all_objs, + compile_folder_path_for_object, + serialize_spec, + vmware_argument_spec, + set_vm_power_state, + PyVmomi, + find_dvs_by_name, + find_dvspg_by_name, + wait_for_vm_ip, + quote_obj_name, +) +from ansible_collections.vmware.vmware.plugins.module_utils.vm_device_helper import PyVmomiDeviceHelper +from ansible_collections.vmware.vmware.plugins.module_utils.vmware_spbm import SPBM + + +class PyVmomiCache(object): + """ This class caches references to objects which are requested multiples times but not modified """ + + def __init__(self, content, dc_name=None): + self.content = content + self.dc_name = dc_name + self.networks = {} + self.clusters = {} + self.esx_hosts = {} + self.parent_datacenters = {} + + def find_obj(self, content, types, name, confine_to_datacenter=True): + """ Wrapper around find_obj to set datacenter context """ + result = find_obj(content, types, name) + if result and confine_to_datacenter: + if to_text(self.get_parent_datacenter(result).name) != to_text(self.dc_name): + result = None + objects = self.get_all_objs(content, types, confine_to_datacenter=True) + for obj in objects: + if name is None or to_text(obj.name) == to_text(name): + return obj + return result + + def get_all_objs(self, content, types, confine_to_datacenter=True): + """ Wrapper around get_all_objs to set datacenter context """ + objects = get_all_objs(content, types) + if confine_to_datacenter: + if hasattr(objects, 'items'): + # resource pools come back as a dictionary + # make a copy + for k, v in tuple(objects.items()): + parent_dc = self.get_parent_datacenter(k) + if parent_dc.name != self.dc_name: + del objects[k] + else: + # everything else should be a list + objects = [x for x in objects if self.get_parent_datacenter(x).name == self.dc_name] + + return objects + + def get_network(self, network): + network = quote_obj_name(network) + + if network not in self.networks: + self.networks[network] = self.find_obj(self.content, [vim.Network], network) + + return self.networks[network] + + def get_cluster(self, cluster): + if cluster not in self.clusters: + self.clusters[cluster] = self.find_obj(self.content, [vim.ClusterComputeResource], cluster) + + return self.clusters[cluster] + + def get_esx_host(self, host): + if host not in self.esx_hosts: + self.esx_hosts[host] = self.find_obj(self.content, [vim.HostSystem], host) + + return self.esx_hosts[host] + + def get_parent_datacenter(self, obj): + """ Walk the parent tree to find the objects datacenter """ + if isinstance(obj, vim.Datacenter): + return obj + if obj in self.parent_datacenters: + return self.parent_datacenters[obj] + datacenter = None + while True: + if not hasattr(obj, 'parent'): + break + obj = obj.parent + if isinstance(obj, vim.Datacenter): + datacenter = obj + break + self.parent_datacenters[obj] = datacenter + return datacenter + + +class PyVmomiHelper(PyVmomi): + def __init__(self, module): + super(PyVmomiHelper, self).__init__(module) + self.device_helper = PyVmomiDeviceHelper(self.module) + self.configspec = None + self.relospec = None + self.change_detected = False # a change was detected and needs to be applied through reconfiguration + self.change_applied = False # a change was applied meaning at least one task succeeded + self.tracked_changes = {} # dict of changes made or would-be-made in check mode, updated when change_applied is set + self.customspec = None + self.cache = PyVmomiCache(self.content, dc_name=self.params['datacenter']) + + def gather_facts(self, vm): + return gather_vm_facts(self.content, vm) + + def remove_vm(self, vm, delete_from_inventory=False): + # https://www.vmware.com/support/developer/converter-sdk/conv60_apireference/vim.ManagedEntity.html#destroy + if vm.summary.runtime.powerState.lower() == 'poweredon': + self.module.fail_json(msg="Virtual machine %s found in 'powered on' state, " + "please use 'force' parameter to remove or poweroff VM " + "and try removing VM again." % vm.name) + # Delete VM from Inventory + if delete_from_inventory: + try: + vm.UnregisterVM() + except (vim.fault.TaskInProgress, + vmodl.RuntimeFault) as e: + return {'changed': self.change_applied, 'failed': True, 'msg': e.msg, 'op': 'UnregisterVM'} + self.change_applied = True + return {'changed': self.change_applied, 'failed': False} + # Delete VM from Disk + task = vm.Destroy() + self.wait_for_task(task) + if task.info.state == 'error': + return {'changed': self.change_applied, 'failed': True, 'msg': task.info.error.msg, 'op': 'destroy'} + else: + return {'changed': self.change_applied, 'failed': False} + + def configure_guestid(self, vm_obj, vm_creation=False): + # guest_id is not required when using templates + if self.params['template']: + return + + # guest_id is only mandatory on VM creation + if vm_creation and self.params['guest_id'] is None: + self.module.fail_json(msg="guest_id attribute is mandatory for VM creation") + + if self.params['guest_id'] and \ + (vm_obj is None or self.params['guest_id'].lower() != vm_obj.summary.config.guestId.lower()): + self.change_detected = True + self.configspec.guestId = self.params['guest_id'] + + def configure_resource_alloc_info(self, vm_obj): + """ + Function to configure resource allocation information about virtual machine + :param vm_obj: VM object in case of reconfigure, None in case of deploy + :return: None + """ + rai_change_detected = False + memory_allocation = vim.ResourceAllocationInfo() + cpu_allocation = vim.ResourceAllocationInfo() + + memory_shares_info = vim.SharesInfo() + cpu_shares_info = vim.SharesInfo() + + mem_shares_level = self.params['hardware']['mem_shares_level'] + if mem_shares_level is not None: + memory_shares_info.level = mem_shares_level + memory_allocation.shares = memory_shares_info + + if vm_obj is None or \ + memory_allocation.shares.level != vm_obj.config.memoryAllocation.shares.level: + rai_change_detected = True + + cpu_shares_level = self.params['hardware']['cpu_shares_level'] + if cpu_shares_level is not None: + cpu_shares_info.level = cpu_shares_level + cpu_allocation.shares = cpu_shares_info + if vm_obj is None or \ + cpu_allocation.shares.level != vm_obj.config.cpuAllocation.shares.level: + rai_change_detected = True + + mem_shares = self.params['hardware']['mem_shares'] + if mem_shares is not None: + memory_shares_info.level = 'custom' + memory_shares_info.shares = mem_shares + memory_allocation.shares = memory_shares_info + if vm_obj is None or \ + memory_allocation.shares != vm_obj.config.memoryAllocation.shares: + rai_change_detected = True + + cpu_shares = self.params['hardware']['cpu_shares'] + if cpu_shares is not None: + cpu_shares_info.level = 'custom' + cpu_shares_info.shares = cpu_shares + cpu_allocation.shares = cpu_shares_info + if vm_obj is None or \ + cpu_allocation.shares != vm_obj.config.cpuAllocation.shares: + rai_change_detected = True + + mem_limit = self.params['hardware']['mem_limit'] + if mem_limit is not None: + memory_allocation.limit = mem_limit + if vm_obj is None or \ + memory_allocation.limit != vm_obj.config.memoryAllocation.limit: + rai_change_detected = True + + mem_reservation = self.params['hardware']['mem_reservation'] + if mem_reservation is not None: + memory_allocation.reservation = mem_reservation + if vm_obj is None or \ + memory_allocation.reservation != vm_obj.config.memoryAllocation.reservation: + rai_change_detected = True + + cpu_limit = self.params['hardware']['cpu_limit'] + if cpu_limit is not None: + cpu_allocation.limit = cpu_limit + if vm_obj is None or \ + cpu_allocation.limit != vm_obj.config.cpuAllocation.limit: + rai_change_detected = True + + cpu_reservation = self.params['hardware']['cpu_reservation'] + if cpu_reservation is not None: + cpu_allocation.reservation = cpu_reservation + if vm_obj is None or \ + cpu_allocation.reservation != vm_obj.config.cpuAllocation.reservation: + rai_change_detected = True + + if rai_change_detected: + self.configspec.memoryAllocation = memory_allocation + self.configspec.cpuAllocation = cpu_allocation + self.change_detected = True + + def configure_cpu_and_memory(self, vm_obj, vm_creation=False): + # set cpu/memory/etc + num_cpus = self.params['hardware']['num_cpus'] + if num_cpus is not None: + # check VM power state and cpu hot-add/hot-remove state before re-config VM + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + if vm_obj and vm_obj.runtime.powerState == vim.VirtualMachinePowerState.poweredOn and not self.module.check_mode: + if not vm_obj.config.cpuHotRemoveEnabled and num_cpus < vm_obj.config.hardware.numCPU: + self.module.fail_json(msg="Configured cpu number is less than the cpu number of the VM, " + "cpuHotRemove is not enabled") + if not vm_obj.config.cpuHotAddEnabled and num_cpus > vm_obj.config.hardware.numCPU: + self.module.fail_json(msg="Configured cpu number is more than the cpu number of the VM, " + "cpuHotAdd is not enabled") + + num_cpu_cores_per_socket = self.params['hardware']['num_cpu_cores_per_socket'] + if num_cpu_cores_per_socket is not None: + if num_cpus % num_cpu_cores_per_socket != 0: + self.module.fail_json(msg="hardware.num_cpus attribute should be a multiple " + "of hardware.num_cpu_cores_per_socket") + if vm_obj is None or num_cpu_cores_per_socket != vm_obj.config.hardware.numCoresPerSocket: + self.change_detected = True + self.configspec.numCoresPerSocket = num_cpu_cores_per_socket + if vm_obj is None or num_cpus != vm_obj.config.hardware.numCPU: + self.change_detected = True + self.configspec.numCPUs = num_cpus + # num_cpu is mandatory for VM creation + elif vm_creation and not self.params['template']: + self.module.fail_json(msg="hardware.num_cpus attribute is mandatory for VM creation") + + memory_mb = self.params['hardware']['memory_mb'] + if memory_mb is not None: + # check VM power state and memory hotadd state before re-config VM + if vm_obj and vm_obj.runtime.powerState == vim.VirtualMachinePowerState.poweredOn: + if vm_obj.config.memoryHotAddEnabled and memory_mb < vm_obj.config.hardware.memoryMB: + self.module.fail_json(msg="Configured memory is less than memory size of the VM, " + "operation is not supported") + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + elif not vm_obj.config.memoryHotAddEnabled and memory_mb != vm_obj.config.hardware.memoryMB and not self.module.check_mode: + self.module.fail_json(msg="memoryHotAdd is not enabled") + if vm_obj is None or memory_mb != vm_obj.config.hardware.memoryMB: + self.change_detected = True + self.configspec.memoryMB = memory_mb + # memory_mb is mandatory for VM creation + elif vm_creation and not self.params['template']: + self.module.fail_json(msg="hardware.memory_mb attribute is mandatory for VM creation") + + hotadd_memory = self.params['hardware']['hotadd_memory'] + if hotadd_memory is not None: + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + if vm_obj and vm_obj.runtime.powerState == vim.VirtualMachinePowerState.poweredOn and \ + vm_obj.config.memoryHotAddEnabled != hotadd_memory and not self.module.check_mode: + self.module.fail_json(msg="Configure hotadd memory operation is not supported when VM is power on") + if vm_obj is None or hotadd_memory != vm_obj.config.memoryHotAddEnabled: + self.change_detected = True + self.configspec.memoryHotAddEnabled = hotadd_memory + + hotadd_cpu = self.params['hardware']['hotadd_cpu'] + if hotadd_cpu is not None: + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + if vm_obj and vm_obj.runtime.powerState == vim.VirtualMachinePowerState.poweredOn and \ + vm_obj.config.cpuHotAddEnabled != hotadd_cpu and not self.module.check_mode: + self.module.fail_json(msg="Configure hotadd cpu operation is not supported when VM is power on") + if vm_obj is None or hotadd_cpu != vm_obj.config.cpuHotAddEnabled: + self.change_detected = True + self.configspec.cpuHotAddEnabled = hotadd_cpu + + hotremove_cpu = self.params['hardware']['hotremove_cpu'] + if hotremove_cpu is not None: + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + if vm_obj and vm_obj.runtime.powerState == vim.VirtualMachinePowerState.poweredOn and \ + vm_obj.config.cpuHotRemoveEnabled != hotremove_cpu and not self.module.check_mode: + self.module.fail_json(msg="Configure hotremove cpu operation is not supported when VM is power on") + if vm_obj is None or hotremove_cpu != vm_obj.config.cpuHotRemoveEnabled: + self.change_detected = True + self.configspec.cpuHotRemoveEnabled = hotremove_cpu + + memory_reservation_lock = self.params['hardware']['memory_reservation_lock'] + if memory_reservation_lock is not None: + if vm_obj is None or memory_reservation_lock != vm_obj.config.memoryReservationLockedToMax: + self.change_detected = True + self.configspec.memoryReservationLockedToMax = memory_reservation_lock + + vpmc_enabled = self.params['hardware']['vpmc_enabled'] + if vpmc_enabled is not None: + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + if vm_obj and vm_obj.runtime.powerState == vim.VirtualMachinePowerState.poweredOn and \ + vm_obj.config.vPMCEnabled != vpmc_enabled and not self.module.check_mode: + self.module.fail_json(msg="Configure vPMC cpu operation is not supported when VM is power on") + if vm_obj is None or vpmc_enabled != vm_obj.config.vPMCEnabled: + self.change_detected = True + self.configspec.vPMCEnabled = vpmc_enabled + + boot_firmware = self.params['hardware']['boot_firmware'] + if boot_firmware is not None: + # boot firmware re-config can cause boot issue + if vm_obj is not None: + return + self.configspec.firmware = boot_firmware + self.change_detected = True + + def sanitize_cdrom_params(self): + cdrom_specs = [] + expected_cdrom_spec = self.params.get('cdrom') + if expected_cdrom_spec: + for cdrom_spec in expected_cdrom_spec: + cdrom_spec['controller_type'] = cdrom_spec.get('controller_type') + cdrom_spec['state'] = cdrom_spec.get('state') + + if cdrom_spec['state'] == 'present': + # set CDROM type is 'client' by default + cdrom_spec['type'] = cdrom_spec.get('type') + if cdrom_spec['type'] == 'iso' and not cdrom_spec.get('iso_path'): + self.module.fail_json(msg="cdrom.iso_path is mandatory when cdrom.type is set to iso.") + + if 'controller_number' not in cdrom_spec or 'unit_number' not in cdrom_spec: + self.module.fail_json(msg="'cdrom.controller_number' and 'cdrom.unit_number' are required" + " parameters when configure CDROM list.") + + cdrom_ctl_num = int(cdrom_spec.get('controller_number')) + cdrom_ctl_unit_num = int(cdrom_spec.get('unit_number')) + + if cdrom_spec['controller_type'] == 'ide' and (cdrom_ctl_num not in [0, 1] or cdrom_ctl_unit_num not in [0, 1]): + self.module.fail_json(msg="Invalid cdrom.controller_number: %s or cdrom.unit_number: %s, valid" + " values are 0 or 1 for IDE controller." + % (cdrom_spec.get('controller_number'), cdrom_spec.get('unit_number'))) + + if cdrom_spec['controller_type'] == 'sata' and (cdrom_ctl_num not in range(0, 4) or cdrom_ctl_unit_num not in range(0, 30)): + self.module.fail_json(msg="Invalid cdrom.controller_number: %s or cdrom.unit_number: %s," + " valid controller_number value is 0-3, valid unit_number is 0-29" + " for SATA controller." % (cdrom_spec.get('controller_number'), + cdrom_spec.get('unit_number'))) + cdrom_spec['controller_number'] = cdrom_ctl_num + cdrom_spec['unit_number'] = cdrom_ctl_unit_num + + ctl_exist = False + for exist_spec in cdrom_specs: + if exist_spec.get('ctl_num') == cdrom_spec['controller_number'] and \ + exist_spec.get('ctl_type') == cdrom_spec['controller_type']: + for cdrom_same_ctl in exist_spec['cdroms']: + if cdrom_same_ctl['unit_number'] == cdrom_spec['unit_number']: + self.module.fail_json(msg="Duplicate cdrom.controller_type: %s, cdrom.controller_number: %s," + "cdrom.unit_number: %s parameters specified." + % (cdrom_spec['controller_type'], cdrom_spec['controller_number'], cdrom_spec['unit_number'])) + ctl_exist = True + exist_spec['cdroms'].append(cdrom_spec) + break + if not ctl_exist: + cdrom_specs.append({'ctl_num': cdrom_spec['controller_number'], + 'ctl_type': cdrom_spec['controller_type'], 'cdroms': [cdrom_spec]}) + + return cdrom_specs + + def configure_cdrom(self, vm_obj): + # Configure the VM CD-ROM + if self.params.get('cdrom'): + if vm_obj and vm_obj.config.template: + # Changing CD-ROM settings on a template is not supported + return + + self.configure_cdrom_list(vm_obj) + + def configure_cdrom_list(self, vm_obj): + configured_cdroms = self.sanitize_cdrom_params() + # get existing CDROM devices + cdrom_devices = self.get_vm_cdrom_devices(vm=vm_obj) + # get existing IDE and SATA controllers + ide_devices = self.get_vm_ide_devices(vm=vm_obj) + sata_devices = self.get_vm_sata_devices(vm=vm_obj) + + for expected_cdrom_spec in configured_cdroms: + ctl_device = None + if expected_cdrom_spec['ctl_type'] == 'ide' and ide_devices: + for device in ide_devices: + if device.busNumber == expected_cdrom_spec['ctl_num']: + ctl_device = device + break + if expected_cdrom_spec['ctl_type'] == 'sata' and sata_devices: + for device in sata_devices: + if device.busNumber == expected_cdrom_spec['ctl_num']: + ctl_device = device + break + # if not find create new ide or sata controller + if not ctl_device: + if expected_cdrom_spec['ctl_type'] == 'ide': + ide_ctl = self.device_helper.create_ide_controller(bus_number=expected_cdrom_spec['ctl_num']) + ctl_device = ide_ctl.device + self.change_detected = True + self.configspec.deviceChange.append(ide_ctl) + if expected_cdrom_spec['ctl_type'] == 'sata': + sata_ctl = self.device_helper.create_sata_controller(bus_number=expected_cdrom_spec['ctl_num']) + ctl_device = sata_ctl.device + self.change_detected = True + self.configspec.deviceChange.append(sata_ctl) + + for cdrom in expected_cdrom_spec['cdroms']: + cdrom_device = None + iso_path = cdrom.get('iso_path') + unit_number = cdrom.get('unit_number') + for target_cdrom in cdrom_devices: + if target_cdrom.controllerKey == ctl_device.key and target_cdrom.unitNumber == unit_number: + cdrom_device = target_cdrom + break + # create new CD-ROM + if not cdrom_device and cdrom.get('state') != 'absent': + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + if vm_obj and vm_obj.runtime.powerState == vim.VirtualMachinePowerState.poweredOn and \ + isinstance(ctl_device, vim.vm.device.VirtualIDEController) and not self.module.check_mode: + self.module.fail_json(msg='CD-ROM attach to IDE controller not support hot-add.') + if len(ctl_device.device) == 2 and isinstance(ctl_device, vim.vm.device.VirtualIDEController): + self.module.fail_json(msg='Maximum number of CD-ROMs attached to IDE controller is 2.') + if len(ctl_device.device) == 30 and isinstance(ctl_device, vim.vm.device.VirtualAHCIController): + self.module.fail_json(msg='Maximum number of CD-ROMs attached to SATA controller is 30.') + + cdrom_spec = self.device_helper.create_cdrom(ctl_device=ctl_device, cdrom_type=cdrom['type'], + iso_path=iso_path, unit_number=unit_number) + self.change_detected = True + self.configspec.deviceChange.append(cdrom_spec) + # re-configure CD-ROM + elif cdrom_device and cdrom.get('state') != 'absent' and \ + not self.device_helper.is_equal_cdrom(vm_obj=vm_obj, cdrom_device=cdrom_device, + cdrom_type=cdrom['type'], iso_path=iso_path): + self.device_helper.update_cdrom_config(vm_obj, cdrom, cdrom_device, iso_path=iso_path) + cdrom_spec = vim.vm.device.VirtualDeviceSpec() + cdrom_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit + cdrom_spec.device = cdrom_device + self.change_detected = True + self.configspec.deviceChange.append(cdrom_spec) + # delete CD-ROM + elif cdrom_device and cdrom.get('state') == 'absent': + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + if vm_obj and vm_obj.runtime.powerState != vim.VirtualMachinePowerState.poweredOff and \ + isinstance(ctl_device, vim.vm.device.VirtualIDEController) and not self.module.check_mode: + self.module.fail_json(msg='CD-ROM attach to IDE controller not support hot-remove.') + cdrom_spec = self.device_helper.remove_cdrom(cdrom_device) + self.change_detected = True + self.configspec.deviceChange.append(cdrom_spec) + + def configure_hardware_params(self, vm_obj): + """ + Function to configure hardware related configuration of virtual machine + Args: + vm_obj: virtual machine object + """ + max_connections = self.params['hardware']['max_connections'] + if max_connections is not None: + if vm_obj is None or max_connections != vm_obj.config.maxMksConnections: + self.change_detected = True + self.configspec.maxMksConnections = max_connections + + nested_virt = self.params['hardware']['nested_virt'] + if nested_virt is not None: + if vm_obj is None or nested_virt != bool(vm_obj.config.nestedHVEnabled): + self.change_detected = True + self.configspec.nestedHVEnabled = nested_virt + + temp_version = self.params['hardware']['version'] + if temp_version is not None: + new_version = None + if temp_version.lower() == 'latest': + # Check is to make sure vm_obj is not of type template + if vm_obj and not vm_obj.config.template: + config_option_descriptors = vm_obj.environmentBrowser.QueryConfigOptionDescriptor() + available_hw_versions = [int(option_desc.key.split("-")[1]) for option_desc in config_option_descriptors if option_desc.upgradeSupported] + temp_version = max(available_hw_versions) + else: + try: + temp_version = int(temp_version) + except ValueError: + self.module.fail_json(msg="Failed to set hardware.version '%s' value as valid" + " values are either 'latest' or a number." + " Please check VMware documentation for valid VM hardware versions." % temp_version) + + if isinstance(temp_version, int): + # Hardware version is denoted as "vmx-10" + new_version = "vmx-%02d" % temp_version + + if vm_obj is None: + self.change_detected = True + self.configspec.version = new_version + # Check is to make sure vm_obj is not of type template + elif not vm_obj.config.template: + # VM exists and we need to update the hardware version + current_version = vm_obj.config.version + # Hardware version is denoted as "vmx-10" + version_digit = int(current_version.split("-", 1)[-1]) + if temp_version < version_digit: + self.module.fail_json(msg="Current hardware version '%d' which is greater than the specified" + " version '%d'. Downgrading hardware version is" + " not supported. Please specify version greater" + " than the current version." % (version_digit, + temp_version)) + elif temp_version > version_digit: + self.change_detected = True + self.tracked_changes['hardware.version'] = temp_version + self.configspec.version = new_version + # Only perform the upgrade if not in check mode. + if not self.module.check_mode: + task = vm_obj.UpgradeVM_Task(new_version) + self.wait_for_task(task) + if task.info.state == 'error': + return {'changed': self.change_applied, 'failed': True, 'msg': task.info.error.msg, 'op': 'upgrade'} + self.change_applied = True + + secure_boot = self.params['hardware']['secure_boot'] + if secure_boot is not None: + if vm_obj is None or secure_boot != vm_obj.config.bootOptions.efiSecureBootEnabled: + self.change_detected = True + self.configspec.bootOptions = vim.vm.BootOptions() + self.configspec.bootOptions.efiSecureBootEnabled = secure_boot + + iommu = self.params['hardware']['iommu'] + if iommu is not None: + if vm_obj is None or iommu != vm_obj.config.flags.vvtdEnabled: + self.change_detected = True + if self.configspec.flags is None: + self.configspec.flags = vim.vm.FlagInfo() + self.configspec.flags.vvtdEnabled = iommu + + virt_based_security = self.params['hardware']['virt_based_security'] + if virt_based_security is not None: + if vm_obj is None or virt_based_security != vm_obj.config.flags.vbsEnabled: + self.change_detected = True + if self.configspec.flags is None: + self.configspec.flags = vim.vm.FlagInfo() + self.configspec.flags.vbsEnabled = virt_based_security + + def configure_encryption_params(self, vm_obj): + + encrypted_vmotion = self.params['encryption']['encrypted_vmotion'] + if encrypted_vmotion is not None: + if vm_obj is None or encrypted_vmotion != vm_obj.config.migrateEncryption: + self.change_detected = True + self.configspec.migrateEncryption = encrypted_vmotion + + encrypted_ft = self.params['encryption']['encrypted_ft'] + if encrypted_ft is not None: + if encrypted_ft == "disabled": + encrypted_ft_cfg = "ftEncryptionDisabled" + elif encrypted_ft == "opportunistic": + encrypted_ft_cfg = "ftEncryptionOpportunistic" + elif encrypted_ft == "required": + encrypted_ft_cfg = "ftEncryptionRequired" + if vm_obj is None or encrypted_ft_cfg != vm_obj.config.ftEncryptionMode: + self.change_detected = True + self.configspec.ftEncryptionMode = encrypted_ft_cfg + + def get_device_by_type(self, vm=None, type=None): + device_list = [] + if vm is None or type is None: + return device_list + for device in vm.config.hardware.device: + if isinstance(device, type): + device_list.append(device) + + return device_list + + def get_vm_cdrom_devices(self, vm=None): + return self.get_device_by_type(vm=vm, type=vim.vm.device.VirtualCdrom) + + def get_vm_ide_devices(self, vm=None): + return self.get_device_by_type(vm=vm, type=vim.vm.device.VirtualIDEController) + + def get_vm_sata_devices(self, vm=None): + return self.get_device_by_type(vm=vm, type=vim.vm.device.VirtualAHCIController) + + def get_vm_nvdimm_ctl_device(self, vm=None): + return self.get_device_by_type(vm=vm, type=vim.vm.device.VirtualNVDIMMController) + + def get_vm_nvdimm_devices(self, vm=None): + return self.get_device_by_type(vm=vm, type=vim.vm.device.VirtualNVDIMM) + + def configure_nvdimm(self, vm_obj): + """ + Manage virtual NVDIMM device to the virtual machine + Args: + vm_obj: virtual machine object + """ + if self.params['nvdimm']['state']: + # Label is required when remove device + if self.params['nvdimm']['state'] == 'absent' and not self.params['nvdimm']['label']: + self.module.fail_json(msg="Please specify the label of virtual NVDIMM device using 'label' parameter" + " when state is set to 'absent'.") + # Reconfigure device requires VM in power off state + if vm_obj and not vm_obj.config.template: + # Allow VM to be powered on during this check when in check mode, when no changes will actually be made + if vm_obj.runtime.powerState != vim.VirtualMachinePowerState.poweredOff and not self.module.check_mode: + self.module.fail_json(msg="VM is not in power off state, can not do virtual NVDIMM configuration.") + + nvdimm_ctl_exists = False + if vm_obj and not vm_obj.config.template: + # Get existing NVDIMM controller + nvdimm_ctl = self.get_vm_nvdimm_ctl_device(vm=vm_obj) + if len(nvdimm_ctl) != 0: + nvdimm_ctl_exists = True + nvdimm_ctl_key = nvdimm_ctl[0].key + if self.params['nvdimm']['label'] is not None: + nvdimm_devices = self.get_vm_nvdimm_devices(vm=vm_obj) + if len(nvdimm_devices) != 0: + existing_nvdimm_dev = self.device_helper.find_nvdimm_by_label( + nvdimm_label=self.params['nvdimm']['label'], + nvdimm_devices=nvdimm_devices + ) + if existing_nvdimm_dev is not None: + if self.params['nvdimm']['state'] == 'absent': + nvdimm_remove_spec = self.device_helper.remove_nvdimm( + nvdimm_device=existing_nvdimm_dev + ) + self.change_detected = True + self.configspec.deviceChange.append(nvdimm_remove_spec) + else: + if existing_nvdimm_dev.capacityInMB < self.params['nvdimm']['size_mb']: + nvdimm_config_spec = self.device_helper.update_nvdimm_config( + nvdimm_device=existing_nvdimm_dev, + nvdimm_size=self.params['nvdimm']['size_mb'] + ) + self.change_detected = True + self.configspec.deviceChange.append(nvdimm_config_spec) + elif existing_nvdimm_dev.capacityInMB > self.params['nvdimm']['size_mb']: + self.module.fail_json(msg="Can not change NVDIMM device size to %s MB, which is" + " smaller than the current size %s MB." + % (self.params['nvdimm']['size_mb'], + existing_nvdimm_dev.capacityInMB)) + # New VM or existing VM without label specified, add new NVDIMM device + if vm_obj is None or (vm_obj and not vm_obj.config.template and self.params['nvdimm']['label'] is None): + if self.params['nvdimm']['state'] == 'present': + vc_pmem_profile_id = None + # Get default PMem storage policy when host is vCenter + if self.is_vcenter(): + storage_profile_name = "Host-local PMem Default Storage Policy" + spbm = SPBM(self.module) + pmem_profile = spbm.find_storage_profile_by_name(profile_name=storage_profile_name) + if pmem_profile is None: + self.module.fail_json(msg="Can not find PMem storage policy with name '%s'." % storage_profile_name) + vc_pmem_profile_id = pmem_profile.profileId.uniqueId + + if not nvdimm_ctl_exists: + nvdimm_ctl_spec = self.device_helper.create_nvdimm_controller() + self.configspec.deviceChange.append(nvdimm_ctl_spec) + nvdimm_ctl_key = nvdimm_ctl_spec.device.key + + nvdimm_dev_spec = self.device_helper.create_nvdimm_device( + nvdimm_ctl_dev_key=nvdimm_ctl_key, + pmem_profile_id=vc_pmem_profile_id, + nvdimm_dev_size_mb=self.params['nvdimm']['size_mb'] + ) + self.change_detected = True + self.configspec.deviceChange.append(nvdimm_dev_spec) + + def get_vm_network_interfaces(self, vm=None): + device_list = [] + if vm is None: + return device_list + + for device in vm.config.hardware.device: + for device_type in self.device_helper.nic_device_type.values(): + if isinstance(device, device_type): + device_list.append(device) + + return device_list + + def sanitize_network_params(self): + """ + Sanitize user provided network provided params + + Returns: A sanitized list of network params, else fails + + """ + network_devices = list() + # Clean up user data here + for network in self.params['networks']: + if 'name' not in network and 'vlan' not in network: + self.module.fail_json(msg="Please specify at least a network name or" + " a VLAN name under VM network list.") + + if 'name' in network and self.cache.get_network(network['name']) is None: + self.module.fail_json(msg="Network '%(name)s' does not exist." % network) + elif 'vlan' in network: + dvps = self.cache.get_all_objs(self.content, [vim.dvs.DistributedVirtualPortgroup]) + for dvp in dvps: + if hasattr(dvp.config.defaultPortConfig, 'vlan') and \ + isinstance(dvp.config.defaultPortConfig.vlan.vlanId, int) and \ + str(dvp.config.defaultPortConfig.vlan.vlanId) == str(network['vlan']): + network['name'] = dvp.config.name + break + if 'dvswitch_name' in network and \ + dvp.config.distributedVirtualSwitch.name == network['dvswitch_name'] and \ + dvp.config.name == network['vlan']: + network['name'] = dvp.config.name + break + + if dvp.config.name == network['vlan']: + network['name'] = dvp.config.name + break + else: + self.module.fail_json(msg="VLAN '%(vlan)s' does not exist." % network) + + if 'type' in network: + if network['type'] not in ['dhcp', 'static']: + self.module.fail_json(msg="Network type '%(type)s' is not a valid parameter." + " Valid parameters are ['dhcp', 'static']." % network) + if network['type'] != 'static' and ('ip' in network or 'netmask' in network): + self.module.fail_json(msg='Static IP information provided for network "%(name)s",' + ' but "type" is set to "%(type)s".' % network) + else: + # Type is optional parameter, if user provided IP or Subnet assume + # network type as 'static' + if 'ip' in network or 'netmask' in network: + network['type'] = 'static' + else: + # User wants network type as 'dhcp' + network['type'] = 'dhcp' + + if network.get('type') == 'static': + if 'ip' in network and 'netmask' not in network: + self.module.fail_json(msg="'netmask' is required if 'ip' is" + " specified under VM network list.") + if 'ip' not in network and 'netmask' in network: + self.module.fail_json(msg="'ip' is required if 'netmask' is" + " specified under VM network list.") + + if 'typev6' in network: + if network['typev6'] not in ['dhcp', 'static']: + self.module.fail_json(msg="Network type '%(typev6)s' for IPv6 is not a valid parameter." + " Valid parameters are ['dhcp', 'static']." % network) + if network['typev6'] != 'static' and ('ipv6' in network or 'netmaskv6' in network): + self.module.fail_json(msg='Static IPv6 information provided for network "%(name)s",' + ' but "typev6" is set to "%(typev6)s".' % network) + else: + # Type is optional parameter, if user provided IP or Subnet assume + # network type as 'static' + if 'ipv6' in network or 'netmaskv6' in network: + network['typev6'] = 'static' + else: + # User wants network type as 'dhcp' + network['typev6'] = 'dhcp' + + if network.get('typev6') == 'static': + if 'ipv6' in network and 'netmaskv6' not in network: + self.module.fail_json(msg="'netmaskv6' is required if 'ipv6' is" + " specified under VM network list.") + if 'ipv6' not in network and 'netmaskv6' in network: + self.module.fail_json(msg="'ipv6' is required if 'netmaskv6' is" + " specified under VM network list.") + + if 'device_type' in network and network['device_type'] not in self.device_helper.nic_device_type.keys(): + self.module.fail_json(msg="Device type specified '%s' is not valid. Please specify correct device type" + " from ['%s']." % (network['device_type'], + "', '".join(self.device_helper.nic_device_type.keys()))) + + if 'mac' in network and not is_mac(network['mac']): + self.module.fail_json(msg="Device MAC address '%s' is invalid." + " Please provide correct MAC address." % network['mac']) + + network_devices.append(network) + + return network_devices + + def configure_network(self, vm_obj): + # Ignore empty networks, this permits to keep networks when deploying a template/cloning a VM + if not self.params['networks']: + return + + network_devices = self.sanitize_network_params() + + # List current device for Clone or Idempotency + current_net_devices = self.get_vm_network_interfaces(vm=vm_obj) + if len(network_devices) < len(current_net_devices): + self.module.fail_json(msg="Given network device list is lesser than current VM device list (%d < %d). " + "Removing interfaces is not allowed" + % (len(network_devices), len(current_net_devices))) + + for key in range(0, len(network_devices)): + nic_change_detected = False + network_name = network_devices[key]['name'] + if key < len(current_net_devices) and (vm_obj or self.params['template']): + # We are editing existing network devices, this is either when + # are cloning from VM or Template + nic = vim.vm.device.VirtualDeviceSpec() + nic.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit + + nic.device = current_net_devices[key] + if "wake_on_lan" in network_devices[key] and \ + nic.device.wakeOnLanEnabled != network_devices[key].get("wake_on_lan"): + nic.device.wakeOnLanEnabled = network_devices[key].get("wake_on_lan") + nic_change_detected = True + if "start_connected" in network_devices[key] and \ + nic.device.connectable.startConnected != network_devices[key].get("start_connected"): + nic.device.connectable.startConnected = network_devices[key].get("start_connected") + nic_change_detected = True + if "connected" in network_devices[key] and \ + nic.device.connectable.connected != network_devices[key].get("connected"): + nic.device.connectable.connected = network_devices[key].get("connected") + nic_change_detected = True + if "allow_guest_control" in network_devices[key] and \ + nic.device.connectable.allowGuestControl != network_devices[key].get("allow_guest_control"): + nic.device.connectable.allowGuestControl = network_devices[key].get("allow_guest_control") + nic_change_detected = True + + if nic.device.deviceInfo.summary != network_name: + nic.device.deviceInfo.summary = network_name + nic_change_detected = True + if 'device_type' in network_devices[key]: + device = self.device_helper.nic_device_type.get(network_devices[key]['device_type']) + if not isinstance(nic.device, device): + self.module.fail_json(msg="Changing the device type is not possible when interface is already" + " present. The failing device type is %s" + % network_devices[key]['device_type']) + # Changing mac address has no effect when editing interface + if 'mac' in network_devices[key] and nic.device.macAddress != current_net_devices[key].macAddress: + self.module.fail_json(msg="Changing MAC address has not effect when interface is already present. " + "The failing new MAC address is %s" % nic.device.macAddress) + + else: + # Default device type is vmxnet3, VMware best practice + device_type = network_devices[key].get('device_type', 'vmxnet3') + nic = self.device_helper.create_nic(device_type, + 'Network Adapter %s' % (key + 1), + network_devices[key]) + nic.operation = vim.vm.device.VirtualDeviceSpec.Operation.add + nic_change_detected = True + + net_obj = self.cache.get_network(network_name) + if hasattr(net_obj, 'portKeys'): + # VDS switch + pg_obj = None + if 'dvswitch_name' in network_devices[key]: + dvs_name = network_devices[key]['dvswitch_name'] + dvs_obj = find_dvs_by_name(self.content, dvs_name) + if dvs_obj is None: + self.module.fail_json(msg="Unable to find distributed virtual switch %s" % dvs_name) + pg_obj = find_dvspg_by_name(dvs_obj, network_name) + if pg_obj is None: + self.module.fail_json(msg="Unable to find distributed port group %s" % network_name) + else: + pg_obj = self.cache.find_obj(self.content, [vim.dvs.DistributedVirtualPortgroup], network_name) + + # TODO: (akasurde) There is no way to find association between resource pool and distributed virtual portgroup + # For now, check if we are able to find distributed virtual switch + if not pg_obj.config.distributedVirtualSwitch: + self.module.fail_json( + msg="Failed to find distributed virtual switch which is associated with" + " distributed virtual portgroup '%s'. Make sure hostsystem is associated with" + " the given distributed virtual portgroup. Also, check if user has correct" + " permission to access distributed virtual switch in the given portgroup." + % pg_obj.name + ) + if nic.device.backing and ( + not hasattr(nic.device.backing, "port") + or ( + nic.device.backing.port.portgroupKey != pg_obj.key + or nic.device.backing.port.switchUuid + != pg_obj.config.distributedVirtualSwitch.uuid + ) + ): + nic_change_detected = True + + dvs_port_connection = vim.dvs.PortConnection() + dvs_port_connection.portgroupKey = pg_obj.key + # If user specifies distributed port group without associating to the hostsystem on which + # virtual machine is going to be deployed then we get error. We can infer that there is no + # association between given distributed port group and host system. + host_system = self.params.get('esxi_hostname') + if host_system and host_system not in [host.config.host.name for host in pg_obj.config.distributedVirtualSwitch.config.host]: + self.module.fail_json(msg="It seems that host system '%s' is not associated with distributed" + " virtual portgroup '%s'. Please make sure host system is associated" + " with given distributed virtual portgroup" % (host_system, pg_obj.name)) + dvs_port_connection.switchUuid = pg_obj.config.distributedVirtualSwitch.uuid + nic.device.backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo() + nic.device.backing.port = dvs_port_connection + + elif isinstance(net_obj, vim.OpaqueNetwork): + # NSX-T Logical Switch + nic.device.backing = vim.vm.device.VirtualEthernetCard.OpaqueNetworkBackingInfo() + network_id = net_obj.summary.opaqueNetworkId + nic.device.backing.opaqueNetworkType = 'nsx.LogicalSwitch' + nic.device.backing.opaqueNetworkId = network_id + nic.device.deviceInfo.summary = 'nsx.LogicalSwitch: %s' % network_id + nic_change_detected = True + else: + # vSwitch + if not isinstance(nic.device.backing, vim.vm.device.VirtualEthernetCard.NetworkBackingInfo): + nic.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo() + nic_change_detected = True + + if nic.device.backing.network != net_obj: + nic.device.backing.network = net_obj + nic_change_detected = True + + if nic.device.backing.deviceName != network_name: + nic.device.backing.deviceName = network_name + nic_change_detected = True + + if nic_change_detected: + # Change to fix the issue found while configuring opaque network + # VMs cloned from a template with opaque network will get disconnected + # Replacing deprecated config parameter with relocation Spec + if isinstance(net_obj, vim.OpaqueNetwork): + self.relospec.deviceChange.append(nic) + else: + self.configspec.deviceChange.append(nic) + self.change_detected = True + + def set_vapp_properties(self, property_spec): + # Sets the values in property_info + property_info = vim.vApp.PropertyInfo() + property_info.classId = property_spec.get('classId') + property_info.instanceId = property_spec.get('instanceId') + property_info.id = property_spec.get('id') + property_info.category = property_spec.get('category') + property_info.label = property_spec.get('label') + property_info.type = property_spec.get('type', 'string') + property_info.userConfigurable = property_spec.get('userConfigurable', True) + property_info.defaultValue = property_spec.get('defaultValue') + property_info.value = property_spec.get('value', '') + property_info.description = property_spec.get('description') + return property_info + + def configure_vapp_properties(self, vm_obj): + if not self.params['vapp_properties']: + return + + for x in self.params['vapp_properties']: + if not x.get('id'): + self.module.fail_json(msg="id is required to set vApp property") + + new_vmconfig_spec = vim.vApp.VmConfigSpec() + + if vm_obj: + # VM exists + orig_spec = vm_obj.config.vAppConfig + + vapp_properties_current = dict((x.id, x) for x in orig_spec.property) + vapp_properties_to_change = dict((x['id'], x) for x in self.params['vapp_properties']) + + # each property must have a unique key + # init key counter with max value + 1 + all_keys = [x.key for x in orig_spec.property] + new_property_index = max(all_keys) + 1 if all_keys else 0 + + for property_id, property_spec in vapp_properties_to_change.items(): + is_property_changed = False + new_vapp_property_spec = vim.vApp.PropertySpec() + + if property_id in vapp_properties_current: + if property_spec.get('operation') == 'remove': + new_vapp_property_spec.operation = 'remove' + new_vapp_property_spec.removeKey = vapp_properties_current[property_id].key + is_property_changed = True + else: + # this is 'edit' branch + new_vapp_property_spec.operation = 'edit' + new_vapp_property_spec.info = vapp_properties_current[property_id] + try: + for property_name, property_value in property_spec.items(): + + if property_name == 'operation': + # operation is not an info object property + # if set to anything other than 'remove' we don't fail + continue + + # Updating attributes only if needed + if getattr(new_vapp_property_spec.info, property_name) != property_value: + setattr(new_vapp_property_spec.info, property_name, property_value) + is_property_changed = True + + except Exception as e: + msg = "Failed to set vApp property field='%s' and value='%s'. Error: %s" % (property_name, property_value, to_text(e)) + self.module.fail_json(msg=msg) + else: + if property_spec.get('operation') == 'remove': + # attempt to delete non-existent property + continue + + # this is add new property branch + new_vapp_property_spec.operation = 'add' + + # Configure the values in property_value + property_info = self.set_vapp_properties(property_spec) + + new_vapp_property_spec.info = property_info + new_vapp_property_spec.info.key = new_property_index + new_property_index += 1 + is_property_changed = True + + if is_property_changed: + new_vmconfig_spec.property.append(new_vapp_property_spec) + else: + # New VM + all_keys = [x.key for x in new_vmconfig_spec.property] + new_property_index = max(all_keys) + 1 if all_keys else 0 + vapp_properties_to_change = dict((x['id'], x) for x in self.params['vapp_properties']) + is_property_changed = False + + for property_id, property_spec in vapp_properties_to_change.items(): + new_vapp_property_spec = vim.vApp.PropertySpec() + # this is add new property branch + new_vapp_property_spec.operation = 'add' + + # Configure the values in property_value + property_info = self.set_vapp_properties(property_spec) + + new_vapp_property_spec.info = property_info + new_vapp_property_spec.info.key = new_property_index + new_property_index += 1 + is_property_changed = True + + if is_property_changed: + new_vmconfig_spec.property.append(new_vapp_property_spec) + + if new_vmconfig_spec.property: + self.configspec.vAppConfig = new_vmconfig_spec + self.change_detected = True + + def customize_advanced_settings(self, vm_obj, config_spec): + if not self.params['advanced_settings']: + return + + vm_custom_spec = config_spec + vm_custom_spec.extraConfig = [] + + changed = False + facts = self.gather_facts(vm_obj) + for kv in self.params['advanced_settings']: + if 'key' not in kv or 'value' not in kv: + self.module.exit_json(msg="advanced_settings items required both 'key' and 'value' fields.") + + # If kv is not kv fetched from facts, change it + if isinstance(kv['value'], (bool, int)): + specifiedvalue = str(kv['value']).upper() + comparisonvalue = facts['advanced_settings'].get(kv['key'], '').upper() + else: + specifiedvalue = kv['value'] + comparisonvalue = facts['advanced_settings'].get(kv['key'], '') + + if (kv['key'] not in facts['advanced_settings'] and kv['value'] != '') or comparisonvalue != specifiedvalue: + option = vim.option.OptionValue() + option.key = kv['key'] + option.value = specifiedvalue + + vm_custom_spec.extraConfig.append(option) + changed = True + + if changed: + self.change_detected = True + + def customize_customvalues(self, vm_obj): + if not self.params['customvalues']: + return + + if not self.is_vcenter(): + self.module.warn("Currently connected to ESXi. " + "customvalues are a vCenter feature, this parameter will be ignored.") + return + + facts = self.gather_facts(vm_obj) + for kv in self.params['customvalues']: + if 'key' not in kv or 'value' not in kv: + self.module.exit_json(msg="customvalues items required both 'key' and 'value' fields.") + + key_id = None + for field in self.content.customFieldsManager.field: + if field.name == kv['key']: + key_id = field.key + break + + if not key_id: + self.module.fail_json(msg="Unable to find custom value key %s" % kv['key']) + + # If kv is not kv fetched from facts, change it + if kv['key'] not in facts['customvalues'] or facts['customvalues'][kv['key']] != kv['value']: + self.content.customFieldsManager.SetField(entity=vm_obj, key=key_id, value=kv['value']) + self.change_detected = True + + def customize_vm(self, vm_obj): + + # User specified customization specification + custom_spec_name = self.params.get('customization_spec') + if custom_spec_name: + cc_mgr = self.content.customizationSpecManager + if cc_mgr.DoesCustomizationSpecExist(name=custom_spec_name): + temp_spec = cc_mgr.GetCustomizationSpec(name=custom_spec_name) + self.customspec = temp_spec.spec + return + self.module.fail_json(msg="Unable to find customization specification" + " '%s' in given configuration." % custom_spec_name) + + # Network settings + adaptermaps = [] + for network in self.params['networks']: + + guest_map = vim.vm.customization.AdapterMapping() + guest_map.adapter = vim.vm.customization.IPSettings() + + if 'ip' in network and 'netmask' in network: + guest_map.adapter.ip = vim.vm.customization.FixedIp() + guest_map.adapter.ip.ipAddress = str(network['ip']) + guest_map.adapter.subnetMask = str(network['netmask']) + elif 'type' in network and network['type'] == 'dhcp': + guest_map.adapter.ip = vim.vm.customization.DhcpIpGenerator() + + if "ipv6" in network and 'netmaskv6' in network: + guest_map.adapter.ipV6Spec = vim.vm.customization.IPSettings.IpV6AddressSpec() + guest_map.adapter.ipV6Spec.ip = [vim.vm.customization.FixedIpV6()] + guest_map.adapter.ipV6Spec.ip[0].ipAddress = str(network['ipv6']) + guest_map.adapter.ipV6Spec.ip[0].subnetMask = int(network['netmaskv6']) + elif 'typev6' in network and network['typev6'] == 'dhcp': + guest_map.adapter.ipV6Spec = vim.vm.customization.IPSettings.IpV6AddressSpec() + guest_map.adapter.ipV6Spec.ip = [vim.vm.customization.DhcpIpV6Generator()] + + if 'gateway' in network: + guest_map.adapter.gateway = network['gateway'] + + if "gatewayv6" in network: + guest_map.adapter.ipV6Spec.gateway = network['gatewayv6'] + + # On Windows, DNS domain and DNS servers can be set by network interface + # https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.customization.IPSettings.html + if 'domain' in network: + guest_map.adapter.dnsDomain = network['domain'] + elif self.params['customization']['domain'] is not None: + guest_map.adapter.dnsDomain = self.params['customization']['domain'] + + if 'dns_servers' in network: + guest_map.adapter.dnsServerList = network['dns_servers'] + elif self.params['customization']['dns_servers'] is not None: + guest_map.adapter.dnsServerList = self.params['customization']['dns_servers'] + + adaptermaps.append(guest_map) + + # Global DNS settings + globalip = vim.vm.customization.GlobalIPSettings() + if self.params['customization']['dns_servers'] is not None: + globalip.dnsServerList = self.params['customization']['dns_servers'] + + # TODO: Maybe list the different domains from the interfaces here by default ? + dns_suffixes = [] + dns_suffix = self.params['customization']['dns_suffix'] + if dns_suffix: + if isinstance(dns_suffix, list): + dns_suffixes += dns_suffix + else: + dns_suffixes.append(dns_suffix) + + globalip.dnsSuffixList = dns_suffixes + + if self.params['customization']['domain'] is not None: + dns_suffixes.insert(0, self.params['customization']['domain']) + globalip.dnsSuffixList = dns_suffixes + + if self.params['guest_id'] is not None: + guest_id = self.params['guest_id'] + else: + guest_id = vm_obj.summary.config.guestId + + # For windows guest OS, use SysPrep + # https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.customization.Sysprep.html#field_detail + if 'win' in guest_id: + ident = vim.vm.customization.Sysprep() + + ident.userData = vim.vm.customization.UserData() + + # Setting hostName, orgName and fullName is mandatory, so we set some default when missing + ident.userData.computerName = vim.vm.customization.FixedName() + # computer name will be truncated to 15 characters if using VM name + default_name = "" + if 'name' in self.params and self.params['name']: + default_name = self.params['name'].replace(' ', '') + elif vm_obj: + default_name = vm_obj.name.replace(' ', '') + punctuation = string.punctuation.replace('-', '') + default_name = ''.join([c for c in default_name if c not in punctuation]) + + if self.params['customization']['hostname'] is not None: + ident.userData.computerName.name = self.params['customization']['hostname'][0:15] + else: + ident.userData.computerName.name = default_name[0:15] + + ident.userData.fullName = str(self.params['customization'].get('fullname', 'Administrator')) + ident.userData.orgName = str(self.params['customization'].get('orgname', 'ACME')) + + if self.params['customization']['productid'] is not None: + ident.userData.productId = str(self.params['customization']['productid']) + + ident.guiUnattended = vim.vm.customization.GuiUnattended() + + if self.params['customization']['autologon'] is not None: + ident.guiUnattended.autoLogon = self.params['customization']['autologon'] + ident.guiUnattended.autoLogonCount = self.params['customization'].get('autologoncount', 1) + + if self.params['customization']['timezone'] is not None: + # Check if timezone value is a int before proceeding. + ident.guiUnattended.timeZone = self.device_helper.integer_value( + self.params['customization']['timezone'], + 'customization.timezone') + + ident.identification = vim.vm.customization.Identification() + + if self.params['customization']['password'] is None or self.params['customization']['password'] == '': + ident.guiUnattended.password = None + else: + ident.guiUnattended.password = vim.vm.customization.Password() + ident.guiUnattended.password.value = str(self.params['customization']['password']) + ident.guiUnattended.password.plainText = True + + if self.params['customization']['joindomain'] is not None: + if self.params['customization']['domainadmin'] is None or self.params['customization']['domainadminpassword'] is None: + self.module.fail_json(msg="'domainadmin' and 'domainadminpassword' entries are mandatory in 'customization' section to use " + "joindomain feature") + + ident.identification.domainAdmin = self.params['customization']['domainadmin'] + ident.identification.joinDomain = self.params['customization']['joindomain'] + ident.identification.domainAdminPassword = vim.vm.customization.Password() + ident.identification.domainAdminPassword.value = self.params['customization']['domainadminpassword'] + ident.identification.domainAdminPassword.plainText = True + + elif self.params['customization']['joinworkgroup'] is not None: + ident.identification.joinWorkgroup = self.params['customization']['joinworkgroup'] + + if self.params['customization']['runonce'] is not None: + ident.guiRunOnce = vim.vm.customization.GuiRunOnce() + ident.guiRunOnce.commandList = self.params['customization']['runonce'] + + else: + # FIXME: We have no clue whether this non-Windows OS is actually Linux, hence it might fail! + + # For Linux guest OS, use LinuxPrep + # https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.customization.LinuxPrep.html + ident = vim.vm.customization.LinuxPrep() + + # TODO: Maybe add domain from interface if missing ? + if self.params['customization']['domain'] is not None: + ident.domain = self.params['customization']['domain'] + + ident.hostName = vim.vm.customization.FixedName() + default_name = "" + if 'name' in self.params and self.params['name']: + default_name = self.params['name'] + elif vm_obj: + default_name = vm_obj.name + + if self.params['customization']['hostname'] is not None: + hostname = self.params['customization']['hostname'].split('.')[0] + else: + hostname = default_name.split('.')[0] + + # Remove all characters except alphanumeric and minus which is allowed by RFC 952 + valid_hostname = re.sub(r"[^a-zA-Z0-9\-]", "", hostname) + ident.hostName.name = valid_hostname + + # List of supported time zones for different vSphere versions in Linux/Unix systems + # https://kb.vmware.com/s/article/2145518 + if self.params['customization']['timezone'] is not None: + ident.timeZone = self.params['customization']['timezone'] + if self.params['customization']['hwclockUTC'] is not None: + ident.hwClockUTC = self.params['customization']['hwclockUTC'] + if self.params['customization']['script_text'] is not None: + ident.scriptText = self.params['customization']['script_text'] + + self.customspec = vim.vm.customization.Specification() + self.customspec.nicSettingMap = adaptermaps + self.customspec.globalIPSettings = globalip + self.customspec.identity = ident + + def get_vm_scsi_controllers(self, vm_obj): + # If vm_obj doesn't exist there is no SCSI controller to find + scsi_ctls = [] + if vm_obj is None: + return None + + for device in vm_obj.config.hardware.device: + if self.device_helper.is_scsi_controller(device): + scsi_ctl = vim.vm.device.VirtualDeviceSpec() + scsi_ctl.device = device + scsi_ctls.append(scsi_ctl) + + return scsi_ctls + + def get_configured_disk_size(self, expected_disk_spec): + # what size is it? + if [x for x in expected_disk_spec.keys() if (x.startswith('size_') or x == 'size') and expected_disk_spec[x]]: + # size, size_tb, size_gb, size_mb, size_kb + if expected_disk_spec['size']: + size_regex = re.compile(r'(\d+(?:\.\d+)?)([tgmkTGMK][bB])') + disk_size_m = size_regex.match(expected_disk_spec['size']) + try: + if disk_size_m: + expected = disk_size_m.group(1) + unit = disk_size_m.group(2) + else: + raise ValueError + + if re.match(r'\d+\.\d+', expected): + # We found float value in string, let's typecast it + expected = float(expected) + else: + # We found int value in string, let's typecast it + expected = int(expected) + + if not expected or not unit: + raise ValueError + + except (TypeError, ValueError, NameError): + # Common failure + self.module.fail_json(msg="Failed to parse disk size please review value" + " provided using documentation.") + else: + param = [x for x in expected_disk_spec.keys() if x.startswith('size_') and expected_disk_spec[x]][0] + unit = param.split('_')[-1] + expected = expected_disk_spec[param] + + disk_units = dict(tb=3, gb=2, mb=1, kb=0) + if unit in disk_units: + unit = unit.lower() + return expected * (1024 ** disk_units[unit]) + else: + self.module.fail_json(msg="%s is not a supported unit for disk size." + " Supported units are ['%s']." % (unit, + "', '".join(disk_units.keys()))) + + # No size found but disk, fail + self.module.fail_json( + msg="No size, size_kb, size_mb, size_gb or size_tb defined in disk configuration") + + def add_existing_vmdk(self, vm_obj, expected_disk_spec, diskspec, scsi_ctl): + """ + Adds vmdk file described by expected_disk_spec['filename'], retrieves the file + information and adds the correct spec to self.configspec.deviceChange. + """ + filename = expected_disk_spec['filename'] + # If this is a new disk, or the disk file names are different + if (vm_obj and diskspec.device.backing.fileName != filename) or vm_obj is None: + diskspec.device.backing.fileName = filename + diskspec.device.key = -1 + self.change_detected = True + self.configspec.deviceChange.append(diskspec) + + def sanitize_disk_parameters(self, vm_obj): + """ + + Sanitize user provided disk parameters to configure multiple types of disk controllers and attached disks + + Returns: A sanitized dict of disk params, else fails + e.g., [{'type': 'nvme', 'num': 1, 'disk': []}, {}, {}, {}]} + + """ + controllers = [] + for disk_spec in self.params.get('disk'): + if disk_spec['controller_type'] is None or disk_spec['controller_number'] is None or disk_spec['unit_number'] is None: + self.module.fail_json(msg="'disk.controller_type', 'disk.controller_number' and 'disk.unit_number' are" + " mandatory parameters when configure multiple disk controllers and disks.") + + ctl_num = disk_spec['controller_number'] + ctl_unit_num = disk_spec['unit_number'] + + disk_spec['unit_number'] = ctl_unit_num + ctl_type = disk_spec['controller_type'] + + if len(controllers) != 0: + ctl_exist = False + for ctl in controllers: + if ctl['type'] in self.device_helper.scsi_device_type.keys() and ctl_type in self.device_helper.scsi_device_type.keys(): + if ctl['type'] != ctl_type and ctl['num'] == ctl_num: + self.module.fail_json(msg="Specified SCSI controller '%s' and '%s' have the same bus number" + ": '%s'" % (ctl['type'], ctl_type, ctl_num)) + + if ctl['type'] == ctl_type and ctl['num'] == ctl_num: + for i in range(0, len(ctl['disk'])): + if disk_spec['unit_number'] == ctl['disk'][i]['unit_number']: + self.module.fail_json(msg="Specified the same 'controller_type, controller_number, " + "unit_number in disk configuration '%s:%s'" % (ctl_type, ctl_num)) + ctl['disk'].append(disk_spec) + ctl_exist = True + break + if not ctl_exist: + controllers.append({'type': ctl_type, 'num': ctl_num, 'disk': [disk_spec]}) + else: + controllers.append({'type': ctl_type, 'num': ctl_num, 'disk': [disk_spec]}) + + return controllers + + def set_disk_parameters(self, disk_spec, expected_disk_spec, reconfigure=False): + disk_modified = False + if expected_disk_spec['disk_mode']: + disk_mode = expected_disk_spec.get('disk_mode') + if reconfigure: + if disk_spec.device.backing.diskMode != disk_mode: + disk_spec.device.backing.diskMode = disk_mode + disk_modified = True + else: + disk_spec.device.backing.diskMode = disk_mode + # default is persistent for new deployed VM + elif not reconfigure: + disk_spec.device.backing.diskMode = "persistent" + + if not reconfigure: + disk_type = expected_disk_spec.get('type', 'thin') + if disk_type == 'thin': + disk_spec.device.backing.thinProvisioned = True + elif disk_type == 'eagerzeroedthick': + disk_spec.device.backing.eagerlyScrub = True + + kb = self.get_configured_disk_size(expected_disk_spec) + if reconfigure: + if disk_spec.device.capacityInKB > kb: + self.module.fail_json(msg="Given disk size is smaller than found (%d < %d)." + "Reducing disks is not allowed." % (kb, disk_spec.device.capacityInKB)) + if disk_spec.device.capacityInKB != kb: + disk_spec.device.capacityInKB = kb + disk_modified = True + else: + disk_spec.device.capacityInKB = kb + disk_modified = True + + return disk_modified + + def configure_multiple_controllers_disks(self, vm_obj): + ctls = self.sanitize_disk_parameters(vm_obj) + if len(ctls) == 0: + return + for ctl in ctls: + # get existing specified disk controller and attached disks + disk_ctl, disk_list = self.device_helper.get_controller_disks(vm_obj, ctl['type'], ctl['num']) + if disk_ctl is None: + # check if scsi controller key already used + if ctl['type'] in self.device_helper.scsi_device_type.keys() and vm_obj is not None: + scsi_ctls = self.get_vm_scsi_controllers(vm_obj) + if scsi_ctls: + for scsi_ctl in scsi_ctls: + if scsi_ctl.device.busNumber == ctl['num']: + self.module.fail_json(msg="Specified SCSI controller number '%s' is already used" + " by: %s" % (ctl['num'], scsi_ctl)) + # create new disk controller if not exist + disk_ctl_spec = self.device_helper.create_disk_controller(ctl['type'], ctl['num']) + self.change_detected = True + self.configspec.deviceChange.append(disk_ctl_spec) + else: + disk_ctl_spec = vim.vm.device.VirtualDeviceSpec() + disk_ctl_spec.device = disk_ctl + for j in range(0, len(ctl['disk'])): + hard_disk = None + hard_disk_spec = None + hard_disk_exist = False + disk_modified_for_spec = False + disk_modified_for_disk = False + disk_unit_number = ctl['disk'][j]['unit_number'] + # from attached disk list find the specified one + if len(disk_list) != 0: + for disk in disk_list: + if disk.unitNumber == disk_unit_number: + hard_disk = disk + hard_disk_exist = True + break + # if find the disk do reconfigure + if hard_disk_exist: + hard_disk_spec = vim.vm.device.VirtualDeviceSpec() + hard_disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit + hard_disk_spec.device = hard_disk + disk_modified_for_spec = self.set_disk_parameters(hard_disk_spec, ctl['disk'][j], reconfigure=True) + # if no disk or the specified one not exist do create new disk + if len(disk_list) == 0 or not hard_disk_exist: + hard_disk = self.device_helper.create_hard_disk(disk_ctl_spec, disk_unit_number) + hard_disk.fileOperation = vim.vm.device.VirtualDeviceSpec.FileOperation.create + disk_modified_for_disk = self.set_disk_parameters(hard_disk, ctl['disk'][j]) + + # Only update the configspec that will be applied in reconfigure_vm if something actually changed + if disk_modified_for_spec: + self.change_detected = True + self.configspec.deviceChange.append(hard_disk_spec) + if disk_modified_for_disk: + self.change_detected = True + self.configspec.deviceChange.append(hard_disk) + + def configure_disks(self, vm_obj): + # Ignore empty disk list, this permits to keep disks when deploying a template/cloning a VM + if not self.params['disk']: + return + + # if one of 'controller_type', 'controller_number', 'unit_number' parameters set in one of disks' configuration + # will call configure_multiple_controllers_disks() function + # do not support mixed old scsi disks configuration and new multiple controller types of disks configuration + configure_multiple_ctl = False + for disk_spec in self.params.get('disk'): + if disk_spec['controller_type'] or disk_spec['controller_number'] or disk_spec['unit_number']: + configure_multiple_ctl = True + break + if configure_multiple_ctl: + self.configure_multiple_controllers_disks(vm_obj) + return + + # do single controller type disks configuration + scsi_ctls = self.get_vm_scsi_controllers(vm_obj) + + # Create scsi controller only if we are deploying a new VM, not a template or reconfiguring + if vm_obj is None or not scsi_ctls: + scsi_ctl = self.device_helper.create_scsi_controller(self.get_scsi_type(), 0) + self.change_detected = True + self.configspec.deviceChange.append(scsi_ctl) + else: + scsi_ctl = scsi_ctls[0] + + disks = [x for x in vm_obj.config.hardware.device if isinstance(x, vim.vm.device.VirtualDisk)] \ + if vm_obj is not None else None + + if disks is not None and self.params.get('disk') and len(self.params.get('disk')) < len(disks): + self.module.fail_json(msg="Provided disks configuration has less disks than " + "the target object (%d vs %d)" % (len(self.params.get('disk')), len(disks))) + + disk_index = 0 + for expected_disk_spec in self.params.get('disk'): + disk_modified = False + # If we are manipulating and existing objects which has disks and disk_index is in disks + if vm_obj is not None and disks is not None and disk_index < len(disks): + diskspec = vim.vm.device.VirtualDeviceSpec() + # set the operation to edit so that it knows to keep other settings + diskspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit + diskspec.device = disks[disk_index] + else: + diskspec = self.device_helper.create_hard_disk(scsi_ctl, disk_index) + disk_modified = True + + # increment index for next disk search + disk_index += 1 + # index 7 is reserved to SCSI controller + if disk_index == 7: + disk_index += 1 + + if expected_disk_spec['disk_mode']: + disk_mode = expected_disk_spec.get('disk_mode', 'persistent') + + if (vm_obj and diskspec.device.backing.diskMode != disk_mode) or (vm_obj is None): + diskspec.device.backing.diskMode = disk_mode + disk_modified = True + else: + diskspec.device.backing.diskMode = "persistent" + + # is it thin? + if expected_disk_spec['type']: + disk_type = expected_disk_spec.get('type', '').lower() + if disk_type == 'thin': + diskspec.device.backing.thinProvisioned = True + elif disk_type == 'eagerzeroedthick': + diskspec.device.backing.eagerlyScrub = True + + if expected_disk_spec['filename']: + self.add_existing_vmdk(vm_obj, expected_disk_spec, diskspec, scsi_ctl) + continue + if vm_obj is None or self.params['template']: + # We are creating new VM or from Template + # Only create virtual device if not backed by vmdk in original template + if diskspec.device.backing.fileName == '': + diskspec.fileOperation = vim.vm.device.VirtualDeviceSpec.FileOperation.create + + # which datastore? + if expected_disk_spec.get('datastore'): + # TODO: This is already handled by the relocation spec, + # but it needs to eventually be handled for all the + # other disks defined + pass + + kb = self.get_configured_disk_size(expected_disk_spec) + # VMware doesn't allow to reduce disk sizes + if kb < diskspec.device.capacityInKB: + self.module.fail_json( + msg="Given disk size is smaller than found (%d < %d). Reducing disks is not allowed." % + (kb, diskspec.device.capacityInKB)) + + if kb != diskspec.device.capacityInKB or disk_modified: + diskspec.device.capacityInKB = kb + self.configspec.deviceChange.append(diskspec) + + self.change_detected = True + + def select_host(self): + hostsystem = self.cache.get_esx_host(self.params['esxi_hostname']) + if not hostsystem: + self.module.fail_json(msg='Failed to find ESX host "%(esxi_hostname)s"' % self.params) + if hostsystem.runtime.connectionState != 'connected' or hostsystem.runtime.inMaintenanceMode: + self.module.fail_json(msg='ESXi "%(esxi_hostname)s" is in invalid state or in maintenance mode.' % self.params) + return hostsystem + + def autoselect_datastore(self): + datastore = None + datastores = self.cache.get_all_objs(self.content, [vim.Datastore]) + + if datastores is None or len(datastores) == 0: + self.module.fail_json(msg="Unable to find a datastore list when autoselecting") + + datastore_freespace = 0 + for ds in datastores: + if not self.is_datastore_valid(datastore_obj=ds): + continue + + if ds.summary.freeSpace > datastore_freespace: + datastore = ds + datastore_freespace = ds.summary.freeSpace + + return datastore + + def select_datastore(self, vm_obj=None): + datastore = None + datastore_name = None + + if self.params['disk']: + # TODO: really use the datastore for newly created disks + if self.params['disk'][0]['autoselect_datastore']: + datastores = [] + + if self.params['cluster']: + cluster = self.find_cluster_by_name(self.params['cluster'], self.content) + + for host in cluster.host: + for mi in host.configManager.storageSystem.fileSystemVolumeInfo.mountInfo: + if mi.volume.type == "VMFS" or mi.volume.type == "NFS": + datastores.append(self.cache.find_obj(self.content, [vim.Datastore], mi.volume.name)) + elif self.params['esxi_hostname']: + host = self.find_hostsystem_by_name(self.params['esxi_hostname']) + + for mi in host.configManager.storageSystem.fileSystemVolumeInfo.mountInfo: + if mi.volume.type == "VMFS" or mi.volume.type == "NFS": + datastores.append(self.cache.find_obj(self.content, [vim.Datastore], mi.volume.name)) + else: + datastores = self.cache.get_all_objs(self.content, [vim.Datastore]) + datastores = [x for x in datastores if self.cache.get_parent_datacenter(x).name == self.params['datacenter']] + + datastore_freespace = 0 + for ds in datastores: + if not self.is_datastore_valid(datastore_obj=ds): + continue + + if (ds.summary.freeSpace > datastore_freespace) or (ds.summary.freeSpace == datastore_freespace and not datastore): + # If datastore field is provided, filter destination datastores + if self.params['disk'][0]['datastore'] and ds.name.find(self.params['disk'][0]['datastore']) < 0: + continue + + datastore = ds + datastore_name = datastore.name + datastore_freespace = ds.summary.freeSpace + + elif self.params['disk'][0]['datastore']: + datastore_name = self.params['disk'][0]['datastore'] + # Check if user has provided datastore cluster first + datastore_cluster = self.cache.find_obj(self.content, [vim.StoragePod], datastore_name) + if datastore_cluster: + # If user specified datastore cluster so get recommended datastore + datastore_name = self.get_recommended_datastore(datastore_cluster_obj=datastore_cluster) + # Check if get_recommended_datastore or user specified datastore exists or not + datastore = self.cache.find_obj(self.content, [vim.Datastore], datastore_name) + else: + self.module.fail_json(msg="Either datastore or autoselect_datastore should be provided to select datastore") + + if not datastore and self.params['template']: + # use the template's existing DS + disks = [x for x in vm_obj.config.hardware.device if isinstance(x, vim.vm.device.VirtualDisk)] + if disks: + datastore = disks[0].backing.datastore + datastore_name = datastore.name + # validation + if datastore: + dc = self.cache.get_parent_datacenter(datastore) + if dc.name != self.params['datacenter']: + datastore = self.autoselect_datastore() + datastore_name = datastore.name + + if not datastore: + if len(self.params['disk']) != 0 or self.params['template'] is None: + self.module.fail_json(msg="Unable to find the datastore with given parameters." + " This could mean, %s is a non-existent virtual machine and module tried to" + " deploy it as new virtual machine with no disk. Please specify disks parameter" + " or specify template to clone from." % self.params['name']) + self.module.fail_json(msg="Failed to find a matching datastore") + + return datastore, datastore_name + + def obj_has_parent(self, obj, parent): + if obj is None and parent is None: + raise AssertionError() + current_parent = obj + + while True: + if current_parent.name == parent.name: + return True + + # Check if we have reached till root folder + moid = current_parent._moId + if moid in ['group-d1', 'ha-folder-root']: + return False + + current_parent = current_parent.parent + if current_parent is None: + return False + + def get_scsi_type(self): + disk_controller_type = self.params['hardware']['scsi'] + if disk_controller_type is not None: + return disk_controller_type + return "paravirtual" + + def find_folder(self, searchpath): + """ Walk inventory objects one position of the searchpath at a time """ + + # split the searchpath so we can iterate through it + paths = [x.replace('/', '') for x in searchpath.split('/')] + paths_total = len(paths) - 1 + position = 0 + + # recursive walk while looking for next element in searchpath + root = self.content.rootFolder + while root and position <= paths_total: + change = False + if hasattr(root, 'childEntity'): + for child in root.childEntity: + if child.name == paths[position]: + root = child + position += 1 + change = True + break + elif isinstance(root, vim.Datacenter): + if hasattr(root, 'vmFolder'): + if root.vmFolder.name == paths[position]: + root = root.vmFolder + position += 1 + change = True + else: + root = None + + if not change: + root = None + + return root + + def get_resource_pool(self, cluster=None, host=None, resource_pool=None): + """ Get a resource pool, filter on cluster, esxi_hostname or resource_pool if given """ + + cluster_name = cluster or self.params.get('cluster', None) + host_name = host or self.params.get('esxi_hostname', None) + resource_pool_name = resource_pool or self.params.get('resource_pool', None) + + # get the datacenter object + datacenter = find_obj(self.content, [vim.Datacenter], self.params['datacenter']) + if not datacenter: + self.module.fail_json(msg='Unable to find datacenter "%s"' % self.params['datacenter']) + + # if cluster is given, get the cluster object + if cluster_name: + cluster = find_obj(self.content, [vim.ComputeResource], cluster_name, folder=datacenter) + if not cluster: + self.module.fail_json(msg='Unable to find cluster "%s"' % cluster_name) + # if host is given, get the cluster object using the host + elif host_name: + host = find_obj(self.content, [vim.HostSystem], host_name, folder=datacenter) + if not host: + self.module.fail_json(msg='Unable to find host "%s"' % host_name) + cluster = host.parent + else: + cluster = None + + # get resource pools limiting search to cluster or datacenter + resource_pool = find_obj(self.content, [vim.ResourcePool], resource_pool_name, folder=cluster or datacenter) + if not resource_pool: + if resource_pool_name: + self.module.fail_json(msg='Unable to find resource_pool "%s"' % resource_pool_name) + else: + self.module.fail_json(msg='Unable to find resource pool, need esxi_hostname, resource_pool, or cluster') + return resource_pool + + def deploy_vm(self): + # https://github.com/vmware/pyvmomi-community-samples/blob/master/samples/clone_vm.py + # https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.vm.CloneSpec.html + # https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.vm.ConfigSpec.html + # https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.RelocateSpec.html + + # FIXME: + # - static IPs + + self.folder = self.params.get('folder', None) + if self.folder is None: + self.module.fail_json(msg="Folder is required parameter while deploying new virtual machine") + + # Prepend / if it was missing from the folder path, also strip trailing slashes + if not self.folder.startswith('/'): + self.folder = '/%(folder)s' % self.params + self.folder = self.folder.rstrip('/') + + datacenter = self.cache.find_obj(self.content, [vim.Datacenter], self.params['datacenter']) + if datacenter is None: + self.module.fail_json(msg='No datacenter named %(datacenter)s was found' % self.params) + + dcpath = compile_folder_path_for_object(datacenter) + + # Nested folder does not have trailing / + if not dcpath.endswith('/'): + dcpath += '/' + + # Check for full path first in case it was already supplied + if self.folder.startswith( + dcpath + self.params["datacenter"] + "/vm" + ) or self.folder.startswith(dcpath + "/" + self.params["datacenter"] + "/vm"): + fullpath = self.folder + elif self.folder.startswith("/vm/") or self.folder == "/vm": + fullpath = "%s%s%s" % (dcpath, self.params["datacenter"], self.folder) + elif self.folder.startswith("/"): + fullpath = "%s%s/vm%s" % (dcpath, self.params["datacenter"], self.folder) + else: + fullpath = "%s%s/vm/%s" % (dcpath, self.params["datacenter"], self.folder) + + f_obj = self.content.searchIndex.FindByInventoryPath(fullpath) + + # abort if no strategy was successful + if f_obj is None: + # Add some debugging values in failure. + details = { + 'datacenter': datacenter.name, + 'datacenter_path': dcpath, + 'folder': self.folder, + 'full_search_path': fullpath, + } + self.module.fail_json(msg='No folder %s matched in the search path : %s' % (self.folder, fullpath), + details=details) + + destfolder = f_obj + + if self.params['template']: + vm_obj = self.get_vm_or_template(template_name=self.params['template']) + if vm_obj is None: + self.module.fail_json(msg="Could not find a template named %(template)s" % self.params) + else: + vm_obj = None + + # always get a resource_pool + resource_pool = self.get_resource_pool() + + # set the destination datastore for VM & disks + if self.params['datastore']: + # Give precedence to datastore value provided by user + # User may want to deploy VM to specific datastore. + datastore_name = self.params['datastore'] + # Check if user has provided datastore cluster first + datastore_cluster = self.cache.find_obj(self.content, [vim.StoragePod], datastore_name) + if datastore_cluster: + # If user specified datastore cluster so get recommended datastore + datastore_name = self.get_recommended_datastore(datastore_cluster_obj=datastore_cluster) + # Check if get_recommended_datastore or user specified datastore exists or not + datastore = self.cache.find_obj(self.content, [vim.Datastore], datastore_name) + else: + (datastore, datastore_name) = self.select_datastore(vm_obj) + + self.configspec = vim.vm.ConfigSpec() + self.configspec.deviceChange = [] + # create the relocation spec + self.relospec = vim.vm.RelocateSpec() + self.relospec.deviceChange = [] + self.configure_guestid(vm_obj=vm_obj, vm_creation=True) + self.configure_cpu_and_memory(vm_obj=vm_obj, vm_creation=True) + self.configure_hardware_params(vm_obj=vm_obj) + self.configure_encryption_params(vm_obj=vm_obj) + self.configure_resource_alloc_info(vm_obj=vm_obj) + self.configure_vapp_properties(vm_obj=vm_obj) + self.configure_disks(vm_obj=vm_obj) + self.configure_network(vm_obj=vm_obj) + self.configure_cdrom(vm_obj=vm_obj) + self.configure_nvdimm(vm_obj=vm_obj) + + # Find if we need network customizations (find keys in dictionary that requires customizations) + network_changes = False + for nw in self.params['networks']: + for key in nw: + # We don't need customizations for these keys + if key == 'type' and nw['type'] == 'dhcp': + network_changes = True + break + if key not in ('device_type', 'mac', 'name', 'vlan', 'type', 'start_connected', 'dvswitch_name'): + network_changes = True + break + + if any(v is not None for v in self.params['customization'].values()) or network_changes or self.params.get('customization_spec') is not None: + self.customize_vm(vm_obj=vm_obj) + + clonespec = None + clone_method = None + try: + if self.params['template']: + # Only select specific host when ESXi hostname is provided + if self.params['esxi_hostname']: + self.relospec.host = self.select_host() + self.relospec.datastore = datastore + + # Convert disk present in template if is set + if self.params['convert']: + for device in vm_obj.config.hardware.device: + if isinstance(device, vim.vm.device.VirtualDisk): + disk_locator = vim.vm.RelocateSpec.DiskLocator() + disk_locator.diskBackingInfo = vim.vm.device.VirtualDisk.FlatVer2BackingInfo() + if self.params['convert'] == 'thin': + disk_locator.diskBackingInfo.thinProvisioned = True + if self.params['convert'] == 'eagerzeroedthick': + disk_locator.diskBackingInfo.eagerlyScrub = True + if self.params['convert'] == 'thick': + disk_locator.diskBackingInfo.diskMode = "persistent" + disk_locator.diskId = device.key + disk_locator.datastore = datastore + self.relospec.disk.append(disk_locator) + + # https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.RelocateSpec.html + # > pool: For a clone operation from a template to a virtual machine, this argument is required. + self.relospec.pool = resource_pool + linked_clone = self.params.get('linked_clone') + snapshot_src = self.params.get('snapshot_src', None) + if linked_clone: + if snapshot_src is not None: + self.relospec.diskMoveType = vim.vm.RelocateSpec.DiskMoveOptions.createNewChildDiskBacking + else: + self.module.fail_json(msg="Parameter 'linked_src' and 'snapshot_src' are" + " required together for linked clone operation.") + + clonespec = vim.vm.CloneSpec(template=self.params['is_template'], location=self.relospec) + if self.customspec: + clonespec.customization = self.customspec + + if snapshot_src is not None: + if vm_obj.snapshot is None: + self.module.fail_json(msg="No snapshots present for virtual machine or template [%(template)s]" % self.params) + snapshot = self.get_snapshots_by_name_recursively(snapshots=vm_obj.snapshot.rootSnapshotList, + snapname=snapshot_src) + if len(snapshot) != 1: + self.module.fail_json(msg='virtual machine "%(template)s" does not contain' + ' snapshot named "%(snapshot_src)s"' % self.params) + + clonespec.snapshot = snapshot[0].snapshot + + clonespec.config = self.configspec + clone_method = 'Clone' + try: + task = vm_obj.Clone(folder=destfolder, name=self.params['name'], spec=clonespec) + except vim.fault.NoPermission as e: + self.module.fail_json(msg="Failed to clone virtual machine %s to folder %s " + "due to permission issue: %s" % (self.params['name'], + destfolder, + to_native(e.msg))) + self.change_detected = True + else: + # ConfigSpec require name for VM creation + self.configspec.name = self.params['name'] + self.configspec.files = vim.vm.FileInfo(logDirectory=None, + snapshotDirectory=None, + suspendDirectory=None, + vmPathName="[" + datastore_name + "]") + esx_host = None + # Only select specific host when ESXi hostname is provided + if self.params['esxi_hostname']: + esx_host = self.select_host() + + clone_method = 'CreateVM_Task' + try: + task = destfolder.CreateVM_Task(config=self.configspec, pool=resource_pool, host=esx_host) + except vmodl.fault.InvalidRequest as e: + self.module.fail_json(msg="Failed to create virtual machine due to invalid configuration " + "parameter %s" % to_native(e.msg)) + except vim.fault.RestrictedVersion as e: + self.module.fail_json(msg="Failed to create virtual machine due to " + "product versioning restrictions: %s" % to_native(e.msg)) + self.change_detected = True + self.wait_for_task(task) + except TypeError as e: + self.module.fail_json(msg="TypeError was returned, please ensure to give correct inputs. %s" % to_text(e)) + + if task.info.state == 'error': + # https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2021361 + # https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2173 + + # provide these to the user for debugging + clonespec_json = serialize_spec(clonespec) + configspec_json = serialize_spec(self.configspec) + kwargs = { + 'changed': self.change_applied, + 'failed': True, + 'msg': task.info.error.msg, + 'clonespec': clonespec_json, + 'configspec': configspec_json, + 'clone_method': clone_method + } + + return kwargs + else: + # set annotation + vm = task.info.result + if self.params['annotation']: + annotation_spec = vim.vm.ConfigSpec() + annotation_spec.annotation = str(self.params['annotation']) + task = vm.ReconfigVM_Task(annotation_spec) + self.wait_for_task(task) + if task.info.state == 'error': + return {'changed': self.change_applied, 'failed': True, 'msg': task.info.error.msg, 'op': 'annotation'} + + if self.params['advanced_settings']: + vm_custom_spec = vim.vm.ConfigSpec() + self.customize_advanced_settings(vm_obj=vm, config_spec=vm_custom_spec) + task = vm.ReconfigVM_Task(vm_custom_spec) + self.wait_for_task(task) + if task.info.state == 'error': + return {'changed': self.change_applied, 'failed': True, 'msg': task.info.error.msg, 'op': 'advanced_settings'} + + if self.params['customvalues']: + self.customize_customvalues(vm_obj=vm) + + if self.params['wait_for_ip_address'] or self.params['wait_for_customization'] or self.params['state'] in ['poweredon', 'powered-on', 'restarted']: + set_vm_power_state(self.content, vm, 'poweredon', force=False) + + if self.params['wait_for_ip_address']: + wait_for_vm_ip(self.content, vm, self.params['wait_for_ip_address_timeout']) + + if self.params['wait_for_customization']: + is_customization_ok = self.wait_for_customization(vm=vm, timeout=self.params['wait_for_customization_timeout']) + if not is_customization_ok: + vm_facts = self.gather_facts(vm) + return {'changed': self.change_applied, 'failed': True, + 'msg': 'Customization failed. For detailed information see warnings', + 'instance': vm_facts, 'op': 'customization'} + + vm_facts = self.gather_facts(vm) + return {'changed': self.change_applied, 'failed': False, 'instance': vm_facts} + + def get_snapshots_by_name_recursively(self, snapshots, snapname): + snap_obj = [] + for snapshot in snapshots: + if snapshot.name == snapname: + snap_obj.append(snapshot) + else: + snap_obj = snap_obj + self.get_snapshots_by_name_recursively(snapshot.childSnapshotList, snapname) + return snap_obj + + def reconfigure_vm(self): + self.configspec = vim.vm.ConfigSpec() + self.configspec.deviceChange = [] + # create the relocation spec + self.relospec = vim.vm.RelocateSpec() + self.relospec.deviceChange = [] + self.configure_guestid(vm_obj=self.current_vm_obj) + self.configure_cpu_and_memory(vm_obj=self.current_vm_obj) + self.configure_hardware_params(vm_obj=self.current_vm_obj) + self.configure_encryption_params(vm_obj=self.current_vm_obj) + self.configure_disks(vm_obj=self.current_vm_obj) + self.configure_network(vm_obj=self.current_vm_obj) + self.configure_cdrom(vm_obj=self.current_vm_obj) + self.configure_nvdimm(vm_obj=self.current_vm_obj) + self.customize_advanced_settings(vm_obj=self.current_vm_obj, config_spec=self.configspec) + self.customize_customvalues(vm_obj=self.current_vm_obj) + self.configure_resource_alloc_info(vm_obj=self.current_vm_obj) + self.configure_vapp_properties(vm_obj=self.current_vm_obj) + + if self.params['annotation'] and self.current_vm_obj.config.annotation != self.params['annotation']: + self.configspec.annotation = str(self.params['annotation']) + self.change_detected = True + + if self.params['resource_pool']: + self.relospec.pool = self.get_resource_pool() + + if self.relospec.pool != self.current_vm_obj.resourcePool: + self.tracked_changes['resourcePool'] = str(self.relospec.pool) + if self.module.check_mode: + self.change_applied = True + else: + task = self.current_vm_obj.RelocateVM_Task(spec=self.relospec) + self.wait_for_task(task) + if task.info.state == 'error': + return {'changed': self.change_applied, 'failed': True, 'msg': task.info.error.msg, 'op': 'relocate'} + + # Only send VMware task if we see a modification + if self.change_detected: + self.tracked_changes['configspec'] = str(self.configspec) + if self.module.check_mode: + self.change_applied = True + else: + task = None + try: + task = self.current_vm_obj.ReconfigVM_Task(spec=self.configspec) + except vim.fault.RestrictedVersion as e: + self.module.fail_json(msg="Failed to reconfigure virtual machine due to" + " product versioning restrictions: %s" % to_native(e.msg)) + self.wait_for_task(task) + if task.info.state == 'error': + return {'changed': self.change_applied, 'failed': True, 'msg': task.info.error.msg, 'op': 'reconfig'} + + # Rename VM + if self.params['uuid'] and self.params['name'] and self.params['name'] != self.current_vm_obj.config.name: + self.tracked_changes['name'] = self.params['name'] + if self.module.check_mode: + self.change_applied = True + else: + task = self.current_vm_obj.Rename_Task(self.params['name']) + self.wait_for_task(task) + if task.info.state == 'error': + return {'changed': self.change_applied, 'failed': True, 'msg': task.info.error.msg, 'op': 'rename'} + + # Mark VM as Template + if self.params['is_template'] and not self.current_vm_obj.config.template: + try: + if not self.module.check_mode: + self.current_vm_obj.MarkAsTemplate() + self.change_applied = True + self.tracked_changes['MarkAsTemplate'] = True + except vmodl.fault.NotSupported as e: + self.module.fail_json(msg="Failed to mark virtual machine [%s] " + "as template: %s" % (self.params['name'], e.msg)) + + # Mark Template as VM + elif not self.params['is_template'] and self.current_vm_obj.config.template: + resource_pool = self.get_resource_pool() + kwargs = dict(pool=resource_pool) + + if self.params.get('esxi_hostname', None): + host_system_obj = self.select_host() + kwargs.update(host=host_system_obj) + + try: + if not self.module.check_mode: + self.current_vm_obj.MarkAsVirtualMachine(**kwargs) + self.change_applied = True + self.tracked_changes['MarkAsVirtualMachine'] = True + except vim.fault.InvalidState as invalid_state: + self.module.fail_json(msg="Virtual machine is not marked" + " as template : %s" % to_native(invalid_state.msg)) + except vim.fault.InvalidDatastore as invalid_ds: + self.module.fail_json(msg="Converting template to virtual machine" + " operation cannot be performed on the" + " target datastores: %s" % to_native(invalid_ds.msg)) + except vim.fault.CannotAccessVmComponent as cannot_access: + self.module.fail_json(msg="Failed to convert template to virtual machine" + " as operation unable access virtual machine" + " component: %s" % to_native(cannot_access.msg)) + except vmodl.fault.InvalidArgument as invalid_argument: + self.module.fail_json(msg="Failed to convert template to virtual machine" + " due to : %s" % to_native(invalid_argument.msg)) + except Exception as generic_exc: + self.module.fail_json(msg="Failed to convert template to virtual machine" + " due to generic error : %s" % to_native(generic_exc)) + + # add customize existing VM after VM re-configure + if self.params['customization']['existing_vm']: + if self.current_vm_obj.config.template: + self.module.fail_json(msg="VM is template, not support guest OS customization.") + if self.current_vm_obj.runtime.powerState != vim.VirtualMachinePowerState.poweredOff and not self.module.check_mode: + self.module.fail_json(msg="VM is not in poweroff state, can not do guest OS customization.") + # TODO not sure if it is possible to query the current customspec to compare against the one being provided to check in check mode. + # Maybe by breaking down the individual fields and querying, but it needs more research. + # For now, assume changed... + self.tracked_changes['customization'] = True + if self.module.check_mode: + self.change_applied = True + else: + cus_result = self.customize_exist_vm() + if cus_result['failed']: + return cus_result + + vm_facts = self.gather_facts(self.current_vm_obj) + return {'changed': self.change_applied, 'failed': False, 'instance': vm_facts, 'changes': self.tracked_changes} + + def customize_exist_vm(self): + task = None + # Find if we need network customizations (find keys in dictionary that requires customizations) + network_changes = False + for nw in self.params['networks']: + for key in nw: + # We don't need customizations for these keys + if key not in ('device_type', 'mac', 'name', 'vlan', 'type', 'start_connected', 'dvswitch_name'): + network_changes = True + break + if any(v is not None for v in self.params['customization'].values()) or network_changes or self.params.get('customization_spec'): + self.customize_vm(vm_obj=self.current_vm_obj) + try: + task = self.current_vm_obj.CustomizeVM_Task(self.customspec) + except vim.fault.CustomizationFault as e: + self.module.fail_json(msg="Failed to customization virtual machine due to CustomizationFault: %s" % to_native(e.msg)) + except vim.fault.RuntimeFault as e: + self.module.fail_json(msg="failed to customization virtual machine due to RuntimeFault: %s" % to_native(e.msg)) + except Exception as e: + self.module.fail_json(msg="failed to customization virtual machine due to fault: %s" % to_native(e.msg)) + self.wait_for_task(task) + if task.info.state == 'error': + return {'changed': self.change_applied, 'failed': True, 'msg': task.info.error.msg, 'op': 'customize_exist'} + + if self.params['wait_for_customization']: + set_vm_power_state(self.content, self.current_vm_obj, 'poweredon', force=False) + is_customization_ok = self.wait_for_customization(vm=self.current_vm_obj, timeout=self.params['wait_for_customization_timeout']) + if not is_customization_ok: + return {'changed': self.change_applied, 'failed': True, + 'msg': 'Customization failed. For detailed information see warnings', + 'op': 'wait_for_customize_exist'} + + return {'changed': self.change_applied, 'failed': False} + + def wait_for_task(self, task, poll_interval=1): + """ + Wait for a VMware task to complete. Terminal states are 'error' and 'success'. + + Inputs: + - task: the task to wait for + - poll_interval: polling interval to check the task, in seconds + + Modifies: + - self.change_applied + """ + # https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.Task.html + # https://www.vmware.com/support/developer/vc-sdk/visdk25pubs/ReferenceGuide/vim.TaskInfo.html + # https://github.com/virtdevninja/pyvmomi-community-samples/blob/master/samples/tools/tasks.py + while task.info.state not in ['error', 'success']: + time.sleep(poll_interval) + self.change_applied = self.change_applied or task.info.state == 'success' + + def get_vm_events(self, vm, eventTypeIdList): + byEntity = vim.event.EventFilterSpec.ByEntity(entity=vm, recursion="self") + filterSpec = vim.event.EventFilterSpec(entity=byEntity, eventTypeId=eventTypeIdList) + eventManager = self.content.eventManager + return eventManager.QueryEvent(filterSpec) + + def wait_for_customization(self, vm, timeout=3600, sleep=10): + poll = int(timeout // sleep) + thispoll = 0 + while thispoll <= poll: + eventStarted = self.get_vm_events(vm, ['CustomizationStartedEvent']) + if len(eventStarted): + thispoll = 0 + while thispoll <= poll: + eventsFinishedResult = self.get_vm_events(vm, ['CustomizationSucceeded', 'CustomizationFailed']) + if len(eventsFinishedResult): + if not isinstance(eventsFinishedResult[0], vim.event.CustomizationSucceeded): + self.module.warn("Customization failed with error {%s}:{%s}" + % (eventsFinishedResult[0]._wsdlName, eventsFinishedResult[0].fullFormattedMessage)) + return False + else: + return True + else: + time.sleep(sleep) + thispoll += 1 + if len(eventsFinishedResult) == 0: + self.module.warn('Waiting for customization result event timed out.') + return False + else: + time.sleep(sleep) + thispoll += 1 + if len(eventStarted): + self.module.warn('Waiting for customization result event timed out.') + else: + self.module.warn('Waiting for customization start event timed out.') + return False + + +def main(): + argument_spec = vmware_argument_spec() + argument_spec.update( + state=dict(type='str', default='present', + choices=['absent', 'poweredoff', 'powered-off', + 'poweredon', 'powered-on', 'present', + 'rebootguest', 'reboot-guest', 'restarted', + 'shutdownguest', 'shutdown-guest', 'suspended']), + template=dict(type='str', aliases=['template_src']), + is_template=dict(type='bool', default=False), + annotation=dict(type='str', aliases=['notes']), + customvalues=dict(type='list', default=[], elements='dict'), + advanced_settings=dict(type='list', default=[], elements='dict'), + name=dict(type='str'), + name_match=dict(type='str', choices=['first', 'last'], default='first'), + uuid=dict(type='str'), + use_instance_uuid=dict(type='bool', default=False), + folder=dict(type='str'), + guest_id=dict(type='str'), + disk=dict( + type='list', + default=[], + elements='dict', + options=dict( + autoselect_datastore=dict(type='bool'), + controller_number=dict(type='int', choices=[0, 1, 2, 3]), + controller_type=dict(type='str', choices=['buslogic', 'lsilogic', 'paravirtual', 'lsilogicsas', 'sata', 'nvme']), + datastore=dict(type='str'), + disk_mode=dict(type='str', choices=['persistent', 'independent_persistent', 'independent_nonpersistent']), + filename=dict(type='str'), + size=dict(type='str'), + size_gb=dict(type='int'), + size_kb=dict(type='int'), + size_mb=dict(type='int'), + size_tb=dict(type='int'), + type=dict(type='str', choices=['thin', 'eagerzeroedthick', 'thick']), + unit_number=dict(type='int'), + ) + ), + nvdimm=dict( + type='dict', + default={}, + options=dict( + state=dict(type='str', choices=['present', 'absent']), + label=dict(type='str'), + size_mb=dict(type='int', default=1024), + ) + ), + cdrom=dict( + type='list', + default=[], + elements='dict', + options=dict( + type=dict(type='str', choices=['none', 'client', 'iso'], default='client'), + iso_path=dict(type='str'), + controller_type=dict(type='str', choices=['ide', 'sata'], default='ide'), + controller_number=dict(type='int'), + unit_number=dict(type='int'), + state=dict(type='str', choices=['present', 'absent'], default='present'), + ) + ), + hardware=dict( + type='dict', + default={}, + options=dict( + boot_firmware=dict(type='str', choices=['bios', 'efi']), + cpu_limit=dict(type='int'), + cpu_reservation=dict(type='int'), + hotadd_cpu=dict(type='bool'), + hotadd_memory=dict(type='bool'), + hotremove_cpu=dict(type='bool'), + vpmc_enabled=dict(type='bool'), + max_connections=dict(type='int'), + mem_limit=dict(type='int'), + cpu_shares_level=dict(type='str', choices=['low', 'normal', 'high', 'custom']), + mem_shares_level=dict(type='str', choices=['low', 'normal', 'high', 'custom']), + cpu_shares=dict(type='int'), + mem_shares=dict(type='int'), + mem_reservation=dict(type='int', aliases=['memory_reservation']), + memory_mb=dict(type='int'), + memory_reservation_lock=dict(type='bool'), + nested_virt=dict(type='bool'), + num_cpu_cores_per_socket=dict(type='int'), + num_cpus=dict(type='int'), + scsi=dict(type='str', choices=['buslogic', 'lsilogic', 'lsilogicsas', 'paravirtual']), + secure_boot=dict(type='bool'), + version=dict(type='str'), + virt_based_security=dict(type='bool'), + iommu=dict(type='bool') + )), + encryption=dict( + type='dict', + default={}, + options=dict( + encrypted_vmotion=dict(type='str', choices=['disabled', 'opportunistic', 'required']), + encrypted_ft=dict(type='str', choices=['disabled', 'opportunistic', 'required']) + )), + force=dict(type='bool', default=False), + datacenter=dict(type='str', default='ha-datacenter'), + esxi_hostname=dict(type='str'), + cluster=dict(type='str'), + wait_for_ip_address=dict(type='bool', default=False), + wait_for_ip_address_timeout=dict(type='int', default=300), + state_change_timeout=dict(type='int', default=0), + snapshot_src=dict(type='str'), + linked_clone=dict(type='bool', default=False), + networks=dict(type='list', default=[], elements='dict'), + resource_pool=dict(type='str'), + customization=dict( + type='dict', + default={}, + options=dict( + autologon=dict(type='bool'), + autologoncount=dict(type='int'), + dns_servers=dict(type='list', elements='str'), + dns_suffix=dict(type='list', elements='str'), + domain=dict(type='str'), + domainadmin=dict(type='str'), + domainadminpassword=dict(type='str', no_log=True), + existing_vm=dict(type='bool'), + fullname=dict(type='str'), + hostname=dict(type='str'), + hwclockUTC=dict(type='bool'), + joindomain=dict(type='str'), + joinworkgroup=dict(type='str'), + orgname=dict(type='str'), + password=dict(type='str', no_log=True), + productid=dict(type='str'), + runonce=dict(type='list', elements='str'), + script_text=dict(type='str'), + timezone=dict(type='str') + )), + customization_spec=dict(type='str', default=None), + wait_for_customization=dict(type='bool', default=False), + wait_for_customization_timeout=dict(type='int', default=3600), + vapp_properties=dict(type='list', default=[], elements='dict'), + datastore=dict(type='str'), + convert=dict(type='str', choices=['thin', 'thick', 'eagerzeroedthick']), + delete_from_inventory=dict(type='bool', default=False), + ) + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True, + mutually_exclusive=[ + ['cluster', 'esxi_hostname'], + ], + required_one_of=[ + ['name', 'uuid'], + ], + ) + result = {'failed': False, 'changed': False} + pyv = PyVmomiHelper(module) + + # Check requirements for virtualization based security + if pyv.params['hardware']['virt_based_security']: + if not pyv.params['hardware']['nested_virt']: + pyv.module.fail_json(msg="Virtualization based security requires nested virtualization. Please enable nested_virt.") + + if not pyv.params['hardware']['secure_boot']: + pyv.module.fail_json(msg="Virtualization based security requires (U)EFI secure boot. Please enable secure_boot.") + + if not pyv.params['hardware']['iommu']: + pyv.module.fail_json(msg="Virtualization based security requires I/O MMU. Please enable iommu.") + + # Check if the VM exists before continuing + vm = pyv.get_vm() + + # VM already exists + if vm: + if module.params['state'] == 'absent': + # destroy it + if module.check_mode: + result.update( + vm_name=vm.name, + changed=True, + current_powerstate=vm.summary.runtime.powerState.lower(), + desired_operation='remove_vm', + ) + module.exit_json(**result) + if module.params['force']: + # has to be poweredoff first + set_vm_power_state(pyv.content, vm, 'poweredoff', module.params['force']) + result = pyv.remove_vm(vm, module.params['delete_from_inventory']) + elif module.params['state'] == 'present': + # Note that check_mode is handled inside reconfigure_vm + result = pyv.reconfigure_vm() + elif module.params['state'] in ['poweredon', 'powered-on', 'poweredoff', + 'powered-off', 'restarted', 'suspended', + 'shutdownguest', 'shutdown-guest', + 'rebootguest', 'reboot-guest']: + if module.check_mode: + # Identify if the power state would have changed if not in check mode + current_powerstate = vm.summary.runtime.powerState.lower() + powerstate_will_change = False + if ((current_powerstate == 'poweredon' and module.params['state'] not in ['poweredon', 'powered-on']) + or (current_powerstate == 'poweredoff' and module.params['state'] + not in ['poweredoff', 'powered-off', 'shutdownguest', 'shutdown-guest']) + or (current_powerstate == 'suspended' and module.params['state'] != 'suspended')): + powerstate_will_change = True + + result.update( + vm_name=vm.name, + changed=powerstate_will_change, + current_powerstate=current_powerstate, + desired_operation='set_vm_power_state', + ) + module.exit_json(**result) + # set powerstate + tmp_result = set_vm_power_state(pyv.content, vm, module.params['state'], module.params['force'], module.params['state_change_timeout']) + if tmp_result['changed']: + result["changed"] = True + if module.params['state'] in ['poweredon', 'powered-on', 'restarted', 'rebootguest', 'reboot-guest'] and module.params['wait_for_ip_address']: + wait_result = wait_for_vm_ip(pyv.content, vm, module.params['wait_for_ip_address_timeout']) + if not wait_result: + module.fail_json(msg='Waiting for IP address timed out') + tmp_result['instance'] = wait_result + if not tmp_result["failed"]: + result["failed"] = False + result['instance'] = tmp_result['instance'] + if tmp_result["failed"]: + result["failed"] = True + result["msg"] = tmp_result["msg"] + else: + # This should not happen + raise AssertionError() + # VM doesn't exist + else: + if module.params['state'] in ['poweredon', 'powered-on', 'poweredoff', 'powered-off', + 'present', 'restarted', 'suspended']: + if module.check_mode: + result.update( + changed=True, + desired_operation='deploy_vm', + ) + module.exit_json(**result) + result = pyv.deploy_vm() + if result['failed']: + module.fail_json(msg='Failed to create a virtual machine : %s' % result['msg']) + + if result['failed']: + module.fail_json(**result) + else: + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/vmware_guest_powerstate.py b/plugins/modules/vmware_guest_powerstate.py new file mode 100644 index 00000000..3001afad --- /dev/null +++ b/plugins/modules/vmware_guest_powerstate.py @@ -0,0 +1,348 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Abhijeet Kasurde +# 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: vmware_guest_powerstate +short_description: Manages power states of virtual machines in vCenter +description: +- Power on / Power off / Restart a virtual machine. +author: +- Abhijeet Kasurde (@Akasurde) +options: + datacenter: + description: + - The datacenter where the VM you'd like to operate the power. + default: ha-datacenter + type: str + state: + description: + - Set the state of the virtual machine. + choices: [ powered-off, powered-on, reboot-guest, restarted, shutdown-guest, suspended, present] + default: present + type: str + name: + description: + - Name of the virtual machine to work with. + - Virtual machine names in vCenter are not necessarily unique, which may be problematic, see O(name_match). + type: str + name_match: + description: + - If multiple virtual machines matching the name, use the first or last found. + default: first + choices: [ first, last ] + type: str + uuid: + description: + - UUID of the instance to manage if known, this is VMware's unique identifier. + - This is required if O(name) or O(moid) is not supplied. + type: str + moid: + description: + - Managed Object ID of the instance to manage if known, this is a unique identifier only within a single vCenter instance. + - This is required if O(name) or O(uuid) is not supplied. + type: str + use_instance_uuid: + description: + - Whether to use the VMware instance UUID rather than the BIOS UUID. + default: false + type: bool + folder: + description: + - Destination folder, absolute or relative path to find an existing guest. + - The folder should include the datacenter. ESX's datacenter is ha-datacenter + - 'Examples:' + - ' folder: /ha-datacenter/vm' + - ' folder: ha-datacenter/vm' + - ' folder: /datacenter1/vm' + - ' folder: datacenter1/vm' + - ' folder: /datacenter1/vm/folder1' + - ' folder: datacenter1/vm/folder1' + - ' folder: /folder1/datacenter1/vm' + - ' folder: folder1/datacenter1/vm' + - ' folder: /folder1/datacenter1/vm/folder2' + type: str + scheduled_at: + description: + - Date and time in string format at which specified task needs to be performed. + - "The required format for date and time - 'dd/mm/yyyy hh:mm'." + - Scheduling task requires vCenter server. A standalone ESXi server does not support this option. + type: str + schedule_task_name: + description: + - Name of schedule task. + - Valid only if O(scheduled_at) is specified. + type: str + required: false + schedule_task_description: + description: + - Description of schedule task. + - Valid only if O(scheduled_at) is specified. + type: str + required: false + schedule_task_enabled: + description: + - Flag to indicate whether the scheduled task is enabled or disabled. + type: bool + required: false + default: true + force: + description: + - Ignore warnings and complete the actions. + - This parameter is useful while forcing virtual machine state. + default: false + type: bool + state_change_timeout: + description: + - If the O(state=shutdown-guest), by default the module will return immediately after sending the shutdown signal. + - If this argument is set to a positive integer, the module will instead wait for the VM to reach the poweredoff state. + - The value sets a timeout in seconds for the module to wait for the state change. + default: 0 + type: int + answer: + description: + - A list of questions to answer, should one or more arise while waiting for the task to complete. + - Some common uses are to allow a cdrom to be changed even if locked, or to answer the question as to whether a VM was copied or moved. + - Can be used if O(state=powered-on). + suboptions: + question: + description: + - The message id, for example C(msg.uuid.altered). + type: str + required: true + response: + description: + - The choice key, for example C(button.uuid.copiedTheVM). + type: str + required: true + type: list + elements: dict +extends_documentation_fragment: +- community.vmware.vmware.documentation +""" + +EXAMPLES = r""" +- name: Set the state of a virtual machine to poweroff + vmware.vmware.vmware_guest_powerstate: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "/{{ datacenter_name }}/vm/my_folder" + name: "{{ guest_name }}" + state: powered-off + delegate_to: localhost + register: deploy + +- name: Set the state of a virtual machine to poweron using MoID + vmware.vmware.vmware_guest_powerstate: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "/{{ datacenter_name }}/vm/my_folder" + moid: vm-42 + state: powered-on + delegate_to: localhost + register: deploy + +- name: Set the state of a virtual machine to poweroff at given scheduled time + vmware.vmware.vmware_guest_powerstate: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "/{{ datacenter_name }}/vm/my_folder" + name: "{{ guest_name }}" + state: powered-off + scheduled_at: "09/01/2018 10:18" + schedule_task_name: "task_00001" + schedule_task_description: "Sample task to poweroff VM" + schedule_task_enabled: true + delegate_to: localhost + register: deploy_at_schedule_datetime + +- name: Wait for the virtual machine to shutdown + vmware.vmware.vmware_guest_powerstate: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ guest_name }}" + state: shutdown-guest + state_change_timeout: 200 + delegate_to: localhost + register: deploy + +- name: Automatically answer if a question locked a virtual machine + block: + - name: Power on a virtual machine without the answer param + vmware.vmware.vmware_guest_powerstate: + hostname: "{{ esxi_hostname }}" + username: "{{ esxi_username }}" + password: "{{ esxi_password }}" + validate_certs: false + folder: "{{ f1 }}" + name: "{{ vm_name }}" + state: powered-on + rescue: + - name: Power on a virtual machine with the answer param + vmware.vmware.vmware_guest_powerstate: + hostname: "{{ esxi_hostname }}" + username: "{{ esxi_username }}" + password: "{{ esxi_password }}" + validate_certs: false + folder: "{{ f1 }}" + name: "{{ vm_name }}" + answer: + - question: "msg.uuid.altered" + response: "button.uuid.copiedTheVM" + state: powered-on +""" + +RETURN = r""" # """ + +try: + from pyVmomi import vim, vmodl +except ImportError: + pass + +from random import randint +from datetime import datetime +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vmware.vmware.plugins.module_utils.vmware import PyVmomi, set_vm_power_state, vmware_argument_spec, \ + check_answer_question_status, make_answer_response, answer_question, gather_vm_facts +from ansible.module_utils._text import to_native + + +def main(): + argument_spec = vmware_argument_spec() + argument_spec.update( + datacenter=dict(type='str', default='ha-datacenter'), + state=dict(type='str', default='present', + choices=['present', 'powered-off', 'powered-on', 'reboot-guest', 'restarted', 'shutdown-guest', 'suspended']), + name=dict(type='str'), + name_match=dict(type='str', choices=['first', 'last'], default='first'), + uuid=dict(type='str'), + moid=dict(type='str'), + use_instance_uuid=dict(type='bool', default=False), + folder=dict(type='str'), + force=dict(type='bool', default=False), + scheduled_at=dict(type='str'), + schedule_task_name=dict(), + schedule_task_description=dict(), + schedule_task_enabled=dict(type='bool', default=True), + state_change_timeout=dict(type='int', default=0), + answer=dict(type='list', + elements='dict', + options=dict( + question=dict(type='str', required=True), + response=dict(type='str', required=True) + )) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=False, + mutually_exclusive=[ + ['name', 'uuid', 'moid'], + ['scheduled_at', 'answer'] + ], + ) + + result = dict(changed=False,) + + if module.params['folder']: + module.params['folder'] = module.params['folder'].rstrip('/') + + pyv = PyVmomi(module) + + # Check if the VM exists before continuing + vm = pyv.get_vm() + + if vm: + # VM already exists, so set power state + scheduled_at = module.params.get('scheduled_at', None) + if scheduled_at: + if not pyv.is_vcenter(): + module.fail_json(msg="Scheduling task requires vCenter, hostname %s " + "is an ESXi server." % module.params.get('hostname')) + powerstate = { + 'present': vim.VirtualMachine.PowerOn, + 'powered-off': vim.VirtualMachine.PowerOff, + 'powered-on': vim.VirtualMachine.PowerOn, + 'reboot-guest': vim.VirtualMachine.RebootGuest, + 'restarted': vim.VirtualMachine.Reset, + 'shutdown-guest': vim.VirtualMachine.ShutdownGuest, + 'suspended': vim.VirtualMachine.Suspend, + } + dt = '' + try: + dt = datetime.strptime(scheduled_at, '%d/%m/%Y %H:%M') + except ValueError as e: + module.fail_json(msg="Failed to convert given date and time string to Python datetime object," + "please specify string in 'dd/mm/yyyy hh:mm' format: %s" % to_native(e)) + schedule_task_spec = vim.scheduler.ScheduledTaskSpec() + schedule_task_name = module.params['schedule_task_name'] or 'task_%s' % str(randint(10000, 99999)) + schedule_task_desc = module.params['schedule_task_description'] + if schedule_task_desc is None: + schedule_task_desc = 'Schedule task for vm %s for ' \ + 'operation %s at %s' % (vm.name, module.params['state'], scheduled_at) + schedule_task_spec.name = schedule_task_name + schedule_task_spec.description = schedule_task_desc + schedule_task_spec.scheduler = vim.scheduler.OnceTaskScheduler() + schedule_task_spec.scheduler.runAt = dt + schedule_task_spec.action = vim.action.MethodAction() + schedule_task_spec.action.name = powerstate[module.params['state']] + schedule_task_spec.enabled = module.params['schedule_task_enabled'] + + try: + pyv.content.scheduledTaskManager.CreateScheduledTask(vm, schedule_task_spec) + # As this is async task, we create scheduled task and mark state to changed. + module.exit_json(changed=True) + except vim.fault.InvalidName as e: + module.fail_json(msg="Failed to create scheduled task %s for %s : %s" % (module.params.get('state'), + vm.name, + to_native(e.msg))) + except vim.fault.DuplicateName as e: + module.exit_json(changed=False, details=to_native(e.msg)) + except vmodl.fault.InvalidArgument as e: + module.fail_json(msg="Failed to create scheduled task %s as specifications " + "given are invalid: %s" % (module.params.get('state'), + to_native(e.msg))) + else: + # Check if a virtual machine is locked by a question + if check_answer_question_status(vm) and module.params['answer']: + try: + responses = make_answer_response(vm, module.params['answer']) + answer_question(vm, responses) + except Exception as e: + module.fail_json(msg="%s" % e) + + # Wait until a virtual machine is unlocked + while True: + if check_answer_question_status(vm) is False: + break + + result['changed'] = True + result['instance'] = gather_vm_facts(pyv.content, vm) + else: + result = set_vm_power_state(pyv.content, vm, module.params['state'], module.params['force'], module.params['state_change_timeout'], + module.params['answer']) + result['answer'] = module.params['answer'] + else: + id = module.params.get('uuid') or module.params.get('moid') or module.params.get('name') + module.fail_json(msg="Unable to set power state for non-existing virtual machine : '%s'" % id) + + if result.get('failed') is True: + module.fail_json(**result) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/prepare_vmware_tests/meta/main.yml b/tests/integration/targets/prepare_vmware_tests/meta/main.yml new file mode 100644 index 00000000..61d3ffe4 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/meta/main.yml @@ -0,0 +1,2 @@ +--- +allow_duplicates: true diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/init_real_lab.yml b/tests/integration/targets/prepare_vmware_tests/tasks/init_real_lab.yml new file mode 100644 index 00000000..51c5d34f --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/init_real_lab.yml @@ -0,0 +1,31 @@ +--- +- include_tasks: teardown_with_esxi.yml + when: esxi_hosts|length > 0 +- include_tasks: teardown.yml + +- when: setup_esxi_instance is not defined + block: + - include_tasks: setup_datacenter.yml + - include_tasks: setup_cluster.yml + - include_tasks: setup_attach_hosts.yml + when: setup_attach_host is defined + - include_tasks: move_host_out_of_cluster.yml + when: move_host_out_of_cluster is defined + - include_tasks: setup_datastore.yml + when: setup_datastore is defined + - include_tasks: setup_virtualmachines.yml + when: setup_virtualmachines is defined + - include_tasks: setup_switch.yml + when: setup_switch is defined + - include_tasks: setup_dvswitch.yml + when: setup_dvswitch is defined + - include_tasks: setup_resource_pool.yml + when: setup_resource_pool is defined + - include_tasks: setup_category.yml + when: setup_category is defined + - include_tasks: setup_tag.yml + when: setup_tag is defined + - include_tasks: setup_content_library.yml + when: setup_content_library is defined + - include_tasks: setup_dvs_portgroup.yml + when: setup_dvs_portgroup is defined diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/main.yml b/tests/integration/targets/prepare_vmware_tests/tasks/main.yml new file mode 100644 index 00000000..5e134588 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/main.yml @@ -0,0 +1,20 @@ +--- + +- name: load vmware common vars + include_vars: + file: common.yml + +- when: esxi1_hostname is not defined and esxi2_hostname is not defined + include_vars: + file: vcenter_only.yml + +- when: esxi1_hostname is defined and esxi2_hostname is not defined + include_vars: + file: vcenter_1esxi.yml + +- when: esxi1_hostname is defined and esxi2_hostname is defined + include_vars: + file: vcenter_2esxi.yml + +- name: Init lab + include_tasks: init_real_lab.yml diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/move_host_out_of_cluster.yml b/tests/integration/targets/prepare_vmware_tests/tasks/move_host_out_of_cluster.yml new file mode 100644 index 00000000..d4a68d64 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/move_host_out_of_cluster.yml @@ -0,0 +1,35 @@ +--- +- name: Enter maintenance mode + vmware_maintenancemode: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + esxi_hostname: "{{ item }}" + state: present + with_items: "{{ esxi_hosts }}" + ignore_errors: true + +- name: Move ESXi out of Cluster + vmware_host: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "{{ dc1 }}/host" + esxi_hostname: "{{ item }}" + state: reconnect + with_items: "{{ esxi_hosts }}" + ignore_errors: true + +- name: Exit maintenance mode + vmware_maintenancemode: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + esxi_hostname: "{{ item }}" + state: absent + with_items: "{{ esxi_hosts }}" + ignore_errors: true diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_attach_hosts.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_attach_hosts.yml new file mode 100644 index 00000000..38c8adf3 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_attach_hosts.yml @@ -0,0 +1,54 @@ +- fail: + msg: "No ESXi hosts defined. esxi_hosts is empty." + when: "esxi_hosts|length == 0" + +- name: Add ESXi Hosts to vCenter + vmware_host: + datacenter_name: '{{ dc1 }}' + cluster_name: '{{ ccr1 }}' + esxi_hostname: '{{ item }}' + esxi_username: '{{ esxi_user }}' + esxi_password: '{{ esxi_password }}' + state: add_or_reconnect + with_items: "{{ esxi_hosts }}" + +- name: Disable the Maintenance Mode + vmware_maintenancemode: + esxi_hostname: '{{ item }}' + state: absent + with_items: "{{ esxi_hosts }}" + +# A bit of explanation here. Our test VMs will produce some ARP traffic, +# if we keep the "VM Network" on vSwitch0, the VM MAC address will +# be visible by the external switch port. Depending on the +# antispoofing policy in place, the switch may just decide to block +# the port. +- name: Remove any potential existing "VM Network" on vSwitch0 + vmware_portgroup: + esxi_hostname: '{{ item }}' + switch: vSwitch0 + portgroup: VM Network + validate_certs: false + state: absent + ignore_errors: true + with_items: "{{ esxi_hosts }}" + +- name: Add an isolated VMware vSwitch + vmware_vswitch: + hostname: '{{ item }}' + username: '{{ esxi_user }}' + password: '{{ esxi_password }}' + switch: isolated_vSwitch + with_items: "{{ esxi_hosts }}" + +- name: Add Management Network VM Portgroup + vmware_portgroup: + esxi_hostname: '{{ esxi_hosts }}' + switch: isolated_vSwitch + portgroup: VM Network + validate_certs: false + register: _vm_network_portgroup + until: _vm_network_portgroup is succeeded + retries: 10 + delay: 1 + failed_when: _vm_network_portgroup is failure diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_category.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_category.yml new file mode 100644 index 00000000..17e3cd81 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_category.yml @@ -0,0 +1,9 @@ +- name: Create a category for cluster + vmware_category: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + category_name: '{{ cluster_category }}' + category_description: '{{ cluster_category }} description' + state: present diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_cluster.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_cluster.yml new file mode 100644 index 00000000..608dbdd2 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_cluster.yml @@ -0,0 +1,10 @@ +- name: Create Cluster + vmware_cluster: + datacenter_name: '{{ dc1 }}' + cluster_name: '{{ ccr1 }}' + +- name: Enable DRS on Cluster + vmware_cluster_drs: + datacenter_name: '{{ dc1 }}' + cluster_name: '{{ ccr1 }}' + enable: true diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_content_library.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_content_library.yml new file mode 100644 index 00000000..26affd53 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_content_library.yml @@ -0,0 +1,7 @@ +- name: Create Content Library + vmware_content_library_manager: + library_name: test-content-lib + library_description: 'Library created by the prepare_vmware_tests role' + library_type: local + datastore_name: '{{ rw_datastore }}' + state: present diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_datacenter.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_datacenter.yml new file mode 100644 index 00000000..a5f3eafe --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_datacenter.yml @@ -0,0 +1,11 @@ +- name: Create Datacenter + vmware_datacenter: + datacenter_name: '{{ dc1 }}' + state: present + +- name: Create a VM folder on given Datacenter + vcenter_folder: + datacenter: '{{ dc1 }}' + folder_name: '{{ f0 }}' + folder_type: vm + state: present diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_datastore.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_datastore.yml new file mode 100644 index 00000000..c1f42abd --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_datastore.yml @@ -0,0 +1,38 @@ +--- +- name: Mount NFS (ro_datastore) datastores to ESXi + vmware_host_datastore: + hostname: '{{ item }}' + username: '{{ esxi_user }}' + password: '{{ esxi_password }}' + datastore_name: '{{ ro_datastore }}' + datastore_type: '{{ infra.datastores[ro_datastore].type }}' + nfs_server: '{{ infra.datastores[ro_datastore].server }}' + nfs_path: '{{ infra.datastores[ro_datastore].path }}' + nfs_ro: '{{ infra.datastores[ro_datastore].ro }}' + state: present + validate_certs: false + with_items: "{{ esxi_hosts }}" + +- name: Mount NFS (rw_datastore) datastores on the ESXi + vmware_host_datastore: + hostname: '{{ item }}' + username: '{{ esxi_user }}' + password: '{{ esxi_password }}' + datastore_name: '{{ rw_datastore }}' + datastore_type: '{{ infra.datastores[rw_datastore].type }}' + nfs_server: '{{ infra.datastores[rw_datastore].server }}' + nfs_path: '{{ infra.datastores[rw_datastore].path }}' + nfs_ro: '{{ infra.datastores[rw_datastore].ro }}' + state: present + validate_certs: false + with_items: "{{ esxi_hosts }}" + +- name: The vcenter needs a bit of time to refresh the DS list + vmware_datastore_info: + validate_certs: false + cluster: '{{ ccr1 }}' + register: setup_datastore_datatstore_info + failed_when: setup_datastore_datatstore_info.datastores|selectattr('type', 'equalto', 'NFS')|list|length != 2 + until: setup_datastore_datatstore_info is succeeded + retries: 60 + delay: 1 diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvs_portgroup.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvs_portgroup.yml new file mode 100644 index 00000000..4d4cf96c --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvs_portgroup.yml @@ -0,0 +1,20 @@ +--- +- name: create basic DVS portgroup + vmware_dvs_portgroup: + switch_name: "{{ dvswitch1 }}" + portgroup_name: '{{ dvpg1 }}' + vlan_id: 0 + num_ports: 32 + port_binding: 'static' + port_allocation: 'fixed' + state: present + +- name: Create the DVS PG with slash in name + vmware_dvs_portgroup: + portgroup_name: '{{ dvpg_with_slash }}' + switch_name: '{{ dvswitch1 }}' + vlan_id: 0 + num_ports: 120 + port_binding: 'static' + port_allocation: 'fixed' + state: present diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvswitch.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvswitch.yml new file mode 100644 index 00000000..0d589d8c --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvswitch.yml @@ -0,0 +1,20 @@ +--- +- name: Create the DVSwitch + vmware_dvswitch: + datacenter_name: '{{ dc1 }}' + switch_name: '{{ dvswitch1 }}' + switch_version: 6.6.0 + mtu: 9000 + uplink_quantity: 2 + discovery_proto: lldp + discovery_operation: both + state: present +- name: Attach the hosts to the DVSwitch + vmware_dvs_host: + esxi_hostname: "{{ item }}" + switch_name: '{{ dvswitch1 }}' + vmnics: + - vmnic1 + state: present + with_items: "{{ esxi_hosts }}" + when: setup_attach_host is defined diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_resource_pool.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_resource_pool.yml new file mode 100644 index 00000000..01a17d29 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_resource_pool.yml @@ -0,0 +1,15 @@ +--- +- name: Add resource pool to vCenter + vmware_resource_pool: + datacenter: '{{ dc1 }}' + cluster: '{{ ccr1 }}' + resource_pool: DC0_C0_RP1 + mem_shares: normal + mem_limit: -1 + mem_reservation: 0 + mem_expandable_reservations: true + cpu_shares: normal + cpu_limit: -1 + cpu_reservation: 0 + cpu_expandable_reservations: true + state: present diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_switch.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_switch.yml new file mode 100644 index 00000000..c63a28c5 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_switch.yml @@ -0,0 +1,7 @@ +--- +- name: Add a VMware vSwitchs + vmware_vswitch: + esxi_hostname: '{{ item }}' + switch_name: "{{ switch1 }}" + state: present + with_items: "{{ esxi_hosts }}" diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_tag.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_tag.yml new file mode 100644 index 00000000..31b495c0 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_tag.yml @@ -0,0 +1,15 @@ +- name: Get Category facts + vmware_category_info: + register: cat_info + +- name: Get Category id for {{ cluster_category }} + set_fact: + cluster_category_id: "{{ cat_info.tag_category_info[0].category_id }}" + +- name: Create a tag for cluster + vmware_tag: + category_id: '{{ cluster_category_id }}' + tag_name: '{{ cluster_tag }}' + tag_description: '{{ cluster_tag }} Description' + state: present + when: cluster_category_id is defined diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_virtualmachines.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_virtualmachines.yml new file mode 100644 index 00000000..2b2b3928 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_virtualmachines.yml @@ -0,0 +1,51 @@ +--- +- name: Create VMs + vmware_guest: + datacenter: "{{ dc1 }}" + folder: '{{ item.folder }}' + name: '{{ item.name }}' + state: poweredoff + guest_id: debian8_64Guest + disk: + - size_mb: 10 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + version: 11 + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + with_items: '{{ virtual_machines }}' + + +- name: Create VMs in cluster + vmware_guest: + datacenter: "{{ dc1 }}" + folder: '{{ item.folder }}' + cluster: '{{ item.cluster }}' + name: '{{ item.name }}' + state: poweredoff + guest_id: debian8_64Guest + disk: + - size_gb: 1 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + with_items: '{{ virtual_machines_in_cluster }}' diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/teardown.yml b/tests/integration/targets/prepare_vmware_tests/tasks/teardown.yml new file mode 100644 index 00000000..b5e42118 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/teardown.yml @@ -0,0 +1,22 @@ +--- +- name: Get a list of all the datacenters + vmware.vmware_rest.vcenter_datacenter_info: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + register: existing_datacenters +- name: Force delete the existing DC + vmware.vmware_rest.vcenter_datacenter: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + state: absent + datacenter: '{{ item.datacenter }}' + force: true + with_items: "{{ existing_datacenters.value }}" + until: _result is succeeded + retries: 10 + delay: 1 + register: _result diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/teardown_with_esxi.yml b/tests/integration/targets/prepare_vmware_tests/tasks/teardown_with_esxi.yml new file mode 100644 index 00000000..db4c165d --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/tasks/teardown_with_esxi.yml @@ -0,0 +1,94 @@ +--- +- name: Collect the list of the existing VM + vmware.vmware_rest.vcenter_vm_info: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + register: existing_vms + until: existing_vms is not failed + +- name: Turn off the VM + vmware.vmware_rest.vcenter_vm_power: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + state: stop + vm: '{{ item.vm }}' + with_items: "{{ existing_vms.value }}" + ignore_errors: true + +- name: Delete the VMs + vmware.vmware_rest.vcenter_vm: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + state: absent + vm: '{{ item.vm }}' + with_items: "{{ existing_vms.value }}" + when: + - not item.name.startswith("vCLS") + + +- name: Build a list of local libraries + vmware.vmware_rest.content_locallibrary_info: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + register: result + retries: 100 + delay: 3 + until: result is not failed + +- name: Delete all the local libraries + vmware.vmware_rest.content_locallibrary: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + library_id: "{{ item.id }}" + state: absent + with_items: "{{ result.value }}" + +- name: Build a list of subscribed libraries + vmware.vmware_rest.content_subscribedlibrary_info: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + register: result + +- name: Delete all the subscribed libraries + vmware.vmware_rest.content_subscribedlibrary: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + library_id: "{{ item.id }}" + state: absent + with_items: "{{ result.value }}" + +- name: Get a list of all the datacenters + vmware.vmware_rest.vcenter_datacenter_info: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + register: existing_datacenters +- name: Force delete the existing DC + vmware.vmware_rest.vcenter_datacenter: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + state: absent + datacenter: '{{ item.datacenter }}' + force: true + with_items: "{{ existing_datacenters.value }}" + until: _result is succeeded + retries: 10 + delay: 1 + register: _result diff --git a/tests/integration/targets/prepare_vmware_tests/vars/common.yml b/tests/integration/targets/prepare_vmware_tests/vars/common.yml new file mode 100644 index 00000000..bfe5a30f --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/vars/common.yml @@ -0,0 +1,12 @@ +--- +dc1: DC0 +ccr1: DC0_C0 +f0: F0 +switch1: switch1 +esxi1: '{{ esxi_hosts[0] }}' +esxi2: '{{ esxi_hosts[1] }}' +esxi3: '{{ esxi_hosts[2] }}' +dvswitch1: DVS0 +esxi_user: root +dvpg1: DC0_DVPG0 +dvpg_with_slash: DVPG/1 diff --git a/tests/integration/targets/prepare_vmware_tests/vars/vcenter_1esxi.yml b/tests/integration/targets/prepare_vmware_tests/vars/vcenter_1esxi.yml new file mode 100644 index 00000000..45e94331 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/vars/vcenter_1esxi.yml @@ -0,0 +1,33 @@ +--- +esxi_hosts: + - esxi1.test +rw_datastore: rw_datastore +ro_datastore: ro_datastore +esxi_password: '{{ esxi1_password }}' +esxi_user: '{{ esxi1_username }}' +infra: + datastores: + rw_datastore: + type: nfs + server: datastore.test + path: /srv/share/vms + ro: false + ro_datastore: + type: nfs + server: datastore.test + path: /srv/share/isos + ro: true +virtual_machines: + - name: DC0_H0_VM0 + folder: '{{ f0 }}' + - name: DC0_H0_VM1 + folder: '{{ f0 }}' +virtual_machines_in_cluster: + - name: DC0_C0_RP0_VM0 + folder: '{{ f0 }}' + cluster: '{{ ccr1 }}' + - name: DC0_C0_RP0_VM1 + folder: '{{ f0 }}' + cluster: '{{ ccr1 }}' +cluster_tag: test_cluster_tag_0001 +cluster_category: test_cluster_cat_0001 diff --git a/tests/integration/targets/prepare_vmware_tests/vars/vcenter_2esxi.yml b/tests/integration/targets/prepare_vmware_tests/vars/vcenter_2esxi.yml new file mode 100644 index 00000000..6b0203b4 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/vars/vcenter_2esxi.yml @@ -0,0 +1,34 @@ +--- +esxi_hosts: + - esxi1.test + - esxi2.test +rw_datastore: rw_datastore +ro_datastore: ro_datastore +esxi_password: '{{ esxi1_password }}' +esxi_user: '{{ esxi1_username }}' +infra: + datastores: + rw_datastore: + type: nfs + server: datastore.test + path: /srv/share/vms + ro: false + ro_datastore: + type: nfs + server: datastore.test + path: /srv/share/isos + ro: true +virtual_machines: + - name: DC0_H0_VM0 + folder: '{{ f0 }}' + - name: DC0_H0_VM1 + folder: '{{ f0 }}' +virtual_machines_in_cluster: + - name: DC0_C0_RP0_VM0 + folder: '{{ f0 }}' + cluster: '{{ ccr1 }}' + - name: DC0_C0_RP0_VM1 + folder: '{{ f0 }}' + cluster: '{{ ccr1 }}' +cluster_tag: test_cluster_tag_0001 +cluster_category: test_cluster_cat_0001 diff --git a/tests/integration/targets/prepare_vmware_tests/vars/vcenter_only.yml b/tests/integration/targets/prepare_vmware_tests/vars/vcenter_only.yml new file mode 100644 index 00000000..58560b56 --- /dev/null +++ b/tests/integration/targets/prepare_vmware_tests/vars/vcenter_only.yml @@ -0,0 +1,6 @@ +--- +esxi_hosts: [] +infra: +virtual_machines: [] +virtual_machines_in_cluster: [] +#esxi_password: '' \ No newline at end of file diff --git a/tests/integration/targets/vmware_guest/aliases b/tests/integration/targets/vmware_guest/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest/defaults/main.yml b/tests/integration/targets/vmware_guest/defaults/main.yml new file mode 100644 index 00000000..04690fb5 --- /dev/null +++ b/tests/integration/targets/vmware_guest/defaults/main.yml @@ -0,0 +1,30 @@ +vmware_guest_test_playbooks: + - boot_firmware_d1_c1_f0.yml + - boot_firmware_d1_c1_f0.yml + - cdrom_d1_c1_f0.yml + - check_mode.yml + - clone_customize_guest_test.yml + - clone_d1_c1_f0.yml + - clone_resize_disks.yml + - clone_with_convert.yml + - create_d1_c1_f0.yml + - create_guest_invalid_d1_c1_f0.yml + - create_nw_d1_c1_f0.yml + - create_rp_d1_c1_f0.yml + - delete_vm.yml + - disk_mode_d1_c1_f0.yml + - disk_size_d1_c1_f0.yml + - disk_type_d1_c1_f0.yml + - mac_address_d1_c1_f0.yml + - max_connections.yml + - mem_reservation.yml + - shares.yml + - multiple_disk_controllers_d1_c1_f0.yml + - network_negative_test.yml + - network_with_device.yml + - non_existent_vm_ops.yml + - vapp_d1_c1_f0.yml + - reconfig_vm_to_latest_version.yml + - remove_vm_from_inventory.yml + - create_vm_using_special_characters.yml + - advanced_settings.yml diff --git a/tests/integration/targets/vmware_guest/tasks/advanced_settings.yml b/tests/integration/targets/vmware_guest/tasks/advanced_settings.yml new file mode 100644 index 00000000..b60a7ba4 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/advanced_settings.yml @@ -0,0 +1,97 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2021, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Create new VM for advance settings + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ f0 }}" + register: r + +- debug: var=r + +- name: Assert that changes were made + assert: + that: + - r is changed + +- name: Change advance settings for VM in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + datacenter: "{{ dc1 }}" + state: present + folder: "{{ f0 }}" + advanced_settings: + - key: "svga.present" + value: "FALSE" + register: r + check_mode: true + +- debug: var=r + +- name: Assert that changes would be made + assert: + that: + - r is changed + +- name: Change advance settings for VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + datacenter: "{{ dc1 }}" + state: present + folder: "{{ f0 }}" + advanced_settings: + - key: "svga.present" + value: "FALSE" + register: r + +- debug: var=r + +- name: Assert that changes were made + assert: + that: + - r is changed + +- name: Again change advance settings for VM (idempotency) + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + datacenter: "{{ dc1 }}" + state: present + folder: "{{ f0 }}" + advanced_settings: + - key: "svga.present" + value: "FALSE" + register: r + +- debug: var=r + +- name: Assert that changes were not made + assert: + that: + - r is not changed diff --git a/tests/integration/targets/vmware_guest/tasks/boot_firmware_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/boot_firmware_d1_c1_f0.yml new file mode 100644 index 00000000..469d2e60 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/boot_firmware_d1_c1_f0.yml @@ -0,0 +1,111 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new VMs with boot_firmware as 'bios' + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + boot_firmware: "bios" + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: "{{ f0 }}" + register: clone_d1_c1_f0 + +- debug: var=clone_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - clone_d1_c1_f0 is changed + +- name: create new VMs again with boot_firmware as 'bios' + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm2 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + boot_firmware: "bios" + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: "{{ f0 }}" + register: clone_d1_c1_f0 +- debug: var=clone_d1_c1_f0 +- name: assert that changes were not made + assert: + that: + - clone_d1_c1_f0 is changed + +- name: create new VMs with boot_firmware as 'efi' + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm3 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + boot_firmware: "efi" + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: "{{ f0 }}" + register: clone_d1_c1_f0 + +- debug: var=clone_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - clone_d1_c1_f0 is changed + +- name: create new VMs again with boot_firmware as 'efi' + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm3 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + boot_firmware: "efi" + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: "{{ f0 }}" + register: clone_d1_c1_f0 +- debug: var=clone_d1_c1_f0 +- name: assert that changes were not made + assert: + that: + - not (clone_d1_c1_f0 is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/cdrom_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/cdrom_d1_c1_f0.yml new file mode 100644 index 00000000..3e8ccb44 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/cdrom_d1_c1_f0.yml @@ -0,0 +1,246 @@ +# this is used to test create and configure VM with a single CDROM +- name: Create VM with CDROM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm1 + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: centos64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] centos.iso" + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was created + assert: + that: + - "cdrom_vm.changed == true" + +- name: Update CDROM to iso for the new VM in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "vm" + name: test_vm1 + datastore: "{{ rw_datastore }}" + datacenter: "{{ dc1 }}" + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + state: present + register: cdrom_vm + check_mode: true + +- debug: var=cdrom_vm + +- name: assert the VM would be changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Update CDROM to iso for the new VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "vm" + name: test_vm1 + datastore: "{{ rw_datastore }}" + datacenter: "{{ dc1 }}" + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + state: present + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Update CDROM to client for the new VM in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm1 + datacenter: "{{ dc1 }}" + cdrom: + - controller_number: 0 + unit_number: 0 + type: client + state: present + register: cdrom_vm + check_mode: true + +- debug: var=cdrom_vm + +- name: assert the VM would be changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Update CDROM to client for the new VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm1 + datacenter: "{{ dc1 }}" + cdrom: + - controller_number: 0 + unit_number: 0 + type: client + state: present + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: clone vm + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm2 + template: test_vm1 + datacenter: "{{ dc1 }}" + state: poweredoff + folder: vm + convert: thin + +- name: Update CDROM to none for the new VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm2 + datacenter: "{{ dc1 }}" + cdrom: + - controller_number: 0 + unit_number: 0 + type: none + state: present + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Create VM with multiple disks and a CDROM - GitHub issue 38679 + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "{{ f0 }}" + name: test_vm3 + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: centos64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was created + assert: + that: + - "cdrom_vm.changed == true" + +- name: Again create VM with multiple disks and a CDROM - GitHub issue 38679 + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "{{ f0 }}" + name: test_vm3 + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: centos64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] base.iso" + register: cdrom_vm +- debug: var=cdrom_vm +- name: assert the VM was created + assert: + that: + - cdrom_vm is changed diff --git a/tests/integration/targets/vmware_guest/tasks/cdrom_d1_c1_f1.yml b/tests/integration/targets/vmware_guest/tasks/cdrom_d1_c1_f1.yml new file mode 100644 index 00000000..f18a4b4a --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/cdrom_d1_c1_f1.yml @@ -0,0 +1,365 @@ +# this is used to test create and configure VM with multiple CDROMs +# attached to IDE or SATA +- name: Create VM with multiple CDROMs attached to IDE + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm1 + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: centos64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + cdrom: + - controller_type: ide + controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] centos.iso" + - controller_type: ide + controller_number: 0 + unit_number: 1 + type: client + - controller_number: 1 + unit_number: 0 + type: none + - controller_number: 1 + unit_number: 1 + type: client + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was created + assert: + that: + - "cdrom_vm.changed == true" + +- name: Remove the last 2 CDROMs and update the first 2 for the new VM in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm1 + datacenter: "{{ dc1 }}" + cdrom: + - controller_type: ide + controller_number: 0 + unit_number: 0 + type: client + - controller_type: ide + controller_number: 0 + unit_number: 1 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + - controller_type: ide + controller_number: 1 + unit_number: 0 + state: absent + - controller_type: ide + controller_number: 1 + unit_number: 1 + state: absent + state: present + register: cdrom_vm + check_mode: true + +- debug: var=cdrom_vm + +- name: assert the VM would be changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Remove the last 2 CDROMs and update the first 2 for the new VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm1 + datacenter: "{{ dc1 }}" + cdrom: + - controller_type: ide + controller_number: 0 + unit_number: 0 + type: client + - controller_type: ide + controller_number: 0 + unit_number: 1 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + - controller_type: ide + controller_number: 1 + unit_number: 0 + state: absent + - controller_type: ide + controller_number: 1 + unit_number: 1 + state: absent + state: present + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Create VM with multiple CDROMs attached to SATA + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm2 + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: centos64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + cdrom: + - controller_type: sata + controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] centos.iso" + - controller_type: sata + controller_number: 1 + unit_number: 1 + type: client + - controller_type: sata + controller_number: 2 + unit_number: 0 + type: none + - controller_type: sata + controller_number: 3 + unit_number: 1 + type: client + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was created + assert: + that: + - "cdrom_vm.changed == true" + +- name: Remove the first 2 CDROMs and update the last 2 for the new VM in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm2 + datacenter: "{{ dc1 }}" + cdrom: + - controller_type: sata + controller_number: 0 + unit_number: 0 + state: absent + - controller_type: sata + controller_number: 1 + unit_number: 1 + state: absent + - controller_type: sata + controller_number: 2 + unit_number: 0 + type: client + - controller_type: sata + controller_number: 3 + unit_number: 1 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + state: present + register: cdrom_vm + check_mode: true + +- debug: var=cdrom_vm + +- name: assert the VM would be changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Remove the first 2 CDROMs and update the last 2 for the new VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm2 + datacenter: "{{ dc1 }}" + cdrom: + - controller_type: sata + controller_number: 0 + unit_number: 0 + state: absent + - controller_type: sata + controller_number: 1 + unit_number: 1 + state: absent + - controller_type: sata + controller_number: 2 + unit_number: 0 + type: client + - controller_type: sata + controller_number: 3 + unit_number: 1 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + state: present + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Create VM with multiple CDROMs attached to IDE and SATA + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm3 + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: centos64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + cdrom: + - controller_type: ide + controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] centos.iso" + - controller_type: sata + controller_number: 0 + unit_number: 0 + type: client + - controller_type: ide + controller_number: 1 + unit_number: 0 + type: none + - controller_type: sata + controller_number: 1 + unit_number: 0 + type: client + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was created + assert: + that: + - "cdrom_vm.changed == true" + +- name: Remove the 2 CDROMs and update the other 2 for the new VM in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm3 + datacenter: "{{ dc1 }}" + cdrom: + - controller_type: ide + controller_number: 0 + unit_number: 0 + state: absent + - controller_type: sata + controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + - controller_type: ide + controller_number: 1 + unit_number: 0 + type: client + - controller_type: sata + controller_number: 1 + unit_number: 0 + state: absent + state: present + register: cdrom_vm + check_mode: true + +- debug: var=cdrom_vm + +- name: assert the VM would be changed + assert: + that: + - "cdrom_vm.changed == true" + +- name: Remove the 2 CDROMs and update the other 2 for the new VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + name: test_vm3 + datacenter: "{{ dc1 }}" + cdrom: + - controller_type: ide + controller_number: 0 + unit_number: 0 + state: absent + - controller_type: sata + controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + - controller_type: ide + controller_number: 1 + unit_number: 0 + type: client + - controller_type: sata + controller_number: 1 + unit_number: 0 + state: absent + state: present + register: cdrom_vm + +- debug: var=cdrom_vm + +- name: assert the VM was changed + assert: + that: + - "cdrom_vm.changed == true" diff --git a/tests/integration/targets/vmware_guest/tasks/check_mode.yml b/tests/integration/targets/vmware_guest/tasks/check_mode.yml new file mode 100644 index 00000000..ebab33cb --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/check_mode.yml @@ -0,0 +1,87 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Create VMs + vmware_guest: + datacenter: "{{ dc1 }}" + folder: '{{ f0 }}' + name: DC0_H0_VM0 + state: poweredoff + guest_id: debian8_64Guest + disk: + - size_mb: 10 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + version: 11 + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + +- name: Perform all operation in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + datacenter: "{{ dc1 }}" + state: "{{ item }}" + with_items: + - absent + - present + - poweredoff + - powered-off + - poweredon + - powered-on + - restarted + - suspended + - shutdownguest + - shutdown-guest + - rebootguest + - reboot-guest + register: check_mode_state + check_mode: true + +- name: assert that changes were appropriate for an existing powered-off VM + assert: + that: + - "check_mode_state.results|map(attribute='changed') == [true, false, false, false, true, true, true, true, false, false, true, true]" + #- "check_mode_state.results|map(attribute='vm_name')|unique|list == [ virtual_machines[0].name ]" + +- name: Perform all operation on non-existent VM in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: non_existent_vm + datacenter: "{{ dc1 }}" + state: "{{ item }}" + with_items: + - present + - poweredoff + - powered-off + - poweredon + - powered-on + - restarted + - suspended + register: check_mode_state + check_mode: true + +- debug: + var: check_mode_state + +- name: assert that changes were made + assert: + that: + - "check_mode_state.results|map(attribute='changed')|unique|list == [true]" + - "check_mode_state.results|map(attribute='desired_operation')|unique|list == ['deploy_vm']" diff --git a/tests/integration/targets/vmware_guest/tasks/clone_customize_guest_test.yml b/tests/integration/targets/vmware_guest/tasks/clone_customize_guest_test.yml new file mode 100644 index 00000000..0945b8aa --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/clone_customize_guest_test.yml @@ -0,0 +1,71 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2019, Pavan Bidkar +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Create VMs + vmware_guest: + datacenter: "{{ dc1 }}" + folder: '{{ f0 }}' + name: DC0_H0_VM0 + state: poweredoff + guest_id: debian8_64Guest + disk: + - size_mb: 10 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + version: 11 + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + +- name: clone vm from template and customize GOS + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + template: DC0_H0_VM0 + datacenter: "{{ dc1 }}" + state: poweredoff + folder: "{{ f0 }}" + convert: thin + register: clone_customize + +- debug: + var: clone_customize + +- name: assert that changes were made + assert: + that: + - clone_customize is changed + +- name: clone vm from template and customize GOS again + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + template: "{{ virtual_machines[0].name }}" + datacenter: "{{ dc1 }}" + state: poweredoff + folder: "{{ virtual_machines[0].folder }}" + convert: thin + register: clone_customize_again + +- debug: + var: clone_customize_again + +- name: assert that changes were not made + assert: + that: + - not (clone_customize_again is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/clone_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/clone_d1_c1_f0.yml new file mode 100644 index 00000000..cc06dc60 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/clone_d1_c1_f0.yml @@ -0,0 +1,49 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2017, James Tanner +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new linked clone without specifying snapshot_src + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + template: "{{ virtual_machines[0].name }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + linked_clone: true + register: linked_clone_d1_c1_f0 + ignore_errors: true + +- debug: + var: linked_clone_d1_c1_f0 + +- name: assert that changes were not made + assert: + that: + - not (linked_clone_d1_c1_f0 is changed) + +- name: create new linked clone without specifying linked_clone + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm2 + template: "{{ virtual_machines[0].name }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + snapshot_src: "snap_shot1" + register: linked_clone_d1_c1_f0 + ignore_errors: true + +- debug: + var: linked_clone_d1_c1_f0 + +- name: assert that changes were not made + assert: + that: + - not (linked_clone_d1_c1_f0 is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/clone_resize_disks.yml b/tests/integration/targets/vmware_guest/tasks/clone_resize_disks.yml new file mode 100644 index 00000000..6f2b4c18 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/clone_resize_disks.yml @@ -0,0 +1,75 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2019, Noe Gonzalez +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: clone_resize_disks_original + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + folder: "{{ f0 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + guest_id: debian8_64Guest + disk: + - size_gb: 1 + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + +- name: convert to VM template + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: clone_resize_disks_original + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + folder: "{{ f0 }}" + is_template: true + +- name: clone template and modify disks + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: clone_resize_disks_clone + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + folder: "{{ f0 }}" + disk: + - size_gb: 2 + type: thin + datastore: "{{ rw_datastore }}" + - size_gb: 3 + type: thin + datastore: "{{ rw_datastore }}" + template: clone_resize_disks_original + state: poweredoff + register: l_clone_template_modify_disks + +- assert: + that: + - l_clone_template_modify_disks.changed | bool + +- name: delete VM clone & original template + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ item }}" + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + folder: "{{ f0 }}" + state: absent + with_items: + - clone_resize_disks_original + - clone_resize_disks_clone diff --git a/tests/integration/targets/vmware_guest/tasks/clone_with_convert.yml b/tests/integration/targets/vmware_guest/tasks/clone_with_convert.yml new file mode 100644 index 00000000..70c13589 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/clone_with_convert.yml @@ -0,0 +1,90 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Christophe FERREIRA +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Create VMs + vmware_guest: + datacenter: "{{ dc1 }}" + folder: '{{ f0 }}' + name: DC0_H0_VM0 + state: poweredoff + guest_id: debian8_64Guest + disk: + - size_mb: 10 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + version: 11 + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + +- name: clone vm from template and convert to thin + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + template: DC0_H0_VM0 + datacenter: "{{ dc1 }}" + state: poweredoff + folder: "{{ f0 }}" + convert: thin + register: clone_thin + +- debug: var=clone_thin + +- name: assert that changes were made + assert: + that: + - clone_thin is changed + +- name: clone vm from template and convert to thick + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm2 + template: "{{ virtual_machines[0].name }}" + datacenter: "{{ dc1 }}" + state: poweredoff + folder: "{{ virtual_machines[0].folder }}" + convert: thick + register: clone_thick + +- debug: var=clone_thick + +- name: assert that changes were made + assert: + that: + - clone_thick is changed + +- name: clone vm from template and convert to eagerzeroedthick + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm3 + template: "{{ virtual_machines[0].name }}" + datacenter: "{{ dc1 }}" + state: poweredoff + folder: "{{ virtual_machines[0].folder }}" + convert: eagerzeroedthick + register: clone_eagerzeroedthick + +- debug: var=clone_eagerzeroedthick + +- name: assert that changes were made + assert: + that: + - clone_eagerzeroedthick is changed diff --git a/tests/integration/targets/vmware_guest/tasks/create_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/create_d1_c1_f0.yml new file mode 100644 index 00000000..287bb209 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/create_d1_c1_f0.yml @@ -0,0 +1,162 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2017, James Tanner +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + num_cpu_cores_per_socket: 1 + memory_mb: 128 + hotadd_memory: true + hotadd_cpu: false + memory_reservation: 128 + memory_reservation_lock: false + nested_virt: true + hotremove_cpu: true + mem_limit: 8096 + mem_reservation: 4096 + cpu_limit: 8096 + cpu_reservation: 4096 + max_connections: 10 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: '{{ f0 }}' + register: clone_d1_c1_f0 + +- debug: var=clone_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - clone_d1_c1_f0 is changed + +- name: create the VM again + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + num_cpu_cores_per_socket: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: '{{ f0 }}' + register: clone_d1_c1_f0_recreate + +- debug: var=clone_d1_c1_f0_recreate + +- name: assert that no changes were made after re-creating + assert: + that: + - not (clone_d1_c1_f0_recreate is changed) + +- name: modify the new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 2 + memory_mb: 128 + state: present + folder: '{{ f0 }}' + register: clone_d1_c1_f0_modify + +- debug: var=clone_d1_c1_f0_modify + +- name: assert that changes were made with modification + assert: + that: + - clone_d1_c1_f0_modify is changed + +- name: re-modify the new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 2 + memory_mb: 128 + state: present + folder: '{{ f0 }}' + register: clone_d1_c1_f0_remodify + +- debug: var=clone_d1_c1_f0_remodify + +- name: assert that no changes were made when re-modified + assert: + that: + - not (clone_d1_c1_f0_remodify is changed) + +- name: delete the new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + #guest_id: centos64Guest + datacenter: "{{ dc1 }}" + state: absent + folder: '{{ f0 }}' + register: clone_d1_c1_f0_delete + +- debug: var=clone_d1_c1_f0_delete + +- name: assert that changes were made with deletion + assert: + that: + - clone_d1_c1_f0_delete is changed + +- name: re-delete the new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + #guest_id: centos64Guest + datacenter: "{{ dc1 }}" + state: absent + folder: '{{ f0 }}' + register: clone_d1_c1_f0_redelete + +- debug: var=clone_d1_c1_f0_redelete + +- name: assert that no changes were made with redeletion + assert: + that: + - not (clone_d1_c1_f0_redelete is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/create_guest_invalid_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/create_guest_invalid_d1_c1_f0.yml new file mode 100644 index 00000000..3f70bb8f --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/create_guest_invalid_d1_c1_f0.yml @@ -0,0 +1,30 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2017, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new virtual machine with invalid guest id + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: invalid_vm + guest_id: "invalid_guest_id" + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ f0 }}" + register: invalid_guest_0001_d1_c1_f0 + ignore_errors: true +- debug: var=invalid_guest_0001_d1_c1_f0 +- name: assert that changes were made + assert: + that: + - "not (invalid_guest_0001_d1_c1_f0 is changed)" + - "'configSpec.guestId' in invalid_guest_0001_d1_c1_f0['msg']" diff --git a/tests/integration/targets/vmware_guest/tasks/create_nw_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/create_nw_d1_c1_f0.yml new file mode 100644 index 00000000..41dfbf86 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/create_nw_d1_c1_f0.yml @@ -0,0 +1,38 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2017, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: 'VM Network' + device_type: vmxnet3 + ip: 192.168.10.1 + netmask: 255.255.255.0 + wake_on_lan: true + start_connected: true + allow_guest_control: true + state: poweredoff + folder: F0 + register: clone_d1_c1_f0 + +- debug: var=clone_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - clone_d1_c1_f0 is changed diff --git a/tests/integration/targets/vmware_guest/tasks/create_rp_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/create_rp_d1_c1_f0.yml new file mode 100644 index 00000000..0e4458c4 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/create_rp_d1_c1_f0.yml @@ -0,0 +1,208 @@ +# Create one with the defaults +- name: create new VM with default resource pool + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + version: 11 + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: F0 + register: clone_rp_d1_c1_f0 + +- debug: var=clone_rp_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - clone_rp_d1_c1_f0 is changed + +- name: delete the new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + #guest_id: centos64Guest + datacenter: "{{ dc1 }}" + state: absent + folder: F0 + register: clone_rp_d1_c1_f0_delete + +- debug: var=clone_rp_d1_c1_f0_delete + +- name: assert that changes were made with deletion + assert: + that: + - clone_rp_d1_c1_f0_delete is changed + +# now create with just a cluster +- name: create new VM with default resource pool in cluster + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + hardware: + version: 11 + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: F0 + register: clone_rpc_d1_c1_f0 + +- debug: var=clone_rpc_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - clone_rpc_d1_c1_f0 is changed + +- name: delete the new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + #guest_id: centos64Guest + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + state: absent + folder: F0 + register: clone_rpc_d1_c1_f0_delete + +- debug: var=clone_rpc_d1_c1_f0_delete + +- name: assert that changes were made with deletion + assert: + that: + - clone_rpc_d1_c1_f0_delete is changed + +# now create with a specific resource pool +- name: create new VM with specific resource pool in cluster + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: DC0_C0_RP1 + hardware: + version: 11 + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: F0 + register: clone_rpcp_d1_c1_f0 + +- debug: var=clone_rpcp_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - clone_rpcp_d1_c1_f0 is changed + +- name: delete the new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + #guest_id: centos64Guest + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + state: absent + folder: F0 + register: clone_rpcp_d1_c1_f0_delete + +- debug: var=clone_rpcp_d1_c1_f0_delete + +- name: assert that changes were made with deletion + assert: + that: + - clone_rpcp_d1_c1_f0_delete is changed + +# now create with a specific host +- name: create new VM with specific host + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + esxi_hostname: '{{ esxi1 }}' + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: F0 + register: clone_rph_d1_c1_f0 + +- debug: var=clone_rph_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - clone_rph_d1_c1_f0 is changed + +- name: delete the new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + #template: "{{ item|basename }}" + #guest_id: centos64Guest + datacenter: "{{ dc1 }}" + state: absent + folder: F0 + register: clone_rph_d1_c1_f0_delete + +- debug: var=clone_rph_d1_c1_f0_delete + +- name: assert that changes were made with deletion + assert: + that: + - clone_rph_d1_c1_f0_delete is changed diff --git a/tests/integration/targets/vmware_guest/tasks/create_vm_using_special_characters.yml b/tests/integration/targets/vmware_guest/tasks/create_vm_using_special_characters.yml new file mode 100644 index 00000000..3e6208f6 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/create_vm_using_special_characters.yml @@ -0,0 +1,81 @@ +- name: Create VM with special characters + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + name: 'test_vm\/%' + guest_id: debian8_64Guest + hardware: + memory_mb: 512 + num_cpus: 1 + scsi: paravirtual + disk: + - size_gb: 1 + type: thin + datastore: "{{ rw_datastore }}" + state: present + register: create_vm_with_special_characters_result + +- assert: + that: + - create_vm_with_special_characters_result.changed is sameas true + +- name: Create VM with special characters(idempotency check) + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + name: 'test_vm\/%' + guest_id: debian8_64Guest + hardware: + memory_mb: 512 + num_cpus: 1 + scsi: paravirtual + disk: + - size_gb: 1 + type: thin + datastore: "{{ rw_datastore }}" + state: present + register: create_vm_with_special_characters_idempotency_check_result + +- assert: + that: + - create_vm_with_special_characters_idempotency_check_result.changed is sameas false + +- name: Delete VM with special characters + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + name: 'test_vm\/%' + state: absent + register: delete_vm_with_special_characters_result + +- assert: + that: + - delete_vm_with_special_characters_result.changed is sameas true + +- name: Delete VM with special characters(idempotency check) + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + name: 'test_vm\/%' + state: absent + register: delete_vm_with_special_characters_idempotency_check_result + +- assert: + that: + - delete_vm_with_special_characters_idempotency_check_result.changed is sameas false diff --git a/tests/integration/targets/vmware_guest/tasks/delete_vm.yml b/tests/integration/targets/vmware_guest/tasks/delete_vm.yml new file mode 100644 index 00000000..ad0575f2 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/delete_vm.yml @@ -0,0 +1,82 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Delete VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: nothinghere + datacenter: "{{ dc1 }}" + state: absent + register: delete_vm + ignore_errors: true + +- debug: var=delete_vm + +- name: assert that changes were made + assert: + that: + - "not delete_vm.changed" + +- name: create new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_delete_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + num_cpu_cores_per_socket: 1 + memory_mb: 128 + hotadd_memory: true + hotadd_cpu: false + max_connections: 10 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: '{{ f0 }}' + register: clone_d1_c1_f0 + +- name: Delete VM using different datacenter and folder + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_delete_vm + datacenter: "{{ dc1 }}-fake" + folder: '{{ f0 }}-fake' + state: absent + register: delete_vm + ignore_errors: true + +- name: assert that VM was not deleted + assert: + that: + - not delete_vm.changed + +- name: Delete VM using right parameters value + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_delete_vm + datacenter: "{{ dc1 }}" + folder: '{{ f0 }}' + state: absent + register: delete_vm + ignore_errors: true + +- name: assert that VM was deleted + assert: + that: + - delete_vm.changed diff --git a/tests/integration/targets/vmware_guest/tasks/disk_mode_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/disk_mode_d1_c1_f0.yml new file mode 100644 index 00000000..e0029212 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/disk_mode_d1_c1_f0.yml @@ -0,0 +1,86 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new VMs with invalid disk mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: eagerzeroedthick + datastore: "{{ rw_datastore }}" + disk_mode: 'invalid_disk_mode' + state: poweredoff + folder: "{{ f0 }}" + register: test_vm1 + ignore_errors: true + +- debug: var=test_vm1 + +- name: assert that changes were not made + assert: + that: + - not(test_vm1 is changed) + +- name: create new VMs with valid disk mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: eagerzeroedthick + datastore: "{{ rw_datastore }}" + disk_mode: 'independent_persistent' + state: poweredoff + folder: "{{ f0 }}" + register: test_vm1_2 + +- debug: var=test_vm1_2 + +- name: assert that changes were made + assert: + that: + - test_vm1_2 is changed + +- name: create new VMs with valid disk mode again + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: eagerzeroedthick + datastore: "{{ rw_datastore }}" + disk_mode: 'independent_persistent' + state: poweredoff + folder: "{{ f0 }}" + register: test_vm1_2 +- debug: var=test_vm1_2 +- name: assert that changes were not made + assert: + that: + - not (test_vm1_2 is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/disk_size_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/disk_size_d1_c1_f0.yml new file mode 100644 index 00000000..0acc829c --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/disk_size_d1_c1_f0.yml @@ -0,0 +1,31 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new VMs with invalid disk size + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 0gb + type: eagerzeroedthick + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: "{{ f0 }}" + register: disk_size_d1_c1_f0 + ignore_errors: true + +- debug: var=disk_size_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - not (disk_size_d1_c1_f0 is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/disk_type_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/disk_type_d1_c1_f0.yml new file mode 100644 index 00000000..f0e83c6d --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/disk_type_d1_c1_f0.yml @@ -0,0 +1,33 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2017, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new VMs + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: eagerzeroedthick + datastore: "{{ rw_datastore }}" + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: poweredoff + folder: F0 + register: disk_type_d1_c1_f0 + +- debug: var=disk_type_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - disk_type_d1_c1_f0 is changed diff --git a/tests/integration/targets/vmware_guest/tasks/mac_address_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/mac_address_d1_c1_f0.yml new file mode 100644 index 00000000..45e3b274 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/mac_address_d1_c1_f0.yml @@ -0,0 +1,37 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2017, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: create new VMs with manual MAC address + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: VM Network + ip: 192.168.10.12 + netmask: 255.255.255.0 + gateway: 192.168.10.254 + mac: aa:bb:cc:dd:aa:42 + state: poweredoff + folder: vm + register: clone_d1_c1_f0 + +- debug: var=clone_d1_c1_f0 + +- name: assert that changes were made + assert: + that: + - "clone_d1_c1_f0['instance']['hw_eth0']['addresstype'] == 'manual'" + - "clone_d1_c1_f0['instance']['hw_eth0']['macaddress'] == 'aa:bb:cc:dd:aa:42'" diff --git a/tests/integration/targets/vmware_guest/tasks/main.yml b/tests/integration/targets/vmware_guest/tasks/main.yml new file mode 100644 index 00000000..67e4df30 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/main.yml @@ -0,0 +1,18 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2017, James Tanner +# 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_attach_host: true + setup_datacenter: true + setup_datastore: true + setup_dvswitch: true + setup_resource_pool: true + setup_dvs_portgroup: true + +- include_tasks: run_test_playbook.yml + with_items: '{{ vmware_guest_test_playbooks }}' + loop_control: + loop_var: test_playbook diff --git a/tests/integration/targets/vmware_guest/tasks/max_connections.yml b/tests/integration/targets/vmware_guest/tasks/max_connections.yml new file mode 100644 index 00000000..c46040ff --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/max_connections.yml @@ -0,0 +1,43 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2019, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- &add_mk_conn + name: Create new VM with max_connections as 4 + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + max_connections: 4 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ f0 }}" + register: mk_conn_result_0001 + +- debug: var=mk_conn_result_0001 + +- name: Assert that changes were made + assert: + that: + - mk_conn_result_0001 is changed + +- <<: *add_mk_conn + name: Again create new VMs again with max_connections as 4 + register: mk_conn_result_0002 + +- debug: var=mk_conn_result_0002 + +- name: Assert that changes were not made + assert: + that: + - not (mk_conn_result_0002 is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/mem_reservation.yml b/tests/integration/targets/vmware_guest/tasks/mem_reservation.yml new file mode 100644 index 00000000..f23b2de3 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/mem_reservation.yml @@ -0,0 +1,123 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2019, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- &add_mem_reserve + name: Create new VMs with mem_reservation as 0 + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + mem_reservation: 0 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ virtual_machines[0].folder }}" + register: mem_reserve_result_0001 + +- debug: var=mem_reserve_result_0001 + +- name: Assert that changes were made + assert: + that: + - mem_reserve_result_0001 is changed + +- <<: *add_mem_reserve + name: Again create new VMs with mem_reservation as 0 + register: mem_reserve_result_0002 + +- debug: var=mem_reserve_result_0002 + +- name: Assert that changes were not made + assert: + that: + - not (mem_reserve_result_0002 is changed) + +- &add_memory_reserve + name: Create new VM with memory_reservation as 0 + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm2 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + memory_reservation: 0 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ virtual_machines[0].folder }}" + register: memory_reserve_result_0003 + +- debug: var=memory_reserve_result_0003 + +- name: Assert that changes were made + assert: + that: + - memory_reserve_result_0003 is changed + +- <<: *add_memory_reserve + name: Again create new VMs with memory_reservation as 0 + register: memory_reserve_result_0004 + +- debug: var=memory_reserve_result_0004 + +- name: Assert that changes were not made + assert: + that: + - not (memory_reserve_result_0004 is changed) + +- &no_memory_reserve + name: Create new VMs without memory_reservation or mem_reservation + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm3 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + memory_reservation: 0 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ virtual_machines[0].folder }}" + register: no_memory_reserve_result_0005 + +- debug: var=no_memory_reserve_result_0005 + +- name: Assert that changes were made + assert: + that: + - no_memory_reserve_result_0005 is changed + +- <<: *no_memory_reserve + name: Again create new VMs without memory_reservation or mem_reservation + register: no_memory_reserve_result_0006 + +- debug: var=no_memory_reserve_result_0006 + +- name: Assert that changes were not made + assert: + that: + - not (no_memory_reserve_result_0006 is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/multiple_disk_controllers_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/multiple_disk_controllers_d1_c1_f0.yml new file mode 100644 index 00000000..9feddfb9 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/multiple_disk_controllers_d1_c1_f0.yml @@ -0,0 +1,216 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2020, Diane Wang +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +- name: create a new VM with multiple scsi controllers + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + folder: vm + cluster: "{{ ccr1 }}" + resource_pool: Resources + name: test_vm1 + guest_id: centos64Guest + datastore: "{{ rw_datastore }}" + hardware: + memory_mb: 512 + num_cpus: 1 + disk: + - controller_type: lsilogicsas + controller_number: 0 + unit_number: 0 + size_mb: 512 + type: thin + - controller_type: paravirtual + controller_number: 1 + unit_number: 0 + size_mb: 256 + type: eagerzeroedthick + register: multi_scsi_disk_vm + +- debug: var=multi_scsi_disk_vm + +- name: assert that VM was deployed + assert: + that: + - "multi_scsi_disk_vm.changed == true" + +- name: reconfigure created VM with multiple scsi controllers + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + folder: vm + cluster: "{{ ccr1 }}" + resource_pool: Resources + name: test_vm1 + datastore: "{{ rw_datastore }}" + state: present + disk: + - controller_type: lsilogicsas + controller_number: 0 + unit_number: 0 + size_mb: 512 + disk_mode: independent_persistent + - controller_type: paravirtual + controller_number: 1 + unit_number: 0 + size_mb: 512 + register: multi_scsi_disk_vm + +- debug: var=multi_scsi_disk_vm + +- name: assert that VM was configured + assert: + that: + - "multi_scsi_disk_vm.changed == true" + +- name: create a new VM with multiple sata controllers + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + name: test_vm2 + guest_id: centos64Guest + datastore: "{{ rw_datastore }}" + hardware: + memory_mb: 512 + num_cpus: 1 + disk: + - controller_type: sata + controller_number: 0 + unit_number: 0 + size_mb: 512 + disk_mode: independent_persistent + - controller_type: sata + controller_number: 1 + unit_number: 0 + size_mb: 256 + - controller_type: sata + controller_number: 2 + unit_number: 0 + size_mb: 256 + register: multi_sata_disk_vm + +- debug: var=multi_sata_disk_vm + +- name: assert that VM was deployed + assert: + that: + - "multi_sata_disk_vm.changed == true" + +- name: reconfigure created new VM with multiple sata controllers + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + name: test_vm2 + state: present + datastore: "{{ rw_datastore }}" + disk: + - controller_type: sata + controller_number: 0 + unit_number: 0 + size_mb: 512 + disk_mode: persistent + - controller_type: sata + controller_number: 1 + unit_number: 0 + size_mb: 512 + register: multi_sata_disk_vm + +- debug: var=multi_sata_disk_vm + +- name: assert that VM was deployed + assert: + that: + - "multi_sata_disk_vm.changed == true" + +- name: create a new VM with multiple nvme controllers + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + name: test_vm3 + guest_id: centos64Guest + datastore: "{{ rw_datastore }}" + hardware: + memory_mb: 512 + num_cpus: 1 + version: 14 + disk: + - controller_type: nvme + controller_number: 0 + unit_number: 0 + size_mb: 512 + - controller_type: nvme + controller_number: 1 + unit_number: 0 + size_mb: 256 + type: thin + - controller_type: nvme + controller_number: 2 + unit_number: 0 + size_mb: 256 + register: multi_nvme_disk_vm + +- debug: var=multi_nvme_disk_vm + +- name: assert that VM was deployed + assert: + that: + - "multi_nvme_disk_vm.changed == true" + +- name: reconfigure created new VM with multiple types of controllers + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: vm + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + name: test_vm3 + state: present + datastore: "{{ rw_datastore }}" + disk: + - controller_type: nvme + controller_number: 0 + unit_number: 1 + size_mb: 512 + - controller_type: sata + controller_number: 1 + unit_number: 0 + size_mb: 256 + - controller_type: paravirtual + controller_number: 0 + unit_number: 0 + size_mb: 256 + register: multi_nvme_disk_vm + +- debug: var=multi_nvme_disk_vm + +- name: assert that VM was deployed + assert: + that: + - "multi_nvme_disk_vm.changed == true" diff --git a/tests/integration/targets/vmware_guest/tasks/network_negative_test.yml b/tests/integration/targets/vmware_guest/tasks/network_negative_test.yml new file mode 100644 index 00000000..985df71a --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/network_negative_test.yml @@ -0,0 +1,339 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- debug: var=f0 + +- name: create new VMs with non-existent network + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: "Non existent VM" + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: non_existent_network + ignore_errors: true + +- debug: var=non_existent_network + +- name: assert that no changes were not made + assert: + that: + - not (non_existent_network is changed) + - "'does not exist' in non_existent_network.msg" + +- name: create new VMs with network and with only IP + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: "VM Network" + type: static + ip: 10.10.10.10 + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: no_netmask + ignore_errors: true + +- debug: var=no_netmask + +- name: assert that no changes were not made + assert: + that: + - "not no_netmask.changed" + - "\"'netmask' is required if 'ip' is specified under VM network list.\" in no_netmask.msg" + +- name: create new VMs with network and with only netmask + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: "VM Network" + type: static + netmask: 255.255.255.0 + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: no_ip + ignore_errors: true + +- debug: var=no_ip + +- name: assert that changes were not made + assert: + that: + - "not no_ip.changed" + - "\"'ip' is required if 'netmask' is specified under VM network list.\" in no_ip.msg" + +- name: create new VMs with network and without network name + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - ip: 10.10.10.10 + netmask: 255.255.255 + type: static + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: no_network_name + ignore_errors: true + +- debug: var=no_network_name + +- name: assert that no changes were not made + assert: + that: + - "not no_network_name.changed" + - "\"Please specify at least a network name or a VLAN name under VM network list.\" in no_network_name.msg" + +- name: create new VMs with network and without network name + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - vlan: non_existing_vlan + ip: 10.10.10.10 + netmask: 255.255.255 + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: no_network + ignore_errors: true + +- debug: var=no_network + +- name: assert that changes were not made + assert: + that: + - "not no_network.changed" + - "\"VLAN 'non_existing_vlan' does not exist.\" in no_network.msg" + +- name: create new VMs with invalid device type + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: "VM Network" + ip: 10.10.10.10 + netmask: 255.255.255 + device_type: abc + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: invalid_device_type + ignore_errors: true + +- debug: var=invalid_device_type + +- name: assert that changes were not made + assert: + that: + - "not invalid_device_type.changed" + - "\"Device type specified 'abc' is not valid.\" in invalid_device_type.msg" + +- name: create new VMs with invalid device MAC address + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: "VM Network" + ip: 10.10.10.10 + netmask: 255.255.255 + device_type: e1000 + mac: abcdef + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: invalid_mac + ignore_errors: true + +- debug: var=invalid_mac + +- name: assert that changes were not made + assert: + that: + - "not invalid_mac.changed" + - "\"Device MAC address 'abcdef' is invalid.\" in invalid_mac.msg" + +- name: create new VMs with invalid network type + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: "VM Network" + ip: 10.10.10.10 + netmask: 255.255.255 + device_type: e1000 + mac: 01:23:45:67:89:ab + type: aaaaa + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: invalid_network_type + ignore_errors: true + +- debug: var=invalid_network_type + +- name: assert that changes were not made + assert: + that: + - "not invalid_network_type.changed" + - "\"Network type 'aaaaa' is not a valid parameter.\" in invalid_network_type.msg" + +- name: create new VMs with IP, netmask and network type as "DHCP" + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: "VM Network" + ip: 10.10.10.10 + netmask: 255.255.255 + device_type: e1000 + mac: 01:23:45:67:89:ab + type: dhcp + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: invalid_dhcp_network_type + ignore_errors: true + +- debug: var=invalid_dhcp_network_type + +- name: assert that changes were not made + assert: + that: + - "not invalid_dhcp_network_type.changed" + - "\"Static IP information provided for network\" in invalid_dhcp_network_type.msg" + +- name: create new VMs with no network type which set network type as "DHCP" + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + disk: + - size: 3mb + type: thin + datastore: "{{ rw_datastore }}" + networks: + - name: "VM Network" + hardware: + num_cpus: 1 + memory_mb: 128 + state: poweredoff + folder: "{{ f0 }}" + register: no_network_type + ignore_errors: true + +- debug: var=no_network_type + +- name: assert that changes were made + assert: + that: + - "no_network_type.changed" diff --git a/tests/integration/targets/vmware_guest/tasks/network_with_device.yml b/tests/integration/targets/vmware_guest/tasks/network_with_device.yml new file mode 100644 index 00000000..307fd1fa --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/network_with_device.yml @@ -0,0 +1,62 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Testcase to check #38605 +- name: Deploy VM first VM + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + state: poweredoff + folder: "{{ f0 }}" + name: test_vm1 + disk: + - size: 10mb + datastore: "{{ rw_datastore }}" + guest_id: debian8_64Guest + hardware: + version: 11 + memory_mb: 128 + num_cpus: 1 + networks: + - name: 'VM Network' + device_type: "vmxnet3" + register: vm_result + +- debug: var=vm_result + +- assert: + that: + - "vm_result.changed" + +- name: Deploy VM again + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + state: poweredoff + folder: "{{ f0 }}" + name: test_vm1 + disk: + - size: 10mb + datastore: "{{ rw_datastore }}" + guest_id: debian8_64Guest + hardware: + version: 11 + memory_mb: 128 + num_cpus: 1 + networks: + - name: 'VM Network' + device_type: "vmxnet3" + register: vm_result_again + +- debug: var=vm_result_again + +- assert: + that: + - not (vm_result_again is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/non_existent_vm_ops.yml b/tests/integration/targets/vmware_guest/tasks/non_existent_vm_ops.yml new file mode 100644 index 00000000..fc0c466e --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/non_existent_vm_ops.yml @@ -0,0 +1,23 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Perform operation on non-existent VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "non_existent_vm" + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + state: poweredoff + register: non_existent_vm_ops + ignore_errors: true +- debug: var=non_existent_vm_ops +- name: assert that changes were not made + assert: + that: + - not (non_existent_vm_ops is changed) + - "'msg' in non_existent_vm_ops" + - "'Unable to find the datastore with given parameters.' in non_existent_vm_ops.msg" diff --git a/tests/integration/targets/vmware_guest/tasks/reconfig_vm_to_latest_version.yml b/tests/integration/targets/vmware_guest/tasks/reconfig_vm_to_latest_version.yml new file mode 100644 index 00000000..e7831760 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/reconfig_vm_to_latest_version.yml @@ -0,0 +1,97 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2019, Pavan Bidkar +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# Skipping out idepotency test untill issue fixed in reconfigure_vm() become_method + +- name: Create VM with hardware version 12 + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: debian8_64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + datastore: '{{ rw_datastore }}' + hardware: + num_cpus: 1 + memory_mb: 1028 + version: 12 + state: present + register: create_vm_with_version_12 + +- name: assert that changes were made + assert: + that: + - create_vm_with_version_12 is changed + +- name: Deploy New VM with latest hardware version + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm2 + guest_id: debian8_64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + datastore: '{{ rw_datastore }}' + hardware: + num_cpus: 1 + memory_mb: 1028 + version: latest + state: present + register: deploy_vm_to_latest + +- name: assert that changes were made + assert: + that: + - deploy_vm_to_latest is changed + +- name: Upgrade VM to latest version in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: debian8_64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + datastore: '{{ rw_datastore }}' + hardware: + num_cpus: 1 + memory_mb: 1028 + version: latest + state: present + register: upgrade_vm + check_mode: true + +- name: assert that changes would be made + assert: + that: + - upgrade_vm is changed + +- name: Upgrade VM to latest version + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: debian8_64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + datastore: '{{ rw_datastore }}' + hardware: + num_cpus: 1 + memory_mb: 1028 + version: latest + state: present + register: upgrade_vm + +- name: assert that changes were made + assert: + that: + - upgrade_vm is changed diff --git a/tests/integration/targets/vmware_guest/tasks/remove_vm_from_inventory.yml b/tests/integration/targets/vmware_guest/tasks/remove_vm_from_inventory.yml new file mode 100644 index 00000000..1d815082 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/remove_vm_from_inventory.yml @@ -0,0 +1,61 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2019, Pavan Bidkar +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Create VM to unregister + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + folder: F0 + hardware: + num_cpus: 1 + num_cpu_cores_per_socket: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + register: create_vm_for_test + +- name: assert that changes were made + assert: + that: + - create_vm_for_test is changed + +- name: Remove VM from Inventory + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + delete_from_inventory: true + state: absent + register: remove_vm_from_inventory + +- name: assert that changes were made + assert: + that: + - remove_vm_from_inventory is changed + +- name: Remove VM again from Inventory + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + delete_from_inventory: true + state: absent + register: remove_again_vm_from_inventory + +- name: assert that changes were made + assert: + that: + - not (remove_again_vm_from_inventory is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/run_test_playbook.yml b/tests/integration/targets/vmware_guest/tasks/run_test_playbook.yml new file mode 100644 index 00000000..501a9e86 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/run_test_playbook.yml @@ -0,0 +1,32 @@ +- block: + - include_tasks: '{{ test_playbook }}' + always: + - name: Collect the list of the existing VM + vmware.vmware_rest.vcenter_vm_info: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + register: existing_vms + until: existing_vms is not failed + - name: Turn off the VM + vmware.vmware_rest.vcenter_vm_power: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + state: stop + vm: '{{ item.vm }}' + with_items: "{{ existing_vms.value }}" + ignore_errors: true + - name: Clean up VM + vmware.vmware_rest.vcenter_vm: + vcenter_hostname: '{{ vcenter_hostname }}' + vcenter_username: '{{ vcenter_username }}' + vcenter_password: '{{ vcenter_password }}' + vcenter_validate_certs: false + state: absent + vm: '{{ item.vm }}' + with_items: "{{ existing_vms.value }}" + when: + - not item.name.startswith("vCLS") diff --git a/tests/integration/targets/vmware_guest/tasks/shares.yml b/tests/integration/targets/vmware_guest/tasks/shares.yml new file mode 100644 index 00000000..ff756443 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/shares.yml @@ -0,0 +1,122 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2019, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- &add_cpu_shares + name: Create new VMs with cpu shares level set to High + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + cpu_shares_level: high + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ virtual_machines[0].folder }}" + register: cpu_shares_level_result_0001 + +- debug: var=cpu_shares_level_result_0001 + +- name: Assert that changes were made + assert: + that: + - cpu_shares_level_result_0001 is changed + +- <<: *add_cpu_shares + name: Again create new VMs with CPU shares level set to High + register: cpu_shares_level_result_0002 + +- debug: var=cpu_shares_level_result_0002 + +- name: Assert that changes were not made + assert: + that: + - not (cpu_shares_level_result_0002 is changed) + +- &add_mem_shares + name: Create new VM with memory shares level set to High + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm2 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + mem_shares_level: high + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ virtual_machines[0].folder }}" + register: mem_shares_level_results_0003 + +- debug: var=mem_shares_level_results_0003 + +- name: Assert that changes were made + assert: + that: + - mem_shares_level_results_0003 is changed + +- <<: *add_mem_shares + name: Again create new VMs with memory shares level set to High + register: mem_shares_level_results_0004 + +- debug: var=mem_shares_level_results_0004 + +- name: Assert that changes were not made + assert: + that: + - not (mem_shares_level_results_0004 is changed) + +- &no_shares_levels + name: Create new VMs without CPU or Memory Shares level set + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm3 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + hardware: + num_cpus: 1 + memory_mb: 128 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + folder: "{{ virtual_machines[0].folder }}" + register: no_shares_levels_results_0005 + +- debug: var=no_shares_levels_results_0005 + +- name: Assert that changes were made + assert: + that: + - no_shares_levels_results_0005 is changed + +- <<: *no_shares_levels + name: Again create new VMs without memory_reservation or mem_reservation + register: no_shares_levels_result_0006 + +- debug: var=no_shares_levels_result_0006 + +- name: Assert that changes were not made + assert: + that: + - not (no_shares_levels_result_0006 is changed) diff --git a/tests/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml new file mode 100644 index 00000000..76b63f05 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml @@ -0,0 +1,96 @@ +# Test code for the vmware_guest module. +# Copyright: (c) 2018, goshkis +# Copyright: (c) 2019, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- &vapp_new_vm + name: Create test VM with vAPP settings + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "vm" + name: test_vm1 + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: centos64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: "{{ rw_datastore }}" + vapp_properties: + - id: prop_id1 + category: category + label: prop_label1 + type: string + value: prop_value1 + - id: prop_id2 + category: category + label: prop_label2 + type: string + value: prop_value2 + register: vapp_vm + +- debug: var=vapp_vm + +- name: assert the vApp propeties were created + assert: + that: + - "vapp_vm.failed == false" + - "vapp_vm.changed == true" + +- <<: *vapp_new_vm + name: Try to create same VM with same vAPP settings + register: vapp_vm_no_change + +- debug: var=vapp_vm_no_change + +- name: Assert that vApp properties were not changed + assert: + that: + - "vapp_vm_no_change.failed == false" + - "not vapp_vm_no_change.changed" + +- &vapp_edit_vm + name: Edit one vApp property and removing another + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "vm" + name: test_vm1 + datacenter: "{{ dc1 }}" + vapp_properties: + - id: prop_id1 + operation: remove + - id: prop_id2 + value: prop_value3 + state: present + register: vapp_vm + +- debug: var=vapp_vm + +- name: assert the VM was changed + assert: + that: + - "vapp_vm.failed == false" + - "vapp_vm.changed == true" + +- <<: *vapp_edit_vm + name: Try to edit VM with vApp settings + register: vapp_vm_no_change_edit + +- debug: var=vapp_vm_no_change_edit + +- name: assert the VM was changed + assert: + that: + - "vapp_vm_no_change_edit.failed == false" + - "vapp_vm_no_change_edit.changed == false" diff --git a/tests/integration/targets/vmware_guest/tasks/windows_vbs_d1_c1_f0.yml b/tests/integration/targets/vmware_guest/tasks/windows_vbs_d1_c1_f0.yml new file mode 100644 index 00000000..a7a30542 --- /dev/null +++ b/tests/integration/targets/vmware_guest/tasks/windows_vbs_d1_c1_f0.yml @@ -0,0 +1,123 @@ +- name: Create Windows 10 VM with VBS enabled + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "{{ f0 }}" + name: vbs-Test + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: windows9_64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + virt_based_security: true + version: 14 + boot_firmware: efi + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: '{{ rw_datastore }}' + cdrom: + - controller_number: 0 + unit_number: 0 + type: client + register: vbs_vm + +- debug: var=vbs_vm + +- name: assert the VM was created + assert: + that: + - "vbs_vm.failed == false" + - "vbs_vm.changed == true" + +- name: Create Windows Server 2016 VM without VBS enabled + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "{{ f0 }}" + name: vbs-Test2 + datacenter: "{{ dc1 }}" + cluster: "{{ ccr1 }}" + resource_pool: Resources + guest_id: windows9Server64Guest + hardware: + memory_mb: 128 + num_cpus: 1 + version: 14 + boot_firmware: efi + scsi: paravirtual + disk: + - size_mb: 128 + type: thin + datastore: '{{ rw_datastore }}' + cdrom: + - controller_number: 0 + unit_number: 0 + type: client + register: vbs_vm + +- debug: var=vbs_vm + +- name: assert the VM was created + assert: + that: + - "vbs_vm.failed == false" + - "vbs_vm.changed == true" + +- name: Enable VBS for Windows Server 2016 VM in check mode + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "{{ f0 }}" + name: vbs-Test2 + datacenter: "{{ f0 }}" + disk: + - size_mb: 256 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + virt_based_security: true + state: present + register: vbs_vm + check_mode: true + +- debug: var=vbs_vm + +- name: assert the VM would be changed + assert: + that: + - vbs_vm is changed + +- name: Enable VBS for Windows Server 2016 VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + folder: "{{ f0 }}" + name: vbs-Test2 + datacenter: "{{ f0 }}" + disk: + - size_mb: 256 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + virt_based_security: true + state: present + register: vbs_vm + +- debug: var=vbs_vm + +- name: assert the VM was changed + assert: + that: + - vbs_vm is changed diff --git a/tests/integration/targets/vmware_guest_boot_info/aliases b/tests/integration/targets/vmware_guest_boot_info/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_boot_info/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_boot_info/tasks/main.yml b/tests/integration/targets/vmware_guest_boot_info/tasks/main.yml new file mode 100644 index 00000000..85152475 --- /dev/null +++ b/tests/integration/targets/vmware_guest_boot_info/tasks/main.yml @@ -0,0 +1,49 @@ +# Test code for the vmware_guest_boot_info module. +# Copyright: (c) 2018, Abhijeet Kasurde +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: Gather info about VM boot order + vmware_guest_boot_info: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + register: vm1_info +- debug: var=vm1_info +- name: assert that values are set + assert: + that: + - vm1_info.vm_boot_info.current_boot_delay is defined + - vm1_info.vm_boot_info.current_boot_firmware is defined + - vm1_info.vm_boot_info.current_boot_order is defined + - vm1_info.vm_boot_info.current_boot_retry_delay is defined + - vm1_info.vm_boot_info.current_boot_retry_enabled is defined + - vm1_info.vm_boot_info.current_enter_bios_setup is defined + +- name: Gather info about VM boot order in check mode + vmware_guest_boot_info: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + check_mode: true + register: vm1_info +- debug: var=vm1_info +- name: assert that values are set + assert: + that: + - vm1_info.vm_boot_info.current_boot_delay is defined + - vm1_info.vm_boot_info.current_boot_firmware is defined + - vm1_info.vm_boot_info.current_boot_order is defined + - vm1_info.vm_boot_info.current_boot_retry_delay is defined + - vm1_info.vm_boot_info.current_boot_retry_enabled is defined + - vm1_info.vm_boot_info.current_enter_bios_setup is defined diff --git a/tests/integration/targets/vmware_guest_boot_manager/aliases b/tests/integration/targets/vmware_guest_boot_manager/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_boot_manager/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_boot_manager/tasks/main.yml b/tests/integration/targets/vmware_guest_boot_manager/tasks/main.yml new file mode 100644 index 00000000..5b7dac53 --- /dev/null +++ b/tests/integration/targets/vmware_guest_boot_manager/tasks/main.yml @@ -0,0 +1,190 @@ +# Test code for the vmware_guest_boot_manager module. +# Copyright: (c) 2022, Mario Lenz +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: Enable Secure Boot + community.vmware.vmware_guest_boot_manager: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + boot_firmware: efi + secure_boot_enabled: true + register: enable_secure_boot + +- ansible.builtin.debug: var=enable_secure_boot + +- name: Get VM boot info 1 + community.vmware.vmware_guest_boot_info: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + register: boot_info1 + +- ansible.builtin.debug: var=boot_info1 + +- name: assert that Secure Boot is enabled + assert: + that: + - enable_secure_boot.changed + - boot_info1.vm_boot_info.current_secure_boot_enabled is true + +- name: Issue https://github.com/ansible-collections/community.vmware/issues/1257 + block: + - name: Enter BIOS setup + community.vmware.vmware_guest_boot_manager: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + enter_bios_setup: true + register: enter_bios_setup + + - ansible.builtin.debug: var=enter_bios_setup + + - name: Get VM boot info 2 + community.vmware.vmware_guest_boot_info: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + register: boot_info2 + + - ansible.builtin.debug: var=boot_info2 + + - name: assert that configuration is changed + assert: + that: + - enter_bios_setup.changed + - boot_info2.vm_boot_info.current_enter_bios_setup is true + + - name: assert that Secure Boot is still enabled + assert: + that: + - boot_info2.vm_boot_info.current_secure_boot_enabled is true + +- name: Get VM disk info 1 + community.vmware.vmware_guest_disk_info: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + register: disk_info1 + +- ansible.builtin.debug: var=disk_info1 + +- name: Add a new disk for boot disk config test + block: + - name: Add a new disk to VM + community.vmware.vmware_guest_disk: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + disk: + - size_mb: 10 + type: thin + datastore: "{{ rw_datastore }}" + state: present + controller_number: "{{ disk_info1.guest_disk_info['0'].controller_bus_number }}" + unit_number: "{{ disk_info1.guest_disk_info['0'].unit_number | int + 1 }}" + controller_type: "{{ disk_info1.guest_disk_info['0'].controller_type }}" + register: add_disk_result + + - ansible.builtin.debug: var=add_disk_result + + - name: assert that new disk is added + assert: + that: + - add_disk_result.changed + - add_disk_result.disk_data | length == 2 + when: + - disk_info1 is defined + - disk_info1.guest_disk_info is defined + - disk_info1.guest_disk_info | length == 1 + +- name: Get VM disk info 2 + community.vmware.vmware_guest_disk_info: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + datacenter: "{{ dc1 }}" + register: disk_info2 + +- ansible.builtin.debug: var=disk_info2 + +- name: Get disk with label 'Hard disk 2' + ansible.builtin.set_fact: + get_hard_disk_2: "{{ item }}" + when: item.value.label == 'Hard disk 2' + with_dict: "{{ disk_info2.guest_disk_info }}" + +- name: assert that 'Hard disk 2' found in disk info + assert: + that: + - get_hard_disk_2 is defined + +- ansible.builtin.debug: var=get_hard_disk_2 + +- name: Set boot disk to 'Hard disk 2' + community.vmware.vmware_guest_boot_manager: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + boot_order: + - disk + - cdrom + - ethernet + boot_hdd_name: 'Hard disk 2' + register: set_boot_hdd_result1 + +- ansible.builtin.debug: var=set_boot_hdd_result1 + +- name: assert that boot disk is set to 'Hard disk 2' + assert: + that: + - set_boot_hdd_result1.changed + - set_boot_hdd_result1.vm_boot_status.current_boot_disk == 'Hard disk 2' + +- name: Set boot disk to 'Hard disk 2' again to test idempotency + community.vmware.vmware_guest_boot_manager: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: "{{ virtual_machines[0].name }}" + boot_order: + - disk + - cdrom + - ethernet + boot_hdd_name: 'Hard disk 2' + register: set_boot_hdd_result2 + +- ansible.builtin.debug: var=set_boot_hdd_result2 + +- name: assert that task is not changed + assert: + that: + - not set_boot_hdd_result2.changed + - set_boot_hdd_result2.vm_boot_status.current_boot_disk == 'Hard disk 2' + - set_boot_hdd_result2.vm_boot_status.previous_boot_disk == 'Hard disk 2' diff --git a/tests/integration/targets/vmware_guest_controller/aliases b/tests/integration/targets/vmware_guest_controller/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_controller/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_controller/tasks/main.yml b/tests/integration/targets/vmware_guest_controller/tasks/main.yml new file mode 100644 index 00000000..6ff9d522 --- /dev/null +++ b/tests/integration/targets/vmware_guest_controller/tasks/main.yml @@ -0,0 +1,80 @@ +# Test code for the vmware_guest_controller module +# Copyright: (c) 2019, Diane Wang (Tomorrow9) +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: gather disk controllers facts of the virtual machine + vmware_guest_controller: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + gather_disk_controller_facts: true + register: disk_controller_facts + +- debug: var=disk_controller_facts + +- name: get the number of existing disk controllers + set_fact: + scsi_num: "{{ disk_controller_facts.disk_controller_data.scsi | length }}" +- set_fact: + sata_num: "{{ disk_controller_facts.disk_controller_data.sata | length }}" +- set_fact: + nvme_num: "{{ disk_controller_facts.disk_controller_data.nvme | length }}" + +- name: add new disk controllers to virtual machine + vmware_guest_controller: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + sleep_time: 30 + controllers: + - type: sata + state: present + - type: paravirtual + state: present + register: add_disk_controller + +- debug: var=add_disk_controller + +- name: assert the new disk controllers were added to VM + assert: + that: + - "add_disk_controller.changed == true" + - "add_disk_controller.disk_controller_data.scsi | length | int == scsi_num | int + 1" + - "add_disk_controller.disk_controller_data.sata | length | int == sata_num | int + 1" + +- name: delete specified disk controllers + vmware_guest_controller: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + sleep_time: 30 + controllers: + - state: absent + type: sata + controller_number: "{{ sata_num }}" + - state: absent + type: paravirtual + controller_number: "{{ scsi_num }}" + register: del_disk_controller + +- debug: var=del_disk_controller + +- name: assert the disk controllers were removed + assert: + that: + - "del_disk_controller.changed == true" + - "del_disk_controller.disk_controller_data.sata | length | int == sata_num | int" + - "del_disk_controller.disk_controller_data.scsi | length | int == scsi_num | int" diff --git a/tests/integration/targets/vmware_guest_cross_vc_clone/aliases b/tests/integration/targets/vmware_guest_cross_vc_clone/aliases new file mode 100644 index 00000000..f44f70d8 --- /dev/null +++ b/tests/integration/targets/vmware_guest_cross_vc_clone/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +unsupported +needs/target/prepare_vmware_tests diff --git a/tests/integration/targets/vmware_guest_cross_vc_clone/tasks/main.yml b/tests/integration/targets/vmware_guest_cross_vc_clone/tasks/main.yml new file mode 100644 index 00000000..29a70aad --- /dev/null +++ b/tests/integration/targets/vmware_guest_cross_vc_clone/tasks/main.yml @@ -0,0 +1,160 @@ +# Test code for the vmware_guest_cross_vc_clone Operations. +# Copyright: (c) 2020, Anusha Hegde +# 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 + +- name: Create VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + hardware: + num_cpus: 1 + memory_mb: 512 + disk: + - size: 1gb + type: thin + autoselect_datastore: true + state: present + register: create_vm_for_test + +- name: Create VM template + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm2 + is_template: true + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + hardware: + num_cpus: 1 + memory_mb: 512 + disk: + - size: 1gb + type: thin + autoselect_datastore: true + state: present + register: create_vm_template_for_test + +- name: clone a template across VC + vmware_guest_cross_vc_clone: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm2" + destination_vm_name: "cloned_vm_from_template" + destination_vcenter: '{{ destination_vcenter_hostname }}' + destination_vcenter_username: '{{ destination_vcenter_username }}' + destination_vcenter_password: '{{ destination_vcenter_password }}' + destination_host: '{{ destination_esxi }}' + destination_datastore: '{{ destination_datastore }}' + destination_vm_folder: '{{ destination_vm_folder }}' + power_on: false + register: cross_vc_clone_from_template + +- name: assert that changes were made + assert: + that: + - cross_vc_clone_from_template is changed + +- name: clone a VM across VC + vmware_guest_cross_vc_clone: + hostname: '{{ destination_vcenter_hostname }}' + username: '{{ destination_vcenter_username }}' + password: "{{ destination_vcenter_password }}" + validate_certs: false + name: "test_vm1" + destination_vm_name: "cloned_vm_from_vm" + destination_vcenter: '{{ destination_vcenter_hostname }}' + destination_vcenter_username: '{{ destination_vcenter_username }}' + destination_vcenter_password: '{{ destination_vcenter_password }}' + destination_vcenter_port: '{{ destination_vcenter_port }}' + destination_vcenter_validate_certs: '{{ destination_vcenter_validate_certs }}' + destination_host: '{{ destination_esxi1 }}' + destination_datastore: '{{ destination_datastore }}' + destination_vm_folder: '{{ destination_vm_folder }}' + power_on: true + register: cross_vc_clone_from_vm + +- name: assert that changes were made + assert: + that: + - cross_vc_clone_from_vm is changed + +- name: clone a VM across VC when datastore cluster is specified + vmware_guest_cross_vc_clone: + hostname: '{{ destination_vcenter_hostname }}' + username: '{{ destination_vcenter_username }}' + password: "{{ destination_vcenter_password }}" + validate_certs: false + name: "test_vm1" + destination_vm_name: "cloned_vm_from_vm" + destination_vcenter: '{{ destination_vcenter_hostname }}' + destination_vcenter_username: '{{ destination_vcenter_username }}' + destination_vcenter_password: '{{ destination_vcenter_password }}' + destination_host: '{{ destination_esxi1 }}' + destination_datastore: '{{ destination_datastore_cluster }}' + destination_vm_folder: '{{ destination_vm_folder }}' + power_on: true + register: cross_vc_clone_from_vm_with_datastore_cluster + +- name: assert that changes were made + assert: + that: + - cross_vc_clone_from_vm_with_datastore_cluster is changed + +- name: clone in check mode + vmware_guest_cross_vc_clone: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm2" + destination_vm_name: "cloned_vm_from_template_in_check_mode" + destination_vcenter: '{{ destination_vcenter_hostname }}' + destination_vcenter_username: '{{ destination_vcenter_username }}' + destination_vcenter_password: '{{ destination_vcenter_password }}' + destination_host: '{{ destination_esxi }}' + destination_datastore: '{{ destination_datastore }}' + destination_vm_folder: '{{ destination_vm_folder }}' + power_on: false + check_mode: true + register: check_mode_clone + +- debug: + var: check_mode_clone + +- name: idempotency check - VM name already exists + vmware_guest_cross_vc_clone: + hostname: '{{ destination_vcenter_hostname }}' + username: '{{ destination_vcenter_username }}' + password: "{{ destination_vcenter_password }}" + validate_certs: false + name: "test_vm1" + destination_vm_name: "cloned_vm_from_vm" + destination_vcenter: '{{ destination_vcenter_hostname }}' + destination_vcenter_username: '{{ destination_vcenter_username }}' + destination_vcenter_password: '{{ destination_vcenter_password }}' + destination_host: '{{ destination_esxi1 }}' + destination_datastore: '{{ destination_datastore }}' + destination_vm_folder: '{{ destination_vm_folder }}' + power_on: true + register: idempotency_check + +- name: assert that no changes were made + assert: + that: + - idempotency_check is unchanged \ No newline at end of file diff --git a/tests/integration/targets/vmware_guest_custom_attribute_defs/aliases b/tests/integration/targets/vmware_guest_custom_attribute_defs/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_custom_attribute_defs/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_custom_attribute_defs/tasks/main.yml b/tests/integration/targets/vmware_guest_custom_attribute_defs/tasks/main.yml new file mode 100644 index 00000000..4ded94b2 --- /dev/null +++ b/tests/integration/targets/vmware_guest_custom_attribute_defs/tasks/main.yml @@ -0,0 +1,85 @@ +# Test code for the vmware_guest_custom_attribute_defs module. +# Copyright: (c) 2018, Abhijeet Kasurde +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true +- name: remove attribute definition + vmware_guest_custom_attribute_defs: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + state: absent + attribute_key: sample_5 + +- name: add custom attribute definition + vmware_guest_custom_attribute_defs: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + state: present + attribute_key: sample_5 + register: add_attrib_def + +- debug: var=add_attrib_def + +- assert: + that: + - add_attrib_def is changed + - "'sample_5' in add_attrib_def.custom_attribute_defs" + +- name: add attribute definition again + vmware_guest_custom_attribute_defs: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + state: present + attribute_key: sample_5 + register: add_attrib_def + +- debug: var=add_attrib_def + +- assert: + that: + - not (add_attrib_def is changed) + +- name: remove attribute definition + vmware_guest_custom_attribute_defs: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + state: absent + attribute_key: sample_5 + register: remove_attrib_def + +- debug: var=remove_attrib_def + +- assert: + that: + - "remove_attrib_def.changed" + - "'sample_5' not in remove_attrib_def.custom_attribute_defs" + +- name: remove attribute definition + vmware_guest_custom_attribute_defs: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + state: absent + attribute_key: sample_5 + register: remove_attrib_def + +- debug: var=remove_attrib_def + +- assert: + that: + - "not remove_attrib_def.changed" + - "'sample_5' not in remove_attrib_def.custom_attribute_defs" diff --git a/tests/integration/targets/vmware_guest_custom_attributes/aliases b/tests/integration/targets/vmware_guest_custom_attributes/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_custom_attributes/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_custom_attributes/tasks/main.yml b/tests/integration/targets/vmware_guest_custom_attributes/tasks/main.yml new file mode 100644 index 00000000..62aad771 --- /dev/null +++ b/tests/integration/targets/vmware_guest_custom_attributes/tasks/main.yml @@ -0,0 +1,157 @@ +# Test code for the vmware_guest_custom_attributes module. +# Copyright: (c) 2018, Abhijeet Kasurde +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: Add custom attribute to the given virtual machine with check_mode and diff + vmware_guest_custom_attributes: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + attributes: + - name: 'sample_1' + value: 'sample_1_value' + - name: 'sample_2' + value: 'sample_2_value' + - name: 'sample_3' + value: 'sample_3_value' + check_mode: true + diff: true + register: guest_info_0001_check_mode_diff + +- name: Make sure the target could be changed + assert: + that: + - guest_info_0001_check_mode_diff.changed is sameas true + +- name: Add custom attribute to the given virtual machine + vmware_guest_custom_attributes: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + attributes: + - name: 'sample_1' + value: 'sample_1_value' + - name: 'sample_2' + value: 'sample_2_value' + - name: 'sample_3' + value: 'sample_3_value' + register: guest_info_0001 + +- name: Make sure the target is changed + assert: + that: + - guest_info_0001.changed is sameas true + - guest_info_0001.custom_attributes.sample_1 == "sample_1_value" + - guest_info_0001.custom_attributes.sample_2 == "sample_2_value" + - guest_info_0001.custom_attributes.sample_3 == "sample_3_value" + +- name: Add custom attribute to the given virtual machine again + vmware_guest_custom_attributes: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + attributes: + - name: 'sample_1' + value: 'sample_1_value' + - name: 'sample_2' + value: 'sample_2_value' + - name: 'sample_3' + value: 'sample_3_value' + register: guest_info_0002 + +- name: Make sure the task has the idempotency + assert: + that: + - not (guest_info_0002 is changed) + - guest_info_0002.custom_attributes | length == 0 + +- name: Remove custom attribute to the given virtual machine with check_mode and diff + vmware_guest_custom_attributes: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: absent + attributes: + - name: 'sample_1' + - name: 'sample_2' + - name: 'sample_3' + check_mode: true + diff: true + register: guest_info_0004_check_mode_diff + +- name: Make sure the target could be changed + assert: + that: + - guest_info_0004_check_mode_diff.changed is sameas true + +- name: Remove custom attribute to the given virtual machine + vmware_guest_custom_attributes: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: absent + attributes: + - name: 'sample_1' + - name: 'sample_2' + - name: 'sample_3' + register: guest_info_0004 + +- name: Make sure the target is changed + assert: + that: + - guest_info_0004.changed is sameas true + - guest_info_0004.custom_attributes.sample_1 == "" + - guest_info_0004.custom_attributes.sample_2 == "" + - guest_info_0004.custom_attributes.sample_3 == "" + +- name: Remove custom attribute to the given virtual machine again + vmware_guest_custom_attributes: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: absent + attributes: + - name: 'sample_1' + - name: 'sample_2' + - name: 'sample_3' + register: guest_info_0005 + +- name: Make sure the task has the idempotency + assert: + that: + - not (guest_info_0005 is changed) + - guest_info_0005.custom_attributes | length == 0 diff --git a/tests/integration/targets/vmware_guest_customization_info/aliases b/tests/integration/targets/vmware_guest_customization_info/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_customization_info/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_customization_info/tasks/main.yml b/tests/integration/targets/vmware_guest_customization_info/tasks/main.yml new file mode 100644 index 00000000..6e28d7d7 --- /dev/null +++ b/tests/integration/targets/vmware_guest_customization_info/tasks/main.yml @@ -0,0 +1,36 @@ +# Test code for the vmware_guest_customization_info module. +# Copyright: (c) 2018, Abhijeet Kasurde +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- &vm_guest_info + name: Gather info about given customization spec + vmware_guest_customization_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + register: vm_custom_spec_info + +- debug: + var: vm_custom_spec_info + +- assert: + that: + - "not vm_custom_spec_info.changed" + +- <<: *vm_guest_info + name: Gather info about given customization spec in check module + +- debug: + var: vm_custom_spec_info + +- assert: + that: + - "not vm_custom_spec_info.changed" diff --git a/tests/integration/targets/vmware_guest_disk/aliases b/tests/integration/targets/vmware_guest_disk/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_disk/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_disk/tasks/main.yml b/tests/integration/targets/vmware_guest_disk/tasks/main.yml new file mode 100644 index 00000000..ed3ca129 --- /dev/null +++ b/tests/integration/targets/vmware_guest_disk/tasks/main.yml @@ -0,0 +1,636 @@ +# Test code for the vmware_guest_disk_disk module. + +- import_role: + name: prepare_vmware_tests + vars: + setup_attach_host: true + setup_datastore: true + + +- name: Create VM + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + folder: vm + esxi_hostname: "{{ esxi1 }}" + state: poweredoff + guest_id: debian8_64Guest + disk: + - size_gb: 1 + type: thin + datastore: local + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + hardware: + # vmware_guest_disk need vmx-13 to reconfigure the disks + version: 13 + memory_mb: 1024 + num_cpus: 1 + scsi: paravirtual + register: vm_create + + +- name: create new disk with invalid disk mode + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "invalid_disk_mode" + scsi_controller: 0 + scsi_type: 'paravirtual' + size_gb: 10 + state: present + type: eagerzeroedthick + unit_number: 2 + register: test_create_disk1 + ignore_errors: true + +- debug: + msg: "{{ test_create_disk1 }}" + +- name: assert that changes were not made + assert: + that: + - not(test_create_disk1 is changed) + +- name: create new disk(s) with valid disk mode + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_persistent" + scsi_controller: 0 + scsi_type: 'paravirtual' + size_gb: 1 + state: present + type: eagerzeroedthick + unit_number: 2 + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_nonpersistent" + scsi_controller: 0 + scsi_type: 'paravirtual' + size_gb: 1 + state: present + type: eagerzeroedthick + unit_number: 3 + - datastore: "{{ rw_datastore }}" + disk_mode: "persistent" + scsi_controller: 0 + scsi_type: 'paravirtual' + size_gb: 1 + state: present + type: eagerzeroedthick + unit_number: 4 + register: test_create_disk2 + +- debug: + msg: "{{ test_create_disk2 }}" + +- name: assert that changes were made + assert: + that: + - test_create_disk2 is changed + +- name: create new disk with custom shares + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - size_gb: 1 + type: eagerzeroedthick + datastore: "{{ rw_datastore }}" + disk_mode: "independent_nonpersistent" + scsi_controller: 1 + state: present + unit_number: 4 + shares: + level: custom + level_value: 1300 + register: test_custom_shares + +- debug: + msg: "{{ test_custom_shares }}" + +- name: assert that changes were made + assert: + that: + - test_custom_shares is changed + +- name: create new disk with custom IO limits and shares in IO Limits + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - size_gb: 1 + type: eagerzeroedthick + datastore: "{{ rw_datastore }}" + disk_mode: "independent_nonpersistent" + scsi_controller: 2 + state: present + unit_number: 4 + iolimit: + limit: 1506 + shares: + level: custom + level_value: 1305 + register: test_custom_IoLimit_shares + +- debug: + msg: "{{ test_custom_IoLimit_shares }}" + +- name: assert that changes were made + assert: + that: + - test_custom_IoLimit_shares is changed + +- name: Update disk for custom IO limits in IO Limits + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - size_gb: 2 + scsi_controller: 2 + state: present + unit_number: 4 + iolimit: + limit: 1500 + shares: + level: custom + level_value: 1305 + register: test_custom_IoLimit + +- debug: + msg: "{{ test_custom_IoLimit }}" + +- name: assert that changes were made + assert: + that: + - test_custom_IoLimit is changed + +- name: Update disk for shares of IO limits + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - size_gb: 3 + scsi_controller: 2 + state: present + unit_number: 4 + iolimit: + limit: 1500 + shares: + level: low + register: test_shares_IoLimit + +- debug: + msg: "{{ test_shares_IoLimit }}" + +- name: assert that changes were made + assert: + that: + - test_shares_IoLimit is changed + +- name: Update disk for shares and IoLimits of IO limits + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - size_gb: 4 + scsi_controller: 2 + state: present + unit_number: 4 + iolimit: + limit: 1507 + shares: + level: high + register: test_shares_IoLimits + +- debug: + msg: "{{ test_shares_IoLimits }}" + +- name: assert that changes were made + assert: + that: + - test_shares_IoLimits is changed + +- name: remove disks without destroy file + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - state: "absent" + scsi_controller: 0 + unit_number: 2 + destroy: false + register: test_remove_without_destroy + +- debug: + msg: "{{ test_remove_without_destroy }}" + +- name: assert that changes were made + assert: + that: + - test_remove_without_destroy is changed + +- name: re-create disk with valid disk mode + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "persistent" + scsi_controller: 0 + scsi_type: 'paravirtual' + size_gb: 1 + state: present + type: eagerzeroedthick + unit_number: 8 + register: test_recreate_disk + +- debug: + msg: "{{ test_recreate_disk }}" + +- name: assert that changes were made + assert: + that: + - test_recreate_disk is changed + +- name: create new disk with sharing (multi-writer) mode + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_persistent" + scsi_controller: 0 + scsi_type: 'paravirtual' + size_gb: 1 + state: present + type: eagerzeroedthick + sharing: true + unit_number: 6 + register: test_create_disk_sharing + +- debug: + msg: "{{ test_create_disk_sharing }}" + +- name: assert that changes were made + assert: + that: + - test_create_disk_sharing is changed + +- name: create new disk with invalid disk type for sharing (multi-writer) mode + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_persistent" + scsi_controller: 0 + scsi_type: 'paravirtual' + size_gb: 1 + state: present + type: thin + unit_number: 5 + sharing: true + register: test_create_disk_sharing_invalid + ignore_errors: true + +- debug: + msg: "{{ test_create_disk_sharing_invalid }}" + +- name: assert that changes were not made + assert: + that: + - not(test_create_disk_sharing_invalid is changed) + +- name: remove disk with destroy file + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - state: "absent" + scsi_controller: 0 + unit_number: 3 + destroy: true + - state: "absent" + scsi_controller: 0 + unit_number: 4 + register: test_remove_with_destroy + +- debug: + msg: "{{ test_remove_with_destroy }}" + +- name: assert that changes were made + assert: + that: + - test_remove_with_destroy is changed + +- name: create new disk with SATA controller + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_persistent" + controller_type: 'sata' + controller_number: 1 + unit_number: 3 + size_gb: 1 + state: present + type: thin + register: test_create_sata_disk + +- debug: + msg: "{{ test_create_sata_disk }}" + +- name: assert that changes were made + assert: + that: + - test_create_sata_disk is changed + +- name: create new disk with NVMe controller + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_persistent" + controller_type: 'nvme' + controller_number: 0 + unit_number: 1 + size: 1gb + state: present + type: thin + register: test_create_nvme_disk + +- debug: + msg: "{{ test_create_nvme_disk }}" + +- name: assert that changes were made + assert: + that: + - test_create_nvme_disk is changed + +- name: create 2 new disks on existing SATA controller and NVMe controller + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_persistent" + controller_type: 'sata' + controller_number: 1 + unit_number: 6 + size_gb: 1 + state: present + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_persistent" + controller_type: 'nvme' + controller_number: 0 + unit_number: 4 + size: 1gb + state: present + type: thin + register: test_create_two_disks + +- debug: + msg: "{{ test_create_two_disks }}" + +- name: assert that changes were made + assert: + that: + - test_create_two_disks is changed + +- name: re-configure SATA disk + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - disk_mode: "independent_nonpersistent" + controller_type: 'sata' + controller_number: 1 + unit_number: 3 + size_gb: 2 + state: present + shares: + level: custom + level_value: 1200 + register: test_reconfig_sata_disk + +- debug: + msg: "{{ test_reconfig_sata_disk }}" + +- name: assert that changes were made + assert: + that: + - test_reconfig_sata_disk is changed + +- name: re-configure NVMe disk + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - controller_type: 'nvme' + controller_number: 0 + unit_number: 1 + size_gb: 2 + state: present + iolimit: + limit: 1507 + shares: + level: custom + level_value: 1000 + register: test_reconfig_nvme_disk + +- debug: + msg: "{{ test_reconfig_nvme_disk }}" + +- name: assert that changes were made + assert: + that: + - test_reconfig_nvme_disk is changed + +- name: remove SATA and NVMe disks + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - state: "absent" + controller_type: 'sata' + controller_number: 1 + unit_number: 3 + destroy: false + - state: "absent" + controller_type: 'nvme' + controller_number: 0 + unit_number: 1 + destroy: true + register: test_remove_sata_nvme_disk + +- debug: + msg: "{{ test_remove_sata_nvme_disk }}" + +- name: assert that changes were made + assert: + that: + - test_remove_sata_nvme_disk is changed + +- name: create new disk with IDE controller + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - datastore: "{{ rw_datastore }}" + disk_mode: "independent_persistent" + controller_type: 'ide' + controller_number: 0 + unit_number: 1 + size_gb: 1 + state: present + type: thin + shares: + level: custom + level_value: 1200 + register: test_create_ide_disk + +- debug: + msg: "{{ test_create_ide_disk }}" + +- name: assert that changes were made + assert: + that: + - test_create_ide_disk is changed + +- name: remove IDE disk + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - state: "absent" + controller_type: 'ide' + controller_number: 0 + unit_number: 1 + destroy: true + register: test_remove_ide_disk + +- debug: + msg: "{{ test_remove_ide_disk }}" + +- name: assert that changes were made + assert: + that: + - test_remove_ide_disk is changed + +- name: Try to remove non-existing SCSI disk + vmware_guest_disk: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + name: test_vm1 + disk: + - state: "absent" + controller_type: 'paravirtual' + controller_number: 0 + unit_number: 15 + register: test_remove_non_existing_disk + ignore_errors: true + +- debug: + msg: "{{ test_remove_non_existing_disk }}" + +- name: assert that removing non existing disk does not fail + assert: + that: + - test_remove_non_existing_disk is not failed diff --git a/tests/integration/targets/vmware_guest_disk_info/aliases b/tests/integration/targets/vmware_guest_disk_info/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_disk_info/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_disk_info/tasks/main.yml b/tests/integration/targets/vmware_guest_disk_info/tasks/main.yml new file mode 100644 index 00000000..28de3784 --- /dev/null +++ b/tests/integration/targets/vmware_guest_disk_info/tasks/main.yml @@ -0,0 +1,75 @@ +# Test code for the vmware_guest_disk_info module. +# Copyright: (c) 2018, Abhijeet Kasurde +# 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_attach_host: true + setup_datastore: true + +- name: Create VMs + vmware_guest: + datacenter: "{{ dc1 }}" + folder: '{{ f0 }}' + name: DC0_H0_VM0 + state: poweredoff + guest_id: debian8_64Guest + disk: + - size_mb: 10 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + version: 11 + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + +- name: set state to poweron the first VM + vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: DC0_H0_VM0 + folder: '{{ f0 }}' + state: powered-on + +- name: Gather info about virtual machine disks + vmware_guest_disk_info: &get_info + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + name: DC0_H0_VM0 + datacenter: '{{ dc1 }}' + register: disk_info + +- debug: + msg: '{{ disk_info }}' + +- name: assert that no changes were made + assert: + that: + - "not disk_info.changed" + +- name: Gather info about virtual machine disks in check mode + vmware_guest_disk_info: + <<: *get_info + register: disk_info + check_mode: true + +- debug: + msg: '{{ disk_info }}' + +- name: assert that no changes were made + assert: + that: + - "not disk_info.changed" diff --git a/tests/integration/targets/vmware_guest_find/aliases b/tests/integration/targets/vmware_guest_find/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_find/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_find/tasks/main.yml b/tests/integration/targets/vmware_guest_find/tasks/main.yml new file mode 100644 index 00000000..4ffaec31 --- /dev/null +++ b/tests/integration/targets/vmware_guest_find/tasks/main.yml @@ -0,0 +1,60 @@ +# Test code for the vmware_guest_find module. +# Copyright: (c) 2017, James Tanner +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true +- name: find folders for each vm + vmware_guest_find: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ item.name }}" + with_items: "{{ virtual_machines }}" + register: folders + +- debug: var=item + with_items: "{{ folders.results }}" + +# We only care that each VM was found, not that the folder path +# is completely accurate. Eventually the test should be extended +# to validate the full path for each VM. +- assert: + that: + - "{{ 'folders' in item }}" + - "{{ item['folders']|length == 1 }}" + with_items: "{{ folders.results }}" + +- name: get fact of the first VM + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + register: guest_info_0001 + +- debug: var=guest_info_0001 + +- name: find folders for each vm using UUID + vmware_guest_find: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + uuid: "{{ guest_info_0001['instance']['hw_product_uuid'] }}" + register: folder_uuid + +- debug: var=folder_uuid + +- assert: + that: + - "{{ 'folders' in folder_uuid }}" + - "{{ folder_uuid['folders']|length == 1 }}" diff --git a/tests/integration/targets/vmware_guest_info/aliases b/tests/integration/targets/vmware_guest_info/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_info/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_info/tasks/main.yml b/tests/integration/targets/vmware_guest_info/tasks/main.yml new file mode 100644 index 00000000..4a42b390 --- /dev/null +++ b/tests/integration/targets/vmware_guest_info/tasks/main.yml @@ -0,0 +1,247 @@ +# Test code for the vmware_guest_info module. +# Copyright: (c) 2017, Abhijeet Kasurde +# Copyright: (c) 2018, James E. King III (@jeking3) +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +# Testcase 0001: Get details about virtual machines +- name: get list of info about virtual machines + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + register: guest_info_0001 + +- debug: + var: guest_info_0001 + +- assert: + that: + - "guest_info_0001['instance']['hw_name'] == virtual_machines[0].name" + - "guest_info_0001['instance']['hw_product_uuid'] is defined" + - "guest_info_0001['instance']['hw_cores_per_socket'] is defined" + - "guest_info_0001['instance']['hw_datastores'] is defined" + - "guest_info_0001['instance']['hw_esxi_host'] is defined" + - "guest_info_0001['instance']['hw_files'] is defined" + - "guest_info_0001['instance']['hw_guest_ha_state'] is defined" + - "guest_info_0001['instance']['hw_is_template'] is defined" + - "guest_info_0001['instance']['hw_folder'] is defined" + - "guest_info_0001['instance']['guest_question'] is defined" + - "guest_info_0001['instance']['guest_consolidation_needed'] is defined" + - "guest_info_0001['instance']['moid'] is defined" + - "guest_info_0001['instance']['vimref'] is defined" + - "'portgroup_portkey' in guest_info_0001['instance']['hw_eth0']" + - "'portgroup_key' in guest_info_0001['instance']['hw_eth0']" + - "guest_info_0001['instance']['instance_uuid'] is defined" + +- set_fact: vm1_uuid="{{ guest_info_0001['instance']['hw_product_uuid'] }}" + +- set_fact: vm1_instance_uuid="{{ guest_info_0001['instance']['instance_uuid'] }}" + +- set_fact: vm1_moid="{{ guest_info_0001['instance']['moid'] }}" + +- debug: + var: vm1_uuid + +# Testcase 0002: Get details about virtual machines using UUID +- name: get list of info about virtual machines using UUID + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + uuid: "{{ vm1_uuid }}" + register: guest_info_0002 + +- debug: + var: guest_info_0002 + +- name: Get specific details about virtual machines using the vsphere output schema + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + uuid: "{{ vm1_uuid }}" + schema: vsphere + properties: + - config.hardware.memoryMB + - guest + - name + - summary.runtime.connectionState + register: guest_info_0002b + +- debug: + var: guest_info_0002b + +- assert: + that: + - "guest_info_0002['instance']['hw_name'] == virtual_machines[0].name" + - "guest_info_0002['instance']['hw_product_uuid'] is defined" + - "guest_info_0002['instance']['hw_product_uuid'] == vm1_uuid" + - "guest_info_0002['instance']['hw_cores_per_socket'] is defined" + - "guest_info_0002b['instance']['config']['hardware']['memoryMB'] is defined" + - "guest_info_0002b['instance']['config']['hardware']['numCoresPerSocket'] is not defined" + - "guest_info_0002b['instance']['guest']['toolsVersion'] is defined" + - "guest_info_0001['instance']['moid'] is defined" + - "guest_info_0001['instance']['vimref'] is defined" + - "guest_info_0002b['instance']['overallStatus'] is not defined" + +# https://github.com/ansible-collections/vmware/issues/33 +- name: Get specific details about VM using the vsphere output schema + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + uuid: "{{ vm1_uuid }}" + schema: vsphere + properties: + - config.hardware.device.deviceInfo.summary + register: guest_info_with_list + +- debug: + var: guest_info_with_list + +- assert: + that: + - "guest_info_with_list['instance']['config']['hardware']['device'] is defined" + +# Testcase 0003: Get details about virtual machines without snapshots using UUID +- name: get empty list of snapshots from virtual machine using UUID + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + uuid: "{{ vm1_uuid }}" + register: guest_info_0003 + +- debug: + var: guest_info_0003 + +- assert: + that: + - "guest_info_0003['instance']['snapshots']|length == 0" + - "guest_info_0003['instance']['current_snapshot'] is none" + +# Testcase 0004: Get details about virtual machines with two snapshots using UUID +- name: Create first snapshot + vmware_guest_snapshot: &vm_snap + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + snapshot_name: snap1 + +- name: Create second snapshot + vmware_guest_snapshot: + <<: *vm_snap + snapshot_name: snap2 + +- name: get list of snapshots from virtual machine using UUID + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + uuid: "{{ vm1_uuid }}" + register: guest_info_0004 + +- debug: + var: guest_info_0004 + +- assert: + that: + - "guest_info_0004['instance']['snapshots'] is defined" + - "guest_info_0004['instance']['snapshots'][0]['name'] == 'snap1'" + - "guest_info_0004['instance']['snapshots'][1]['name'] == 'snap2'" + - "guest_info_0004['instance']['current_snapshot']['name'] == 'snap2'" + - "guest_info_0004['instance']['hw_folder'] is defined" + +# Testcase 0005: Get details about virtual machines using UUID +- name: get list of info about virtual machines using instance UUID + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + uuid: "{{ vm1_instance_uuid }}" + use_instance_uuid: true + register: guest_info_0005 + +- debug: + msg: "{{ guest_info_0005 }}" + +- assert: + that: + - "guest_info_0005['instance']['hw_name'] == virtual_machines[0].name" + - "guest_info_0005['instance']['hw_product_uuid'] is defined" + - "guest_info_0005['instance']['hw_product_uuid'] == vm1_uuid" + - "guest_info_0005['instance']['hw_cores_per_socket'] is defined" + - "guest_info_0005['instance']['hw_datastores'] is defined" + - "guest_info_0005['instance']['hw_esxi_host'] is defined" + - "guest_info_0005['instance']['hw_files'] is defined" + - "guest_info_0005['instance']['hw_guest_ha_state'] is defined" + - "guest_info_0005['instance']['hw_is_template'] is defined" + - "guest_info_0005['instance']['hw_folder'] is defined" + - "guest_info_0005['instance']['guest_question'] is defined" + - "guest_info_0005['instance']['guest_consolidation_needed'] is defined" + - "guest_info_0005['instance']['instance_uuid'] is defined" + - "guest_info_0005['instance']['instance_uuid'] == vm1_instance_uuid" + - "guest_info_0005['instance']['moid'] is defined" + - "guest_info_0005['instance']['vimref'] is defined" + +# Testcase 0006: Get details about virtual machines using MoID +- name: get list of information about virtual machines using instance MoID + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + moid: "{{ vm1_moid }}" + use_instance_uuid: true + register: guest_info_0006 + +- debug: + msg: "{{ guest_info_0006 }}" + +- assert: + that: + - "guest_info_0006['instance']['hw_name'] == virtual_machines[0].name" + - "guest_info_0006['instance']['hw_product_uuid'] is defined" + - "guest_info_0006['instance']['hw_product_uuid'] == vm1_uuid" + - "guest_info_0006['instance']['hw_cores_per_socket'] is defined" + - "guest_info_0006['instance']['hw_datastores'] is defined" + - "guest_info_0006['instance']['hw_esxi_host'] is defined" + - "guest_info_0006['instance']['hw_files'] is defined" + - "guest_info_0006['instance']['hw_guest_ha_state'] is defined" + - "guest_info_0006['instance']['hw_is_template'] is defined" + - "guest_info_0006['instance']['hw_folder'] is defined" + - "guest_info_0006['instance']['guest_question'] is defined" + - "guest_info_0006['instance']['guest_consolidation_needed'] is defined" + - "guest_info_0006['instance']['instance_uuid'] is defined" + - "guest_info_0006['instance']['instance_uuid'] == vm1_instance_uuid" + - "guest_info_0006['instance']['moid'] is defined" + - "guest_info_0006['instance']['vimref'] is defined" diff --git a/tests/integration/targets/vmware_guest_instant_clone/aliases b/tests/integration/targets/vmware_guest_instant_clone/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_instant_clone/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_instant_clone/tasks/main.yml b/tests/integration/targets/vmware_guest_instant_clone/tasks/main.yml new file mode 100644 index 00000000..026c89fc --- /dev/null +++ b/tests/integration/targets/vmware_guest_instant_clone/tasks/main.yml @@ -0,0 +1,385 @@ +# Test code for the vmware_guest_instant_clone Operations. +# Copyright: (c) 2021, Anant Chopra +# 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_attach_host: true + setup_datacenter: true + setup_datastore: true + setup_resource_pool: true + +- name: Create VM + community.vmware.vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datastore: "{{ rw_datastore }}" + esxi_hostname: "{{ esxi1 }}" + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + hardware: + num_cpus: 1 + memory_mb: 512 + version: 11 + disk: + - size: 1gb + type: thin + autoselect_datastore: true + state: poweredon + register: create_vm_for_test + +- name: Instant Clone a VM with parent VM + community.vmware.vmware_guest_instant_clone: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + folder: "{{ f0 }}" + datastore: "{{ rw_datastore }}" + datacenter: "{{ dc1 }}" + host: "{{ esxi1 }}" + name: "cloned_vm_from_vm" + parent_vm: "test_vm1" + resource_pool: DC0_C0_RP1 + register: instant_clone_from_vm_parent + +- name: assert that changes were made + assert: + that: + - instant_clone_from_vm_parent is changed + +- name: Instant Clone a VM when datastore cluster is specified + community.vmware.vmware_guest_instant_clone: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + folder: "{{ f0 }}" + datastore: "{{ rw_datastore }}" + datacenter: "{{ dc1 }}" + host: "{{ esxi1 }}" + name: "cloned_vm_from_vm_cluster" + parent_vm: "test_vm1" + resource_pool: DC0_C0_RP1 + register: instant_clone_from_vm_when_cluster_is_specified + +- name: assert that changes were made + assert: + that: + - instant_clone_from_vm_when_cluster_is_specified is changed + +- name: set state to poweroff the Cloned VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "cloned_vm_from_vm_cluster" + folder: "{{ f0 }}" + state: powered-off + register: poweroff_instant_clone_from_vm_when_cluster + +- debug: + var: poweroff_instant_clone_from_vm_when_cluster + +- name: Clean VM + community.vmware.vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "cloned_vm_from_vm_cluster" + datacenter: "{{ dc1 }}" + state: absent + register: delete_instant_clone_from_vm_when_cluster + ignore_errors: true + +- debug: var=delete_instant_clone_from_vm_when_cluster + +- name: Instant Clone a VM when skipping optional params + community.vmware.vmware_guest_instant_clone: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "cloned_vm_from_vm_optional" + parent_vm: "test_vm1" + datacenter: "{{ dc1 }}" + datastore: "{{ rw_datastore }}" + host: "{{ esxi1 }}" + register: instant_clone_from_vm_optional_arguments + +- name: assert that changes were made + assert: + that: + - instant_clone_from_vm_optional_arguments is changed + +- name: set state to poweroff the Cloned VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "cloned_vm_from_vm_optional" + state: powered-off + register: poweroff_instant_clone_from_vm_optional_arguments + +- debug: + var: poweroff_instant_clone_from_vm_optional_arguments + +- name: Clean VM + community.vmware.vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "cloned_vm_from_vm_optional" + datacenter: "{{ dc1 }}" + state: absent + register: delete_instant_clone_from_vm_optional_arguments + ignore_errors: true + +- debug: var=delete_instant_clone_from_vm_optional_arguments + +- name: Instant Clone a VM with moid + community.vmware.vmware_guest_instant_clone: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + datastore: "{{ rw_datastore }}" + host: "{{ esxi1 }}" + name: "Instant_cloned_test" + moid: "{{ create_vm_for_test.instance.moid }}" + resource_pool: DC0_C0_RP1 + register: instant_clone_from_vm_moid + +- name: assert that changes were made + assert: + that: + - instant_clone_from_vm_moid is changed + +- name: set state to poweroff the Cloned VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "Instant_cloned_test" + folder: "{{ f0 }}" + state: powered-off + register: poweroff_instant_clone_from_vm_moid + +- debug: + var: poweroff_instant_clone_from_vm_moid + +- name: Clean VM + community.vmware.vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "Instant_cloned_test" + datacenter: "{{ dc1 }}" + state: absent + register: delete_instant_clone_from_vm_moid + ignore_errors: true + +- debug: var=delete_instant_clone_from_vm_moid + +- name: Instant Clone a VM with instance_uuid + community.vmware.vmware_guest_instant_clone: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + datastore: "{{ rw_datastore }}" + host: "{{ esxi1 }}" + name: "Instant_cloned_instance_uuid" + use_instance_uuid: true + uuid: "{{ create_vm_for_test.instance.instance_uuid }}" + resource_pool: DC0_C0_RP1 + register: instant_clone_from_vm_instance_uuid + +- name: assert that changes were made + assert: + that: + - instant_clone_from_vm_instance_uuid is changed + +- name: set state to poweroff the Cloned VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "Instant_cloned_instance_uuid" + folder: "{{ f0 }}" + state: powered-off + register: poweroff_instant_clone_from_vm_instance_uuid + +- debug: + var: poweroff_instant_clone_from_vm_instance_uuid + +- name: Clean VM + community.vmware.vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "Instant_cloned_instance_uuid" + datacenter: "{{ dc1 }}" + state: absent + register: delete_instant_clone_from_vm_instance_uuid + ignore_errors: true + +- debug: var=delete_instant_clone_from_vm_instance_uuid + +- name: Instant Clone a VM with uuid + community.vmware.vmware_guest_instant_clone: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + datastore: "{{ rw_datastore }}" + host: "{{ esxi1 }}" + name: "Instant_cloned_uuid" + uuid: "{{ create_vm_for_test.instance.hw_product_uuid }}" + resource_pool: DC0_C0_RP1 + register: instant_clone_from_vm_uuid + +- name: assert that changes were made + assert: + that: + - instant_clone_from_vm_uuid is changed + +- name: set state to poweroff the VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: Instant_cloned_uuid + folder: "{{ f0 }}" + state: powered-off + register: poweroff_instant_clone_from_vm_uuid + +- debug: + var: poweroff_instant_clone_from_vm_uuid + +- name: Clean VM + community.vmware.vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: Instant_cloned_uuid + datacenter: "{{ dc1 }}" + state: absent + register: delete_instant_clone_from_vm_uuid + ignore_errors: true + +- debug: var=delete_instant_clone_from_vm_uuid + +- name: Instant clone in check mode + community.vmware.vmware_guest_instant_clone: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + folder: "{{ f0 }}" + datastore: "{{ rw_datastore }}" + datacenter: "{{ dc1 }}" + host: "{{ esxi1 }}" + name: "cloned_vm_from_vm_check" + parent_vm: "test_vm1" + resource_pool: DC0_C0_RP1 + check_mode: true + register: check_mode_instant_clone + +- debug: + var: check_mode_instant_clone + +- name: idempotency check - VM name already exists + community.vmware.vmware_guest_instant_clone: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "cloned_vm_from_vm" + parent_vm: "test_vm1" + datacenter: "{{ dc1 }}" + datastore: "{{ rw_datastore }}" + host: "{{ esxi1 }}" + resource_pool: DC0_C0_RP1 + folder: "{{ f0 }}" + register: idempotency_check + +- debug: + var: idempotency_check + +- name: set state to poweroff the Cloned VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "cloned_vm_from_vm" + folder: "{{ f0 }}" + state: powered-off + register: poweroff_instant_clone_from_vm_parent + +- debug: + var: poweroff_instant_clone_from_vm_parent + +- name: Clean VM + community.vmware.vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "cloned_vm_from_vm" + datacenter: "{{ dc1 }}" + state: absent + register: delete_instant_clone_from_vm_parent + ignore_errors: true + +- debug: var=delete_instant_clone_from_vm_parent + +- name: set state to poweroff the parent VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "test_vm1" + folder: "{{ f0 }}" + state: powered-off + register: poweroff_instant_clone_from_vm_parent + +- debug: + var: poweroff_instant_clone_from_vm_parent + +- name: Clean parent VM + community.vmware.vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "test_vm1" + datacenter: "{{ dc1 }}" + state: absent + register: clean_parent_vm + ignore_errors: true + +- debug: var=clean_parent_vm diff --git a/tests/integration/targets/vmware_guest_move/aliases b/tests/integration/targets/vmware_guest_move/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_move/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_move/tasks/main.yml b/tests/integration/targets/vmware_guest_move/tasks/main.yml new file mode 100644 index 00000000..d35903a5 --- /dev/null +++ b/tests/integration/targets/vmware_guest_move/tasks/main.yml @@ -0,0 +1,81 @@ +# Test code for the vmware_guest_move module +# Copyright: (c) 2018, Jose Angel Munoz +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: Create a VM folder on given Datacenter + vcenter_folder: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: '{{ dc1 }}' + folder_name: 'f1' + folder_type: vm + state: present + validate_certs: false + register: dest_folder + +# Testcase 0001: Move vm and get changed status +- name: Move VM (Changed) + vmware_guest_move: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: '{{ dc1 }}' + name: '{{ virtual_machines[0].name }}' + # Depends-On: https://github.com/ansible/ansible/pull/55237 + dest_folder: '{{ dest_folder.result.path }}' + register: vm_info_0001 + + +# Testcase 0002: Move vm and get OK status (Already Moved) +- &vm_move + name: Move VM (OK) + vmware_guest_move: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: '{{ dc1 }}' + name: '{{ virtual_machines[0].name }}' + dest_folder: '{{ dest_folder.result.path }}' + register: vm_info_0002 + +- debug: + msg: "{{ vm_info_0001 }}" + +- debug: + msg: "{{ vm_info_0002 }}" + +- name: Make sure changes are done + assert: + that: + - vm_info_0001.changed + - not vm_info_0002.changed + +- <<: *vm_move + name: Move VM in check mode + check_mode: true + register: vm_move_0003 + +- name: Make sure changes are not made in check mode + assert: + that: + - vm_move_0003.changed + +- name: Delete the f1 VM folder + vcenter_folder: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: '{{ dc1 }}' + folder_name: 'f1' + folder_type: vm + state: absent + validate_certs: false diff --git a/tests/integration/targets/vmware_guest_network/aliases b/tests/integration/targets/vmware_guest_network/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_network/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_network/tasks/main.yml b/tests/integration/targets/vmware_guest_network/tasks/main.yml new file mode 100644 index 00000000..0dad050e --- /dev/null +++ b/tests/integration/targets/vmware_guest_network/tasks/main.yml @@ -0,0 +1,539 @@ +# Test code for the vmware_guest_network module +# Copyright: (c) 2019, Diane Wang (Tomorrow9) +# 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_attach_host: true + setup_datastore: true + setup_dvs_portgroup: true + setup_dvswitch: true + +- name: Create VMs + community.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + folder: '/DC0/vm/F0' + name: test_vm1 + state: poweredon + guest_id: debian8_64Guest + disk: + - size_gb: 1 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + version: 13 + memory_mb: 1024 + num_cpus: 1 + scsi: paravirtual + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + +- community.vmware.vmware_guest_tools_wait: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + timeout: 600 + validate_certs: false + name: test_vm1 + +- name: gather network adapters' facts of the virtual machine + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + gather_network_info: true + register: netadapter_info + +- debug: var=netadapter_info + +- name: get number of existing network adapters + set_fact: + netadapter_num: "{{ netadapter_info.network_data | length }}" + +- name: add new network adapters to virtual machine + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "{{ item.network_name }}" + device_type: "{{ item.device_type }}" + mac_address: "{{ item.mac_address }}" + connected: "{{ item.connected }}" + state: present + loop: + - network_name: "VM Network" + device_type: e1000e + mac_address: "aa:50:56:58:59:60" + connected: true + - network_name: "VM Network" + device_type: vmxnet3 + mac_address: "aa:50:56:58:59:61" + connected: true + register: add_netadapter + +- debug: var=add_netadapter + +- name: assert the new network adapters were added to VM + assert: + that: + - add_netadapter is changed + - "add_netadapter.results[1].network_info | length | int == netadapter_num | int + 2" + +- name: delete one specified network adapter + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + mac_address: "aa:50:56:58:59:60" + state: absent + register: del_netadapter + +- debug: var=del_netadapter + +- name: assert the network adapter was removed + assert: + that: + - del_netadapter is changed + - "del_netadapter.network_info | length | int == netadapter_num | int + 1" + +- name: get instance uuid of virtual machines + community.vmware.vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + datacenter: '{{ dc1 }}' + register: guest_info + +- set_fact: vm1_instance_uuid="{{ guest_info['instance']['instance_uuid'] }}" + +- name: add new network adapters to virtual machine with instance uuid + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + uuid: '{{ vm1_instance_uuid }}' + use_instance_uuid: true + network_name: "VM Network" + device_type: e1000e + mac_address: "bb:50:56:58:59:60" + connected: true + register: add_netadapter_instanceuuid + +- debug: var=add_netadapter_instanceuuid + +- name: assert the new network adapters were added to VM + assert: + that: + - add_netadapter_instanceuuid is changed + - "add_netadapter_instanceuuid.network_info | length | int == netadapter_num | int + 2" + +- name: delete one specified network adapter + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + mac_address: "bb:50:56:58:59:60" + state: absent + register: del_netadapter + +- name: assert the network adapter was removed + assert: + that: + - del_netadapter is changed + - "del_netadapter.network_info | length | int == netadapter_num | int + 1" + +- name: delete again one specified network adapter (idempotency) + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + mac_address: "bb:50:56:58:59:60" + state: absent + register: del_again_netadapter + +- debug: var=del_again_netadapter + +- name: assert no change (idempotency) + assert: + that: + - not (del_again_netadapter is changed) + - "del_again_netadapter.network_info | length | int == netadapter_num | int + 1" + +- name: disable DirectPath I/O on a Vmxnet3 adapter + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "test_vm1" + state: present + mac_address: "aa:50:56:58:59:61" + directpath_io: false + register: disable_directpath_io + +- debug: var=disable_directpath_io + +- name: enable DirectPath I/O on a Vmxnet3 adapter + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "test_vm1" + state: present + mac_address: "aa:50:56:58:59:61" + directpath_io: true + register: enable_directpath_io + +- debug: var=enable_directpath_io + +- name: disconnect one specified network adapter + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + state: present + mac_address: "aa:50:56:58:59:61" + connected: false + register: disc_netadapter + +- debug: var=disc_netadapter + +- name: assert the network adapter was disconnected + assert: + that: + - disc_netadapter is changed + - "disc_netadapter.network_info[netadapter_num | int]['connected'] == false" + +- name: Check if network does not exists + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: non-existing-nw + mac_address: "aa:50:56:11:22:33" + state: present + register: no_nw_details + ignore_errors: true + +- debug: var=no_nw_details + +- name: Check if network does not exists + assert: + that: + - not (no_nw_details is changed) + - no_nw_details.failed + +- name: Change portgroup to dvPortgroup + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "{{ dvpg1 }}" + label: "Network adapter 1" + connected: false + start_connected: true + state: present + register: change_netaddr_dvp + +- debug: var=change_netaddr_dvp + +- name: Check changed to dvPortgroup from PortGroup + assert: + that: + - change_netaddr_dvp.changed is sameas true + +- name: Change portgroup to dvPortgroup + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "{{ dvpg1 }}" + label: "Network adapter 1" + connected: false + start_connected: true + state: present + register: change_netaddr_dvp + +- debug: var=change_netaddr_dvp + +- name: Check not changed of dvPortgroup + assert: + that: + - change_netaddr_dvp.changed is sameas false + +- name: Change dvPortgroup to PortGroup + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "VM Network" + label: "Network adapter 1" + connected: false + start_connected: true + state: present + register: change_netaddr_pg + +- debug: var=change_netaddr_pg + +- name: Check changed to dvPortgroup from PortGroup + assert: + that: + - change_netaddr_pg.changed is sameas true + - change_netaddr_pg.network_info[0].network_name == "VM Network" + +- name: Change dvPortgroup to PortGroup + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "VM Network" + label: "Network adapter 1" + connected: false + start_connected: true + state: present + register: change_netaddr_pg + +- debug: var=change_netaddr_pg + +- name: Check not changed of PortGroup + assert: + that: + - change_netaddr_pg.changed is sameas false + - change_netaddr_pg.network_info[0].network_name == "VM Network" + +# https://github.com/ansible/ansible/issues/65968 +- name: Create a network with dvPortgroup + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "{{ dvpg1 }}" + label: "Network adapter 2" + connected: true + start_connected: true + state: present + register: create_netaddr_pg + +- debug: var=create_netaddr_pg + +- name: Check if network is created with dvpg + assert: + that: + - create_netaddr_pg.changed is sameas true + +- name: gather network adapters' facts of the virtual machine + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + gather_network_info: true + register: nic_info + +- name: check that nic_info includes network_info + assert: + that: + - nic_info.network_info is defined + +- name: Remove all network interfaces with loop + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + mac_address: "{{ item.mac_address }}" + state: absent + loop: "{{ nic_info.network_info }}" + +- name: gather network adapters' facts of the virtual machine + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + gather_network_info: true + register: nic_info2 + +- name: check that there's no adapters left + assert: + that: + - "nic_info2.network_info | length | int == 0" + +- name: add new adapter(s) + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "VM Network" + state: present + register: new_nic + +- name: check that nic was created + assert: + that: + - "new_nic.network_info | length | int > 0" + +- name: add new PVRDMA adapter + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "{{ dvpg1 }}" + device_type: pvrdma + state: present + register: new_nic_pvrdma + +- name: check that PVRDMA nic was created + assert: + that: + - "new_nic_pvrdma.network_info | length | int == new_nic.network_info | length | int + 1" + +- name: remove PVRDMA adapter + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + mac_address: "{{ (new_nic_pvrdma.diff.after | difference(new_nic_pvrdma.diff.before))[0] }}" + state: absent + register: remove_nic_pvrdma + +- name: check that PVRDMA nic was removed + assert: + that: + - "new_nic_pvrdma.network_info | length | int - 1 == remove_nic_pvrdma.network_info | length | int" + +# https://github.com/ansible-collections/community.vmware/issues/204 +- name: "Change a dvpg with in same DVS(integration test for 204)" + block: + - name: "Prepare the integration test for 204" + community.vmware.vmware_dvs_portgroup: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + switch_name: "{{ dvswitch1 }}" + portgroup_name: 204dvpg + num_ports: 8 + port_binding: 'static' + port_allocation: 'fixed' + vlan_id: 1 + state: present + register: prepare_integration_test_204_result + + - assert: + that: + - prepare_integration_test_204_result.changed is sameas true + + - name: "Change a port group to a dvport group" + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: "{{ dvpg1 }}" + label: "Network adapter 1" + state: present + register: change_port_group_result + + - assert: + that: + - change_port_group_result.changed is sameas true + + - name: "Change a dvport group with in same DVS" + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: 204dvpg + label: Network adapter 1 + state: present + register: change_dvport_group_result + + - assert: + that: + - change_dvport_group_result.changed is sameas true + + - name: "Revert a dvport group to port group" + community.vmware.vmware_guest_network: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + network_name: VM Network + label: "Network adapter 1" + state: present + register: revert_dvport_group_result + + - assert: + that: + - revert_dvport_group_result.changed is sameas true + + - name: "Delete a dvport group for 204 integration test" + community.vmware.vmware_dvs_portgroup: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + switch_name: "{{ dvswitch1 }}" + portgroup_name: 204dvpg + num_ports: 8 + port_binding: 'static' + port_allocation: 'fixed' + vlan_id: 1 + state: absent + register: delete_integration_test_204_result + + - assert: + that: + - delete_integration_test_204_result.changed is sameas true diff --git a/tests/integration/targets/vmware_guest_powerstate/aliases b/tests/integration/targets/vmware_guest_powerstate/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_powerstate/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_powerstate/tasks/main.yml b/tests/integration/targets/vmware_guest_powerstate/tasks/main.yml new file mode 100644 index 00000000..462ae52a --- /dev/null +++ b/tests/integration/targets/vmware_guest_powerstate/tasks/main.yml @@ -0,0 +1,89 @@ +# Test code for the vmware_guest_powerstate module. +# Copyright: (c) 2017, Abhijeet Kasurde +# 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_attach_host: true + setup_datastore: true + +- name: Create a VM with the state poweredoff + community.vmware.vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + folder: '{{ f0 }}' + name: test_vm1 + state: poweredoff + guest_id: debian8_64Guest + disk: + - size_gb: 1 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + memory_mb: 128 + num_cpus: 1 + scsi: paravirtual + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + +- name: set state to poweroff the first VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + folder: '{{ f0 }}/' # Test with a trailing / because of issue 1238 + state: powered-off + register: poweroff_d1_c1_f0 + +- debug: + var: poweroff_d1_c1_f0 + +- name: make sure change was made + assert: + that: + - not (poweroff_d1_c1_f0 is changed) + +- name: set state to poweroff the first VM with datacenter + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + name: test_vm1 + folder: '{{ f0 }}' + state: powered-off + register: poweroff_d1_c1_f0_datacenter + +- name: make sure change was made + assert: + that: + - not (poweroff_d1_c1_f0_datacenter is changed) + +- name: Set a schedule task for first VM + community.vmware.vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + folder: '{{ f0 }}' + scheduled_at: "10/10/2030 10:10" + state: powered-on + register: poweron_d1_c1_f0 + +- name: Check that task is schedule + assert: + that: + - poweron_d1_c1_f0 is changed diff --git a/tests/integration/targets/vmware_guest_register_operation/aliases b/tests/integration/targets/vmware_guest_register_operation/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_register_operation/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_register_operation/tasks/main.yml b/tests/integration/targets/vmware_guest_register_operation/tasks/main.yml new file mode 100644 index 00000000..d80e497e --- /dev/null +++ b/tests/integration/targets/vmware_guest_register_operation/tasks/main.yml @@ -0,0 +1,359 @@ +# Test code for the vmware_guest_register_operation module +# Copyright: (c) 2019, sky-joker +# 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_attach_host: true + setup_datastore: true + setup_cluster: true + setup_virtualmachines: true + setup_resource_pool: true + +- name: gather facts of vm + vmware_guest_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + name: "{{ virtual_machines[0].name }}" + register: vm_facts + +- name: get a vm vmx file path + set_fact: vm_vmx_file_path="{{ vm_facts.instance.hw_files[0] }}" + +- name: Powered off the vm + vmware_guest_powerstate: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + folder: /vm + name: "{{ virtual_machines[0].name }}" + state: powered-off + +- name: Unregister VM from inventory with check_mode + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + name: "{{ virtual_machines[0].name }}" + state: absent + check_mode: true + register: unregister_vm_inventory_check_mode_result + +- name: Make sure the changed occurred + assert: + that: + - unregister_vm_inventory_check_mode_result.changed is sameas true + +- name: Unregister VM from inventory + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + name: "{{ virtual_machines[0].name }}" + state: absent + register: unregister_vm_inventory_result1 + +- name: Gather all registered virtual machines + vmware_vm_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + register: vms + +- assert: + that: + - >- + vms.virtual_machines + | selectattr('guest_name', 'equalto', virtual_machines[0].name) + | map(attribute='guest_name') + | list + | length == 0 + - unregister_vm_inventory_result1.changed is sameas true + +- assert: + that: + - item.guest_name != virtual_machines[0].name + loop: "{{ vms.virtual_machines }}" + +- name: Register VM to inventory with check_mode + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + esxi_hostname: "{{ esxi1 }}" + name: "{{ virtual_machines[0].name }}" + path: "{{ vm_vmx_file_path }}" + state: present + check_mode: true + register: register_vm_inventory_check_mode_result + +- name: Make sure the changed occurred + assert: + that: + - register_vm_inventory_check_mode_result.changed is sameas true + +- name: Register VM to inventory + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + esxi_hostname: "{{ esxi1 }}" + name: "{{ virtual_machines[0].name }}" + path: "{{ vm_vmx_file_path }}" + state: present + register: register_vm_inventory_result + +- name: Gather all registered virtual machines + vmware_vm_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + register: vms + +- assert: + that: + - >- + vms.virtual_machines + | selectattr('guest_name', 'equalto', virtual_machines[0].name) + | selectattr('esxi_hostname', 'equalto', esxi1) + | map(attribute='guest_name') + | list + | length == 1 + - register_vm_inventory_result.changed is sameas true + +- name: Unregister VM from inventory + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + name: "{{ virtual_machines[0].name }}" + state: absent + register: unregister_vm_inventory_result2 + +- name: Gather all registered virtual machines + vmware_vm_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + register: vms + +- assert: + that: + - >- + vms.virtual_machines + | selectattr('guest_name', 'equalto', virtual_machines[0].name) + | map(attribute='guest_name') + | list + | length == 0 + - unregister_vm_inventory_result2.changed is sameas true + +- name: Register VM in Cluster with check_mode + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + name: "{{ virtual_machines[0].name }}" + path: "{{ vm_vmx_file_path }}" + state: present + check_mode: true + register: register_vm_cluster_check_mode_result + +- name: Make sure the changed occurred + assert: + that: + - register_vm_cluster_check_mode_result.changed is sameas true + +- name: Register VM in Cluster + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + name: "{{ virtual_machines[0].name }}" + path: "{{ vm_vmx_file_path }}" + state: present + register: register_vm_cluster_result + +- name: Gather all registered virtual machines + vmware_vm_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + register: vms + +- assert: + that: + - >- + vms.virtual_machines + | selectattr('guest_name', 'equalto', virtual_machines[0].name) + | selectattr('cluster', 'equalto', ccr1) + | map(attribute='guest_name') + | list + | length == 1 + - register_vm_cluster_result.changed is sameas true + +- name: Unregister VM from inventory + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + name: "{{ virtual_machines[0].name }}" + state: absent + register: unregister_vm_inventory_result3 + +- name: Gather all registered virtual machines + vmware_vm_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + register: vms + +- assert: + that: + - >- + vms.virtual_machines + | selectattr('guest_name', 'equalto', virtual_machines[0].name) + | map(attribute='guest_name') + | list + | length == 0 + - unregister_vm_inventory_result3.changed is sameas true + +- name: Register VM in Resource pool with check_mode + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + resource_pool: DC0_C0_RP1 + name: "{{ virtual_machines[0].name }}" + path: "{{ vm_vmx_file_path }}" + state: present + check_mode: true + register: register_vm_resource_pool_check_mode_result + +- name: Make sure the changed occurred + assert: + that: + - register_vm_resource_pool_check_mode_result.changed is sameas true + +- name: Register VM in Resource pool + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + resource_pool: DC0_C0_RP1 + name: "{{ virtual_machines[0].name }}" + path: "{{ vm_vmx_file_path }}" + state: present + register: register_vm_resource_pool_result + +- name: Gather all registered virtual machines + vmware_vm_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + register: vms + +- assert: + that: + - >- + vms.virtual_machines + | selectattr('guest_name', 'equalto', virtual_machines[0].name) + | map(attribute='guest_name') + | list + | length == 1 + - register_vm_resource_pool_result.changed is sameas true + +- name: Gather facts of vm + vmware_guest_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + name: "{{ virtual_machines[0].name }}" + register: vm_facts + +- name: Get a vm uuid + set_fact: vm_uuid="{{ vm_facts.instance.hw_product_uuid }}" + +- name: Unregister VM from inventory with uuid parameter + vmware_guest_register_operation: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + datacenter: "{{ dc1 }}" + folder: "/vm" + cluster: "{{ ccr1 }}" + name: "{{ virtual_machines[0].name }}" + uuid: "{{ vm_uuid }}" + state: absent + register: unregister_vm_inventory_result4 + +- name: Gather all registered virtual machines + vmware_vm_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + register: vms + +- assert: + that: + - >- + vms.virtual_machines + | selectattr('guest_name', 'equalto', virtual_machines[0].name) + | map(attribute='guest_name') + | list + | length == 0 + - unregister_vm_inventory_result4.changed is sameas true \ No newline at end of file diff --git a/tests/integration/targets/vmware_guest_screenshot/aliases b/tests/integration/targets/vmware_guest_screenshot/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_screenshot/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_screenshot/tasks/main.yml b/tests/integration/targets/vmware_guest_screenshot/tasks/main.yml new file mode 100644 index 00000000..70b0d3f4 --- /dev/null +++ b/tests/integration/targets/vmware_guest_screenshot/tasks/main.yml @@ -0,0 +1,52 @@ +# Test code for the vmware_guest_screenshot module +# Copyright: (c) 2019, Diane Wang (Tomorrow9) +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: set VM state to powered on + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + folder: "{{ virtual_machines[0].folder }}" + name: "{{ virtual_machines[0].name }}" + state: poweredon + +- name: take screenshot of virtual machine's console + vmware_guest_screenshot: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + register: take_screenshot +- debug: var=take_screenshot +- name: assert the screenshot captured + assert: + that: + - "take_screenshot.changed == true" + +- name: take screenshot of virtual machine's console and download to local + vmware_guest_screenshot: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + local_path: "/tmp/screenshot_test.png" + datacenter: "{{ dc1 }}" + folder: "{{ virtual_machines[0].folder }}" + register: take_screenshot +- debug: var=take_screenshot +- name: assert the screenshot captured + assert: + that: + - "take_screenshot.changed == true" diff --git a/tests/integration/targets/vmware_guest_sendkey/aliases b/tests/integration/targets/vmware_guest_sendkey/aliases new file mode 100644 index 00000000..ef436f37 --- /dev/null +++ b/tests/integration/targets/vmware_guest_sendkey/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi_with_nest diff --git a/tests/integration/targets/vmware_guest_sendkey/tasks/main.yml b/tests/integration/targets/vmware_guest_sendkey/tasks/main.yml new file mode 100644 index 00000000..4113a706 --- /dev/null +++ b/tests/integration/targets/vmware_guest_sendkey/tasks/main.yml @@ -0,0 +1,53 @@ +# Test code for the vmware_guest_sendkey module +# Copyright: (c) 2017, Diane Wang (Tomorrow9) +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: set state to poweron the first VM + vmware_guest_powerstate: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + folder: '{{ f0 }}' + state: powered-on + +- name: send keys to virtual machine + vmware_guest_sendkey: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + keys_send: + - DOWNARROW + - DOWNARROW + - ENTER + register: send_key +- debug: var=send_key +- name: assert the keys were sent to VM + assert: + that: + - "send_key.changed == true" + +- name: send string to virtual machine + vmware_guest_sendkey: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + string_send: "test-user" + register: send_string +- debug: var=send_string +- name: assert the string was sent to VM + assert: + that: + - "send_string.changed == true" diff --git a/tests/integration/targets/vmware_guest_serial_port/aliases b/tests/integration/targets/vmware_guest_serial_port/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_serial_port/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_serial_port/tasks/main.yml b/tests/integration/targets/vmware_guest_serial_port/tasks/main.yml new file mode 100644 index 00000000..76826cf1 --- /dev/null +++ b/tests/integration/targets/vmware_guest_serial_port/tasks/main.yml @@ -0,0 +1,590 @@ +# Test code for the vmware_guest_serial_port Operations. +# Copyright: (c) 2019, Anusha Hegde +# 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_attach_host: true + setup_datastore: true + +- name: Create VM + vmware_guest: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: test_vm1 + guest_id: centos64Guest + datacenter: "{{ dc1 }}" + folder: "{{ f0 }}" + hardware: + num_cpus: 1 + memory_mb: 512 + disk: + - size: 1gb + type: thin + datastore: "{{ rw_datastore }}" + state: present + register: create_vm_for_test + +- name: assert that changes were made + assert: + that: + - create_vm_for_test is changed + +- name: "Create a new serial port for pipe type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: client + no_rx_loss: true + register: create_serial_port_pipe_type + +- assert: + that: + - create_serial_port_pipe_type.changed is sameas true + +- name: "Create a new serial port for pipe type(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: client + no_rx_loss: true + register: create_serial_port_pipe_type_idempotency_check + +- assert: + that: + - create_serial_port_pipe_type_idempotency_check.changed is sameas false + +- name: "Update serial port for pipe type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: server + no_rx_loss: false + register: update_serial_port_pipe_type + +- assert: + that: + - update_serial_port_pipe_type.changed is sameas true + +- name: "Remove serial port for pipe type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: server + no_rx_loss: false + state: absent + register: remove_serial_port_pipe_type + +- assert: + that: + - remove_serial_port_pipe_type.changed is sameas true + +- name: "Remove serial port for pipe type(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: server + no_rx_loss: false + state: absent + register: remove_serial_port_pipe_type_idempotency_check + +- assert: + that: + - remove_serial_port_pipe_type_idempotency_check.changed is sameas false + +- name: "Create a new serial port for network type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: network + service_uri: tcp://6000 + direction: client + register: create_serial_port_network_type + +- assert: + that: + - create_serial_port_network_type.changed is sameas true + +- name: "Create a new serial port for network type(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: network + service_uri: tcp://6000 + direction: client + register: create_serial_port_network_type_idempotency_check + +- assert: + that: + - create_serial_port_network_type_idempotency_check.changed is sameas false + +- name: "Update serial port for network type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: network + service_uri: tcp://6000 + direction: server + register: update_serial_port_network_type + +- assert: + that: + - update_serial_port_network_type.changed is sameas true + +- name: "Remove serial port for network type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: network + service_uri: tcp://6000 + direction: server + state: absent + register: remove_serial_network_pipe_type + +- assert: + that: + - remove_serial_network_pipe_type.changed is sameas true + +- name: "Remove serial port for network type(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: network + service_uri: tcp://6000 + direction: server + state: absent + register: remove_serial_port_network_type_idempotency_check + +- assert: + that: + - remove_serial_port_network_type_idempotency_check.changed is sameas false + +- name: "Create a new serial port for device type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: device + device_name: /dev/char/serial/uart0 + register: create_serial_port_device_type + +- assert: + that: + - create_serial_port_device_type.changed is sameas true + +- name: "Create a new serial port for device type(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: device + device_name: /dev/char/serial/uart0 + register: create_serial_port_device_type_idempotency_check + +- assert: + that: + - create_serial_port_device_type_idempotency_check.changed is sameas false + +- name: "Update serial port for device type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: device + device_name: /dev/char/serial/uart1 + register: update_serial_port_device_type + +- assert: + that: + - update_serial_port_device_type.changed is sameas true + +- name: "Remove serial port for device type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: device + device_name: /dev/char/serial/uart1 + state: absent + register: remove_serial_network_device_type + +- assert: + that: + - remove_serial_network_device_type.changed is sameas true + +- name: "Remove serial port for device type(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: device + device_name: /dev/char/serial/uart1 + state: absent + register: remove_serial_port_device_type_idempotency_check + +- assert: + that: + - remove_serial_port_device_type_idempotency_check.changed is sameas false + +- name: "Create a new serial port for file type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: file + file_path: "[{{ rw_datastore }}] file1" + register: create_serial_port_file_type + +- assert: + that: + - create_serial_port_file_type.changed is sameas true + +- name: "Create a new serial port for file type(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: file + file_path: "[{{ rw_datastore }}] file1" + register: create_serial_port_file_type_idempotency_check + +- assert: + that: + - create_serial_port_file_type_idempotency_check.changed is sameas false + +- name: "Update serial port for file type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: file + file_path: "[{{ rw_datastore }}] file2" + register: update_serial_port_file_type + +- assert: + that: + - update_serial_port_file_type.changed is sameas true + +- name: "Remove serial port for file type" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: file + file_path: "[{{ rw_datastore }}] file2" + state: absent + register: remove_serial_network_file_type + +- assert: + that: + - remove_serial_network_file_type.changed is sameas true + +- name: "Remove serial port for file type(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: file + file_path: "[{{ rw_datastore }}] file2" + state: absent + register: remove_serial_port_file_type_idempotency_check + +- assert: + that: + - remove_serial_port_file_type_idempotency_check.changed is sameas false + +- name: "Create a new serial port for pipe type using yield_on_poll param" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: client + no_rx_loss: true + yield_on_poll: true + register: create_serial_port_pipe_type_using_yield_on_poll + +- assert: + that: + - create_serial_port_pipe_type_using_yield_on_poll.changed is sameas true + +- name: "Create a new serial port for pipe type using yield_on_poll param(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: client + no_rx_loss: true + yield_on_poll: true + register: create_serial_port_pipe_type_using_yield_on_poll_idempotency_check + +- assert: + that: + - create_serial_port_pipe_type_using_yield_on_poll_idempotency_check.changed is sameas false + +- name: "Update serial port for pipe type using yield_on_poll param" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: client + no_rx_loss: true + yield_on_poll: false + register: update_serial_port_pipe_type_using_yield_on_poll + +- assert: + that: + - update_serial_port_pipe_type_using_yield_on_poll.changed is sameas true + +- name: "Remove serial port for pipe type using yield_on_poll param" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: client + no_rx_loss: true + yield_on_poll: false + state: absent + register: remove_serial_port_pipe_type_using_yield_on_poll + +- assert: + that: + - remove_serial_port_pipe_type_using_yield_on_poll.changed is sameas true + +- name: "Remove serial port for pipe type using yield_on_poll param(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: pipe + pipe_name: test_pipe + endpoint: client + no_rx_loss: true + yield_on_poll: false + state: absent + register: remove_serial_port_pipe_type_using_yield_on_poll_idempotency_check + +- assert: + that: + - remove_serial_port_pipe_type_using_yield_on_poll_idempotency_check.changed is sameas false + +- name: "Create multiple serial ports with Backing type - network, pipe, device and file" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: 'network' + direction: 'client' + service_uri: 'tcp://6000' + yield_on_poll: true + - type: 'pipe' + pipe_name: 'serial_pipe' + endpoint: 'client' + no_rx_loss: true + - type: 'device' + device_name: '/dev/char/serial/uart0' + - type: 'file' + file_path: '[{{ rw_datastore }}] file1' + yield_on_poll: true + register: create_multiple_ports + +- name: assert that changes were made + assert: + that: + - create_multiple_ports.changed is sameas true + +- name: "Create multiple serial ports with Backing type - network, pipe, device and file(idempotency check)" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "test_vm1" + backings: + - type: 'network' + direction: 'client' + service_uri: 'tcp://6000' + yield_on_poll: true + - type: 'pipe' + pipe_name: 'serial_pipe' + endpoint: 'client' + no_rx_loss: true + - type: 'device' + device_name: '/dev/char/serial/uart0' + - type: 'file' + file_path: '[{{ rw_datastore }}] file1' + yield_on_poll: true + register: create_multiple_ports_idempotency_check + +- name: assert that changes were made + assert: + that: + - create_multiple_ports_idempotency_check.changed is sameas false + +- name: "Modify existing serial port with Backing type - network" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: 'test_vm1' + backings: + - type: 'network' + state: 'present' + direction: 'server' + service_uri: 'tcp://6000' + register: modify_network_port + +- name: assert that changes were made + assert: + that: + - modify_network_port.changed is sameas true + +- name: "Remove serial port with Backing type - pipe" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: 'test_vm1' + backings: + - type: 'pipe' + pipe_name: 'serial_pipe' + state: 'absent' + register: remove_pipe_port + +- name: assert that changes were made + assert: + that: + - remove_pipe_port.changed is sameas true + +- name: "Remove all serial ports" + vmware_guest_serial_port: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: 'test_vm1' + backings: + - type: network + service_uri: 'tcp://6000' + state: absent + - type: device + device_name: '/dev/char/serial/uart0' + state: absent + - type: file + file_path: '[{{ rw_datastore }}] file1' + state: absent + register: remove_all_serial_ports + +- assert: + that: + - remove_all_serial_ports.changed is sameas true diff --git a/tests/integration/targets/vmware_guest_snapshot/aliases b/tests/integration/targets/vmware_guest_snapshot/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_snapshot/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_snapshot/tasks/main.yml b/tests/integration/targets/vmware_guest_snapshot/tasks/main.yml new file mode 100644 index 00000000..4eeb11cf --- /dev/null +++ b/tests/integration/targets/vmware_guest_snapshot/tasks/main.yml @@ -0,0 +1,249 @@ +- import_role: + name: prepare_vmware_tests + vars: + setup_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +# Test0001: Try to delete the non-existent snapshot +- name: 0001 - Delete non-existent snapshot + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: absent + snapshot_name: snap_a + +# Test0002: Create two snapshots +- name: 0002 - Create snapshot + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + snapshot_name: "snap_{{item}}" + description: "snap named {{item}}" + with_items: + - a + - b + +# Test0003: Reanme a to c +- name: 0003 - Rename snapshot + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + snapshot_name: snap_a + new_snapshot_name: snap_c + +# Test0004: Create snap_a again +- name: 0004 - Re-create snapshot a + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + snapshot_name: snap_a + description: "snap named a" + +# Test0005: Change description of snap_c +- name: 0005 - Change description of snap_c + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + snapshot_name: snap_c + new_description: "renamed to snap_c from snap_a" + +# Test0006: Delete snap_b with child remove +- name: 0006 - Delete snap_b with child remove + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: absent + snapshot_name: snap_b + remove_children: true + +# Test0007: Delete all snapshots +- name: 0007 - Delete all snapshots + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: remove_all + +# Test0008: Create snap_a again and revert to it +- name: 0008 - Re-create snapshot a + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + snapshot_name: snap_a + description: "snap named a" + +- name: 0008 - Revert to snap_a + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: revert + snapshot_name: snap_a + +# Test0009: Create snap_a and check in result +- name: 0009 - create snapshot a + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + snapshot_name: snap_a + description: "snap named a" + register: snapshot_details + +- ansible.builtin.debug: var=snapshot_details + +- name: Check if snapshot details available or not + ansible.builtin.assert: + that: + - "snapshot_details['msg'] == 'Snapshot named [snap_a] already exists and is current.'" + +# Test0011: Failure sceanrios - when name and UUID is not specified +- name: 0011 - name and UUID is missing + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + state: present + snapshot_name: snap_a + description: "snap named a" + register: snapshot_failure_details + ignore_errors: true + +- name: Check if error is shown + ansible.builtin.assert: + that: + - "'one of the following is required: name, uuid' in snapshot_failure_details['msg']" + - "snapshot_failure_details.changed == false" + +# Test0010 : Test for revert and remove a snapshot to specify snapshot_id +- name: 0012 - Create snapshot + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: present + snapshot_name: "snap_{{item}}" + description: "snap named {{item}}" + with_items: + - b + - c + register: snapshot_creation_result0012 + +- name: Create the snapshot_ids variable + ansible.builtin.set_fact: + snapshot_ids: >- + {{ snapshot_ids | default([]) + + [({ + 'name': item.snapshot_results.current_snapshot.name, + 'snapshot_id': item.snapshot_results.current_snapshot.id + })] + }} + loop: "{{ snapshot_creation_result0012.results }}" + +- name: 0013 - Revert to snap_b with snapshot_id + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: revert + snapshot_id: "{{ item.snapshot_id }}" + loop: "{{ snapshot_ids }}" + when: + - item.name == "snap_b" + register: revert_snapshot_with_id_result + +- name: Make sure whether reverted with snapshot_id + ansible.builtin.assert: + that: + - item.changed is sameas true + loop: "{{ revert_snapshot_with_id_result.results }}" + when: + - item.item.name == "snap_b" + +- name: 0014 - Remove snap_b with snapshot_id + community.vmware.vmware_guest_snapshot: + validate_certs: false + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + state: absent + snapshot_id: "{{ item.snapshot_id }}" + loop: "{{ snapshot_ids }}" + when: + - item.name == "snap_b" + register: remove_snapshot_with_id_result + +- name: Make sure whether removed with snapshot_id + ansible.builtin.assert: + that: + - item.changed is sameas true + loop: "{{ remove_snapshot_with_id_result.results }}" + when: + - item.item.name == "snap_b" diff --git a/tests/integration/targets/vmware_guest_snapshot_info/aliases b/tests/integration/targets/vmware_guest_snapshot_info/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_snapshot_info/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_snapshot_info/tasks/main.yml b/tests/integration/targets/vmware_guest_snapshot_info/tasks/main.yml new file mode 100644 index 00000000..eacb50af --- /dev/null +++ b/tests/integration/targets/vmware_guest_snapshot_info/tasks/main.yml @@ -0,0 +1,27 @@ +# Test code for the vmware_host_dns_info module. +# Copyright: (c) 2018, Abhijeet Kasurde +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: Gather snapshot info about given virtual machine + vmware_guest_snapshot_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + name: "{{ virtual_machines[0].name }}" + folder: "{{ virtual_machines[0].folder }}" + register: vm_snapshot_info + +- debug: var=vm_snapshot_info + +- assert: + that: + - '"guest_snapshots" in vm_snapshot_info' diff --git a/tests/integration/targets/vmware_guest_storage_policy/aliases b/tests/integration/targets/vmware_guest_storage_policy/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_storage_policy/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_storage_policy/tasks/main.yml b/tests/integration/targets/vmware_guest_storage_policy/tasks/main.yml new file mode 100644 index 00000000..fb0fbfa9 --- /dev/null +++ b/tests/integration/targets/vmware_guest_storage_policy/tasks/main.yml @@ -0,0 +1,209 @@ +# Test code for the vmware_guest_storage_policy module. +# Copyright: (c) 2020, @tgates81 +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Run tests and clean up + block: + - import_role: + name: prepare_vmware_tests + vars: + setup_attach_host: true + setup_datastore: true + setup_virtualmachines: true + + - name: "Setup: find existing storage profile(s) for later rollback" + vmware_vm_storage_policy_info: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + register: existing_profiles + + - name: "Setup: Create category" + vmware_category: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + category_name: "{{ category }}" + category_cardinality: "multiple" + state: "present" + register: category_create + + - name: "Setup: Set category id" + set_fact: + category_id: "{{ category_create['category_results']['category_id'] }}" + + - name: "Setup: Create tag" + vmware_tag: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + tag_name: "{{ tag }}" + category_id: "{{ category_id }}" + state: "present" + register: tag_create + + - name: "Setup: Create vSphere tag-based storage policy: {{ storage_policy }}" + vmware_vm_storage_policy: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "{{ storage_policy }}" + description: "test storage policy for vmware_guest_storage_policy" + tag_category: "{{ category }}" + tag_name: "{{ tag }}" + tag_affinity: true + state: "present" + register: new_sp + + - name: "001: Assign storage policy to VM check_mode test" + vmware_guest_storage_policy: &vm_home_args + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "{{ virtual_machines[0].name }}" + vm_home: "{{ storage_policy }}" + register: vm_home_sp_cm + check_mode: true + + - name: "001: assert that changes were made" + assert: + that: + - vm_home_sp_cm is changed + + - name: "001: assert that changed_policies.vm_home is properly set" + assert: + that: + - vm_home_sp_cm.changed_policies.vm_home == storage_policy + + - name: "002: Assign storage policy to VM" + vmware_guest_storage_policy: + <<: *vm_home_args + register: vm_home_sp + + - name: "002: assert that changes were made" + assert: + that: + - vm_home_sp is changed + + - name: "002: assert that changed_policies.vm_home is properly set" + assert: + that: + - vm_home_sp.changed_policies.vm_home == storage_policy + + + - name: "003: Assign storage policy to VM idempotence test" + vmware_guest_storage_policy: + <<: *vm_home_args + register: vm_home_sp_idp + + - name: "003: assert that changes were not made" + assert: + that: + - vm_home_sp_idp is not changed + + - name: "003: assert that changed_policies.vm_home is not set" + assert: + that: + - vm_home_sp_idp.changed_policies.vm_home == "" + + - name: "004: Assign storage policy to controller number {{ controller_number}} disk unit {{ unit_number }} check_mode test" + vmware_guest_storage_policy: &disk_args + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "{{ virtual_machines[0].name }}" + disk: + - unit_number: "{{ unit_number }}" + controller_number: "{{ controller_number }}" + policy: "{{ storage_policy }}" + register: disk_sp_cm + check_mode: true + + - name: "004: assert that changes were made" + assert: + that: + - disk_sp_cm is changed + + - name: "004: assert that changed_policies.disk is properly set" + assert: + that: + - disk_sp_cm.changed_policies.disk[0].unit_number == unit_number + - disk_sp_cm.changed_policies.disk[0].controller_number == controller_number + - disk_sp_cm.changed_policies.disk[0].policy == storage_policy + + - name: "005: Assign storage policy to disk unit {{ unit_number }}" + vmware_guest_storage_policy: + <<: *disk_args + register: disk_sp + + - name: "005: assert that changes were made" + assert: + that: + - disk_sp is changed + + - name: "005: assert that changed_policies.disk is properly set" + assert: + that: + - disk_sp.changed_policies.disk[0].unit_number == unit_number + - disk_sp.changed_policies.disk[0].controller_number == controller_number + - disk_sp.changed_policies.disk[0].policy == storage_policy + + - name: "006: Assign storage policy to disk unit {{ unit_number }} idempotence test" + vmware_guest_storage_policy: + <<: *disk_args + register: disk_sp_idp + + - name: "006: assert that changes were not made" + assert: + that: + - disk_sp_idp is not changed + + - name: "006: assert that changed_policies.disk is not set" + assert: + that: + - disk_sp_idp.changed_policies.disk == [] + + always: + - name: "Cleanup: Delete Tag" + vmware_tag: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + tag_name: "{{ tag }}" + category_id: "{{ category_id }}" + state: "absent" + register: delete_tag + + - name: "Cleanup: Delete Category" + vmware_category: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + category_name: "{{ category }}" + state: "absent" + register: delete_category + + - name: "Cleanup: Remove storage policy" + vmware_vm_storage_policy: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: false + name: "{{ storage_policy }}" + state: "absent" + register: policy_delete + + vars: + storage_policy: "vm_guest_storage_profile_policy001" + category: "vm_guest_storage_profile_cat001" + tag: "vm_guest_storage_profile_tag001" + unit_number: 0 + controller_number: 0 diff --git a/tests/integration/targets/vmware_guest_tools_info/aliases b/tests/integration/targets/vmware_guest_tools_info/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_tools_info/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_tools_info/tasks/main.yml b/tests/integration/targets/vmware_guest_tools_info/tasks/main.yml new file mode 100644 index 00000000..d16fd2d0 --- /dev/null +++ b/tests/integration/targets/vmware_guest_tools_info/tasks/main.yml @@ -0,0 +1,51 @@ +# Test code for the vmware_guest_tools_info module +# Copyright: (c) 2019, Diane Wang (Tomorrow9) +# 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_attach_host: true + setup_datastore: true + setup_virtualmachines: true + +- name: get instance uuid of virtual machine + vmware_guest_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + datacenter: "{{ dc1 }}" + register: guest_info +- debug: var=guest_info + +- name: get VMware tools info of virtual machine by instance uuid + vmware_guest_tools_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + uuid: "{{ guest_info['instance']['instance_uuid'] }}" + use_instance_uuid: true + register: vmtools_info +- debug: var=vmtools_info +- name: assert VMware tools info returned + assert: + that: + - "vmtools_info.vmtools_info.vm_tools_install_status != ''" + +- name: get VMware tools info of virtual machine by name + vmware_guest_tools_info: + validate_certs: false + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + name: "{{ virtual_machines[0].name }}" + datacenter: "{{ dc1 }}" + register: vmtools_info +- debug: var=vmtools_info +- name: assert VMware tools info returned + assert: + that: + - "vmtools_info.vmtools_info.vm_tools_install_status != ''" diff --git a/tests/integration/targets/vmware_guest_tools_wait/aliases b/tests/integration/targets/vmware_guest_tools_wait/aliases new file mode 100644 index 00000000..07e8732a --- /dev/null +++ b/tests/integration/targets/vmware_guest_tools_wait/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +needs/target/prepare_vmware_tests +zuul/vmware/vcenter_1esxi diff --git a/tests/integration/targets/vmware_guest_tools_wait/tasks/main.yml b/tests/integration/targets/vmware_guest_tools_wait/tasks/main.yml new file mode 100644 index 00000000..c675d138 --- /dev/null +++ b/tests/integration/targets/vmware_guest_tools_wait/tasks/main.yml @@ -0,0 +1,47 @@ +# Test code for the vmware_guest_tools_wait module. +# Copyright: (c) 2017 Philippe Dellaert +# 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_attach_host: true + setup_datastore: true + setup_dvs_portgroup: true + setup_dvswitch: true + +- name: Create VMs + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + datacenter: "{{ dc1 }}" + validate_certs: false + folder: '/DC0/vm/F0' + name: test_vm1 + state: poweredon + guest_id: debian8_64Guest + disk: + - size_gb: 1 + type: thin + datastore: '{{ rw_datastore }}' + hardware: + version: 11 + memory_mb: 1024 + num_cpus: 1 + scsi: paravirtual + cdrom: + - controller_number: 0 + unit_number: 0 + type: iso + iso_path: "[{{ ro_datastore }}] fedora.iso" + networks: + - name: VM Network + +- vmware_guest_tools_wait: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + timeout: 800 + validate_certs: false + name: test_vm1