From fb46cf3718d17b9cea8676f9d08c89357d1600ce Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Fri, 16 Feb 2018 13:07:23 +0530 Subject: [PATCH] VMware: Fix DVPG idempotency issue (#36285) This fixes, cloning operation where template or existing VM does not have network or DVPG. Also, adds some strict type checking in network parameters. Signed-off-by: Abhijeet Kasurde (cherry picked from commit a377302d6b26b5a112c7db2bf9e9fb1a8a676319) --- .../modules/cloud/vmware/vmware_guest.py | 175 +++++---- .../targets/vmware_guest/tasks/main.yml | 3 + .../tasks/network_negative_test.yml | 341 ++++++++++++++++++ .../vmware_guest/tasks/network_with_dvpg.yml | 129 +++++++ 4 files changed, 583 insertions(+), 65 deletions(-) create mode 100644 test/integration/targets/vmware_guest/tasks/network_negative_test.yml create mode 100644 test/integration/targets/vmware_guest/tasks/network_with_dvpg.yml diff --git a/lib/ansible/modules/cloud/vmware/vmware_guest.py b/lib/ansible/modules/cloud/vmware/vmware_guest.py index 22514415812..8e78fc5539a 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_guest.py +++ b/lib/ansible/modules/cloud/vmware/vmware_guest.py @@ -502,23 +502,23 @@ class PyVmomiDeviceHelper(object): return diskspec - def create_nic(self, device_type, device_label, device_infos): - nic = vim.vm.device.VirtualDeviceSpec() - if device_type == 'pcnet32': - nic.device = vim.vm.device.VirtualPCNet32() - elif device_type == 'vmxnet2': - nic.device = vim.vm.device.VirtualVmxnet2() - elif device_type == 'vmxnet3': - nic.device = vim.vm.device.VirtualVmxnet3() - elif device_type == 'e1000': - nic.device = vim.vm.device.VirtualE1000() - elif device_type == 'e1000e': - nic.device = vim.vm.device.VirtualE1000e() - elif device_type == 'sriov': - nic.device = vim.vm.device.VirtualSriovEthernetCard() + def get_device(self, device_type, name): + nic_dict = dict(pcnet32=vim.vm.device.VirtualPCNet32(), + vmxnet2=vim.vm.device.VirtualVmxnet2(), + vmxnet3=vim.vm.device.VirtualVmxnet3(), + e1000=vim.vm.device.VirtualE1000(), + e1000e=vim.vm.device.VirtualE1000e(), + sriov=vim.vm.device.VirtualSriovEthernetCard(), + ) + if device_type in nic_dict: + return nic_dict[device_type] else: - self.module.fail_json(msg='Invalid device_type "%s" for network "%s"' % (device_type, device_infos['name'])) + self.module.fail_json(msg='Invalid device_type "%s"' + ' for network "%s"' % (device_type, name)) + def create_nic(self, device_type, device_label, device_infos): + nic = vim.vm.device.VirtualDeviceSpec() + nic.device = self.get_device(device_type, device_infos['name']) nic.device.wakeOnLanEnabled = bool(device_infos.get('wake_on_lan', True)) nic.device.deviceInfo = vim.Description() nic.device.deviceInfo.label = device_label @@ -544,11 +544,8 @@ class PyVmomiDeviceHelper(object): Returns: (Boolean) True if string is valid MAC address, otherwise False """ - ret = False mac_addr_regex = re.compile('[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$') - if mac_addr_regex.match(mac_addr): - ret = True - return ret + return bool(mac_addr_regex.match(mac_addr)) class PyVmomiCache(object): @@ -897,58 +894,93 @@ class PyVmomiHelper(PyVmomi): return device_list - def configure_network(self, vm_obj): - # Ignore empty networks, this permits to keep networks when deploying a template/cloning a VM - if len(self.params['networks']) == 0: - return + def sanitize_network_params(self): + """ + Function to 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 'ip' in network or 'netmask' in network: - if 'ip' not in network or 'netmask' not in network: - self.module.fail_json(msg="Both 'ip' and 'netmask' are required together.") - - if 'name' in network: - if find_obj(self.content, [vim.Network], network['name']) is None: - self.module.fail_json(msg="Network '%(name)s' does not exists" % network) + 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 find_obj(self.content, [vim.Network], network['name']) is None: + self.module.fail_json(msg="Network '%(name)s' does not exists" % 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 dvp.config.defaultPortConfig.vlan.vlanId == network['vlan']: + if hasattr(dvp.config.defaultPortConfig, 'vlan') and \ + dvp.config.defaultPortConfig.vlan.vlanId == 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) + 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: - self.module.fail_json(msg="You need to define a network name or a vlan") + # 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' + + 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.") + + validate_device_types = ['pcnet32', 'vmxnet2', 'vmxnet3', 'e1000', 'e1000e', 'sriov'] + if 'device_type' in network and network['device_type'] not in validate_device_types: + self.module.fail_json(msg="Device type specified '%s' is not valid." + " Please specify correct device" + " type from ['%s']." % (network['device_type'], + "', '".join(validate_device_types))) + + if 'mac' in network and not PyVmomiDeviceHelper.is_valid_mac_addr(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 len(self.params['networks']) == 0: + 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). " + 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)): - # 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_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 - # 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) nic.device = current_net_devices[key] if ('wake_on_lan' in network_devices[key] and @@ -964,37 +996,56 @@ class PyVmomiHelper(PyVmomi): nic.device.connectable.allowGuestControl = network_devices[key].get('allow_guest_control') nic_change_detected = True - nic.device.deviceInfo = vim.Description() + 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.get_device(network_devices[key]['device_type'], network_name) + if 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 - if hasattr(self.cache.get_network(network_devices[key]['name']), 'portKeys'): + if hasattr(self.cache.get_network(network_name), 'portKeys'): # VDS switch - pg_obj = find_obj(self.content, [vim.dvs.DistributedVirtualPortgroup], network_devices[key]['name']) - - if vm_obj is None or (nic.device.backing and not hasattr(nic.device.backing, 'port')) or \ - (nic.device.backing and (nic.device.backing.port.portgroupKey != pg_obj.key or - nic.device.backing.port.switchUuid != pg_obj.config.distributedVirtualSwitch.uuid)): - dvs_port_connection = vim.dvs.PortConnection() - dvs_port_connection.portgroupKey = pg_obj.key - dvs_port_connection.switchUuid = pg_obj.config.distributedVirtualSwitch.uuid - nic.device.backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo() - nic.device.backing.port = dvs_port_connection + pg_obj = find_obj(self.content, [vim.dvs.DistributedVirtualPortgroup], network_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 + dvs_port_connection.switchUuid = pg_obj.config.distributedVirtualSwitch.uuid + nic.device.backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo() + nic.device.backing.port = dvs_port_connection 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 - net_obj = self.cache.get_network(network_devices[key]['name']) + net_obj = self.cache.get_network(network_name) if nic.device.backing.network != net_obj: nic.device.backing.network = net_obj nic_change_detected = True - if nic.device.backing.deviceName != network_devices[key]['name']: - nic.device.backing.deviceName = network_devices[key]['name'] + if nic.device.backing.deviceName != network_name: + nic.device.backing.deviceName = network_name nic_change_detected = True if nic_change_detected: @@ -1035,17 +1086,11 @@ class PyVmomiHelper(PyVmomi): guest_map.adapter = vim.vm.customization.IPSettings() if 'ip' in network and 'netmask' in network: - if 'type' in network and network['type'] != 'static': - self.module.fail_json(msg='Static IP information provided for network "%(name)s", but "type" is set to "%(type)s".' % 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'] == 'static': - self.module.fail_json(msg='Network "%(name)s" was set to type "%(type)s", but "ip" and "netmask" are missing.' % network) elif 'type' in network and network['type'] == 'dhcp': guest_map.adapter.ip = vim.vm.customization.DhcpIpGenerator() - else: - self.module.fail_json(msg='Network "%(name)s" was set to unknown type "%(type)s".' % network) if 'gateway' in network: guest_map.adapter.gateway = network['gateway'] diff --git a/test/integration/targets/vmware_guest/tasks/main.yml b/test/integration/targets/vmware_guest/tasks/main.yml index 83ab3ad61f8..b60d5c93d1c 100644 --- a/test/integration/targets/vmware_guest/tasks/main.yml +++ b/test/integration/targets/vmware_guest/tasks/main.yml @@ -27,4 +27,7 @@ - include: create_nw_d1_c1_f0.yml - include: delete_vm.yml - include: non_existent_vm_ops.yml +- include: network_negative_test.yml +# Currently, VCSIM doesn't support DVPG (as portkeys are not available) so commenting this test +#- include: network_with_dvpg.yml #- include: template_d1_c1_f0.yml diff --git a/test/integration/targets/vmware_guest/tasks/network_negative_test.yml b/test/integration/targets/vmware_guest/tasks/network_negative_test.yml new file mode 100644 index 00000000000..f490d2bed86 --- /dev/null +++ b/test/integration/targets/vmware_guest/tasks/network_negative_test.yml @@ -0,0 +1,341 @@ +# 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: Wait for Flask controller to come up online + wait_for: + host: "{{ vcsim }}" + port: 5000 + state: started + +- name: kill vcsim + uri: + url: http://{{ vcsim }}:5000/killall +- name: start vcsim with no folders + uri: + url: http://{{ vcsim }}:5000/spawn?datacenter=1&cluster=1&folder=0 + register: vcsim_instance + +- name: Wait for Flask controller to come up online + wait_for: + host: "{{ vcsim }}" + port: 443 + state: started + +- name: get a list of VMS from vcsim + uri: + url: http://{{ vcsim }}:5000/govc_find?filter=VM + register: vmlist + +- debug: var=vcsim_instance + +- debug: var=vmlist + +- set_fact: + vm1: "{{ vmlist['json'][0] }}" + +- debug: var=vm1 + +- name: create new VMs with non-existent network + vmware_guest: + validate_certs: False + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + networks: + - name: "Non existent VM" + hardware: + num_cpus: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: non_existent_network + ignore_errors: yes + +- debug: var=non_existent_network + +- name: assert that no changes were made + assert: + that: + - "not non_existent_network.changed" + - "\"Network 'Non existent VM' does not exists\" in non_existent_network.msg" + +- name: create new VMs with network and with only IP + vmware_guest: + validate_certs: False + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + networks: + - name: "VM Network" + type: static + ip: 10.10.10.10 + hardware: + num_cpus: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: no_netmask + ignore_errors: yes + +- debug: var=no_netmask + +- name: assert that no changes were 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: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + networks: + - name: "VM Network" + type: static + netmask: 255.255.255.0 + hardware: + num_cpus: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: no_ip + ignore_errors: yes + +- debug: var=no_ip + +- name: assert that changes were 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: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + networks: + - ip: 10.10.10.10 + netmask: 255.255.255 + type: static + hardware: + num_cpus: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: no_network_name + ignore_errors: yes + +- debug: var=no_network_name + +- name: assert that no changes were 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: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + networks: + - vlan: non_existing_vlan + ip: 10.10.10.10 + netmask: 255.255.255 + hardware: + num_cpus: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: no_network + ignore_errors: yes + +- debug: var=no_network + +- name: assert that changes were 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: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + networks: + - name: "VM Network" + ip: 10.10.10.10 + netmask: 255.255.255 + device_type: abc + hardware: + num_cpus: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: invalid_device_type + ignore_errors: yes + +- debug: var=invalid_device_type + +- name: assert that changes were 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: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + networks: + - name: "VM Network" + ip: 10.10.10.10 + netmask: 255.255.255 + device_type: e1000 + mac: abcdef + hardware: + num_cpus: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: invalid_mac + ignore_errors: yes + +- debug: var=invalid_mac + +- name: assert that changes were 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: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + 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: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: invalid_network_type + ignore_errors: yes + +- debug: var=invalid_network_type + +- name: assert that changes were 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: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + name: new_vm + guest_id: centos64Guest + datacenter: "{{ (vm1 | basename).split('_')[0] }}" + disk: + - size: 3mb + type: thin + autoselect_datastore: yes + 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: 3 + memory_mb: 512 + state: poweredoff + folder: "{{ vm1 | dirname }}" + register: invalid_dhcp_network_type + ignore_errors: yes + +- debug: var=invalid_dhcp_network_type + +- name: assert that changes were made + assert: + that: + - "not invalid_dhcp_network_type.changed" + - "\"Static IP information provided for network\" in invalid_dhcp_network_type.msg" diff --git a/test/integration/targets/vmware_guest/tasks/network_with_dvpg.yml b/test/integration/targets/vmware_guest/tasks/network_with_dvpg.yml new file mode 100644 index 00000000000..5e8401ed29a --- /dev/null +++ b/test/integration/targets/vmware_guest/tasks/network_with_dvpg.yml @@ -0,0 +1,129 @@ +# 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: Wait for Flask controller to come up online + wait_for: + host: "{{ vcsim }}" + port: 5000 + state: started + +- name: kill vcsim + uri: + url: http://{{ vcsim }}:5000/killall +- name: start vcsim with no folders + uri: + url: http://{{ vcsim }}:5000/spawn?datacenter=1&cluster=1&folder=0 + register: vcsim_instance + +- name: Wait for Flask controller to come up online + wait_for: + host: "{{ vcsim }}" + port: 443 + state: started + +- name: get a list of VMS from vcsim + uri: + url: http://{{ vcsim }}:5000/govc_find?filter=VM + register: vmlist + +- debug: var=vcsim_instance + +- debug: var=vmlist + +- set_fact: + vm1: "{{ vmlist['json'][0] }}" + +- debug: var=vm1 + +- set_fact: + vm_name: "VM_{{ 10000 | random }}" + +# Clone from existing VM with DVPG +- name: Deploy VM from template {{ vm1 | basename }} + vmware_guest: + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + validate_certs: no + datacenter: "{{ (vm1|basename).split('_')[0] }}" + state: poweredon + folder: "{{ vm1 | dirname }}" + template: "{{ vm1 | basename }}" + name: "{{ vm_name }}" + disk: + - size: 10mb + autoselect_datastore: yes + guest_id: rhel7_64guest + hardware: + memory_mb: 512 + num_cpus: 1 + networks: + - name: "DC0_DVPG0" + register: no_vm_result + +- debug: var=no_vm_result + +- assert: + that: + - "no_vm_result.changed" + +# New clone with DVPG +- set_fact: + vm_name: "VM_{{ 10000 | random }}" + +- debug: var=vm_name + +- name: Deploy new VM with DVPG + vmware_guest: + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + validate_certs: no + datacenter: "{{ (vm1|basename).split('_')[0] }}" + state: poweredon + folder: "{{ vm1 | dirname }}" + name: "{{ vm_name }}" + disk: + - size: 10mb + autoselect_datastore: yes + guest_id: rhel7_64guest + hardware: + memory_mb: 512 + num_cpus: 1 + networks: + - name: "DC0_DVPG0" + register: no_vm_result + +- debug: var=no_vm_result + +- assert: + that: + - "no_vm_result.changed" + +- name: Deploy same {{ vm_name }} VM again + vmware_guest: + hostname: "{{ vcsim }}" + username: "{{ vcsim_instance['json']['username'] }}" + password: "{{ vcsim_instance['json']['password'] }}" + validate_certs: no + datacenter: "{{ (vm1|basename).split('_')[0] }}" + state: poweredon + folder: "{{ vm1 | dirname }}" + name: "{{ vm_name }}" + disk: + - size: 10mb + autoselect_datastore: yes + guest_id: rhel7_64guest + hardware: + memory_mb: 512 + num_cpus: 1 + networks: + - name: "DC0_DVPG0" + register: no_vm_result + +- debug: var=no_vm_result + +- assert: + that: + - "not no_vm_result.changed"