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(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
|
||||
|
|
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