mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
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
This commit is contained in:
parent
f207897f32
commit
abb956d9eb
3 changed files with 216 additions and 0 deletions
|
@ -241,6 +241,16 @@ options:
|
||||||
- ' - C(runonce) (list): List of commands to run at first user logon.'
|
- ' - 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)).'
|
- ' - C(timezone) (int): Timezone (See U(https://msdn.microsoft.com/en-us/library/ms912391.aspx)).'
|
||||||
version_added: '2.3'
|
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
|
extends_documentation_fragment: vmware.documentation
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -359,6 +369,22 @@ EXAMPLES = r'''
|
||||||
uuid: 421e4592-c069-924d-ce20-7e7533fab926
|
uuid: 421e4592-c069-924d-ce20-7e7533fab926
|
||||||
state: absent
|
state: absent
|
||||||
delegate_to: localhost
|
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'''
|
RETURN = r'''
|
||||||
|
@ -1052,6 +1078,86 @@ class PyVmomiHelper(PyVmomi):
|
||||||
self.configspec.deviceChange.append(nic)
|
self.configspec.deviceChange.append(nic)
|
||||||
self.change_detected = True
|
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):
|
def customize_customvalues(self, vm_obj, config_spec):
|
||||||
if len(self.params['customvalues']) == 0:
|
if len(self.params['customvalues']) == 0:
|
||||||
return
|
return
|
||||||
|
@ -1761,6 +1867,7 @@ class PyVmomiHelper(PyVmomi):
|
||||||
self.configure_cdrom(vm_obj=self.current_vm_obj)
|
self.configure_cdrom(vm_obj=self.current_vm_obj)
|
||||||
self.customize_customvalues(vm_obj=self.current_vm_obj, config_spec=self.configspec)
|
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_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']:
|
if self.params['annotation'] and self.current_vm_obj.config.annotation != self.params['annotation']:
|
||||||
self.configspec.annotation = str(self.params['annotation'])
|
self.configspec.annotation = str(self.params['annotation'])
|
||||||
|
@ -1892,6 +1999,7 @@ def main():
|
||||||
networks=dict(type='list', default=[]),
|
networks=dict(type='list', default=[]),
|
||||||
resource_pool=dict(type='str'),
|
resource_pool=dict(type='str'),
|
||||||
customization=dict(type='dict', default={}, no_log=True),
|
customization=dict(type='dict', default={}, no_log=True),
|
||||||
|
vapp_properties=dict(type='list', default=[]),
|
||||||
)
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
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
|
# Currently, VCSIM doesn't support DVPG (as portkeys are not available) so commenting this test
|
||||||
#- include: network_with_dvpg.yml
|
#- include: network_with_dvpg.yml
|
||||||
#- include: template_d1_c1_f0.yml
|
#- include: template_d1_c1_f0.yml
|
||||||
|
- include: vapp_d1_c1_f0.yml
|
||||||
|
|
107
test/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml
Normal file
107
test/integration/targets/vmware_guest/tasks/vapp_d1_c1_f0.yml
Normal file
|
@ -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…
Reference in a new issue