diff --git a/changelogs/fragments/50617-vmware_guest-vapp_settings.yml b/changelogs/fragments/50617-vmware_guest-vapp_settings.yml new file mode 100644 index 00000000000..80b8f442b15 --- /dev/null +++ b/changelogs/fragments/50617-vmware_guest-vapp_settings.yml @@ -0,0 +1,2 @@ +minor_changes: +- vApp setting can be set while VM creation in vmware_guest (https://github.com/ansible/ansible/issues/50617). diff --git a/lib/ansible/modules/cloud/vmware/vmware_guest.py b/lib/ansible/modules/cloud/vmware/vmware_guest.py index 6a63e977985..f65a633882e 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_guest.py +++ b/lib/ansible/modules/cloud/vmware/vmware_guest.py @@ -1425,51 +1425,84 @@ class PyVmomiHelper(PyVmomi): new_vmconfig_spec = vim.vApp.VmConfigSpec() - # This is primarily for vcsim/integration tests, unset vAppConfig was not seen on my deployments - orig_spec = vm_obj.config.vAppConfig if vm_obj.config.vAppConfig else new_vmconfig_spec - - 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 + if vm_obj: + # VM exists + # This is primarily for vcsim/integration tests, unset vAppConfig was not seen on my deployments + orig_spec = vm_obj.config.vAppConfig if vm_obj.config.vAppConfig else new_vmconfig_spec + + 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: - # 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 + 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' + + 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') + + new_vapp_property_spec.info = property_info + new_vapp_property_spec.info.key = new_property_index + new_property_index += 1 + is_property_changed = True - except Exception as e: - self.module.fail_json(msg="Failed to set vApp property field='%s' and value='%s'. Error: %s" - % (property_name, property_value, to_text(e))) - else: - if property_spec.get('operation') == 'remove': - # attemp to delete non-existent property - continue + 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' @@ -1489,8 +1522,10 @@ class PyVmomiHelper(PyVmomi): 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 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 @@ -2157,6 +2192,7 @@ class PyVmomiHelper(PyVmomi): self.configure_cpu_and_memory(vm_obj=vm_obj, vm_creation=True) self.configure_hardware_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) diff --git a/test/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml b/test/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml index 03b7b7c75f1..7479ed3c9c0 100644 --- a/test/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml +++ b/test/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml @@ -1,4 +1,10 @@ -- name: Create test VM +# 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 }}" @@ -18,19 +24,6 @@ - size_mb: 128 type: thin datastore: "{{ ds2 }}" - register: vapp_vm - -- debug: var=vapp_vm - -- name: Define vApp properties for the new VM - vmware_guest: - validate_certs: False - hostname: "{{ vcenter_hostname }}" - username: "{{ vcenter_username }}" - password: "{{ vcenter_password }}" - folder: "vm" - name: vApp-Test - datacenter: "{{ dc1 }}" vapp_properties: - id: prop_id1 category: category @@ -42,7 +35,6 @@ label: prop_label2 type: string value: prop_value2 - state: present register: vapp_vm - debug: var=vapp_vm @@ -53,7 +45,22 @@ - "vapp_vm.failed == false" - "vapp_vm.changed == true" -- name: Edit one vApp property and removing another +- when: vcsim is not defined + block: + - <<: *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 }}" @@ -77,3 +84,17 @@ that: - "vapp_vm.failed == false" - "vapp_vm.changed == true" + +- when: vcsim is not defined + block: + - <<: *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"