vApp properties support (#32579)

* Adding support for vApp properties.
* vm specification updated only if changes have to be applied. I.e. subsequent updates with the same data will not trigger changed state
* Auxiliary variables renamed, hope this makes the code more readable
* Integration tests changed - re-adding the same properties test not implemented, but tested on real vCenter deployment
* fixing documentation "version_added" for the feature
* Addressing reviewers comments #2:
* documentation updated with the only meaningful value for "option" attribute - "remove"
* Fixed improperly handled case when user requested "add" operation for existent property
* vApp configuration is updated only with properties that contains changes, not with all properties requested by user
pull/36497/head
goshkis 7 years ago committed by Abhijeet Kasurde
parent f207897f32
commit abb956d9eb

@ -241,6 +241,16 @@ options:
- ' - C(runonce) (list): List of commands to run at first user logon.'
- ' - C(timezone) (int): Timezone (See U(https://msdn.microsoft.com/en-us/library/ms912391.aspx)).'
version_added: '2.3'
vapp_properties:
description:
- A list of vApp properties
- 'For full list of attibutes and types refer to: U(https://github.com/vmware/pyvmomi/blob/master/docs/vim/vApp/PropertyInfo.rst)'
- 'Basic attributes are:'
- ' - C(id) (string): Property id - required'
- ' - C(value) (string): Property value'
- ' - C(type) (string): Value type, string type by default.'
- ' - C(operation): C(remove): This attribute is required only when removing properties'
version_added: '2.6'
extends_documentation_fragment: vmware.documentation
'''
@ -359,6 +369,22 @@ EXAMPLES = r'''
uuid: 421e4592-c069-924d-ce20-7e7533fab926
state: absent
delegate_to: localhost
- name: Manipulate vApp properties
vmware_guest:
hostname: 192.168.1.209
username: administrator@vsphere.local
password: vmware
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
'''
RETURN = r'''
@ -1052,6 +1078,86 @@ class PyVmomiHelper(PyVmomi):
self.configspec.deviceChange.append(nic)
self.change_detected = True
def configure_vapp_properties(self, vm_obj):
if len(self.params['vapp_properties']) == 0:
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()
# 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:
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
# 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
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_customvalues(self, vm_obj, config_spec):
if len(self.params['customvalues']) == 0:
return
@ -1761,6 +1867,7 @@ class PyVmomiHelper(PyVmomi):
self.configure_cdrom(vm_obj=self.current_vm_obj)
self.customize_customvalues(vm_obj=self.current_vm_obj, config_spec=self.configspec)
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'])
@ -1892,6 +1999,7 @@ def main():
networks=dict(type='list', default=[]),
resource_pool=dict(type='str'),
customization=dict(type='dict', default={}, no_log=True),
vapp_properties=dict(type='list', default=[]),
)
module = AnsibleModule(argument_spec=argument_spec,

@ -31,3 +31,4 @@
# 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
- include: vapp_d1_c1_f0.yml

@ -0,0 +1,107 @@
- 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 Clusters from vcsim
uri:
url: "{{ 'http://' + vcsim + ':5000/govc_find?filter=CCR' }}"
register: clusterlist
- debug: var=vcsim_instance
- debug: var=clusterlist
- name: Create test VM
vmware_guest:
validate_certs: False
hostname: "{{ vcsim }}"
username: "{{ vcsim_instance['json']['username'] }}"
password: "{{ vcsim_instance['json']['password'] }}"
folder: "/{{ (clusterlist['json'][0]|basename).split('_')[0] }}/vm"
name: vApp-Test
datacenter: "{{ (clusterlist['json'][0]|basename).split('_')[0] }}"
cluster: "{{ clusterlist['json'][0] }}"
resource_pool: Resources
guest_id: centos64Guest
hardware:
memory_mb: 512
num_cpus: 1
scsi: paravirtual
disk:
- size_mb: 128
type: thin
datastore: LocalDS_0
register: vapp_vm
- debug: var=vapp_vm
- name: Define vApp properties for the new VM
vmware_guest:
validate_certs: False
hostname: "{{ vcsim }}"
username: "{{ vcsim_instance['json']['username'] }}"
password: "{{ vcsim_instance['json']['password'] }}"
folder: "/{{ (clusterlist['json'][0]|basename).split('_')[0] }}/vm"
name: vApp-Test
datacenter: "{{ (clusterlist['json'][0]|basename).split('_')[0] }}"
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
state: present
register: vapp_vm
- debug: var=vapp_vm
- name: assert the vApp propeties were created
assert:
that:
- "vapp_vm.failed == false"
- "vapp_vm.changed == true"
- name: Edit one vApp property and removing another
vmware_guest:
validate_certs: False
hostname: "{{ vcsim }}"
username: "{{ vcsim_instance['json']['username'] }}"
password: "{{ vcsim_instance['json']['password'] }}"
folder: "/{{ (clusterlist['json'][0]|basename).split('_')[0] }}/vm"
name: vApp-Test
datacenter: "{{ (clusterlist['json'][0]|basename).split('_')[0] }}"
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"
Loading…
Cancel
Save