From 47f9873eabab41f4c054d393ea7440bd85d7f95c Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Tue, 12 Nov 2019 17:13:57 +0530 Subject: [PATCH] VMware: Handle slash in network name in vmware_guest module (#64494) Encode slash in network name to work with vSphere API. Fixes: #64399 Signed-off-by: Abhijeet Kasurde --- changelogs/fragments/64399_vmware_guest.yml | 2 + lib/ansible/module_utils/vmware.py | 28 +++++- .../modules/cloud/vmware/vmware_guest.py | 4 +- .../tasks/init_real_lab.yml | 2 + .../tasks/setup_dvs_portgroup.yml | 18 ++++ .../prepare_vmware_tests/tasks/teardown.yml | 13 +++ .../prepare_vmware_tests/vars/common.yml | 2 + .../targets/vmware_guest/tasks/main.yml | 1 + .../vmware_guest/tasks/network_with_dvpg.yml | 86 ++++++++++++------- 9 files changed, 121 insertions(+), 35 deletions(-) create mode 100644 changelogs/fragments/64399_vmware_guest.yml create mode 100644 test/integration/targets/prepare_vmware_tests/tasks/setup_dvs_portgroup.yml diff --git a/changelogs/fragments/64399_vmware_guest.yml b/changelogs/fragments/64399_vmware_guest.yml new file mode 100644 index 00000000000..1814a6e3453 --- /dev/null +++ b/changelogs/fragments/64399_vmware_guest.yml @@ -0,0 +1,2 @@ +bugfixes: +- Handle slashes in VMware network name (https://github.com/ansible/ansible/issues/64399). diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py index f6c1d1b7020..ff35412236f 100644 --- a/lib/ansible/module_utils/vmware.py +++ b/lib/ansible/module_utils/vmware.py @@ -40,9 +40,7 @@ except ImportError: from ansible.module_utils._text import to_text, to_native from ansible.module_utils.six import integer_types, iteritems, string_types, raise_from -from ansible.module_utils.six.moves.urllib.parse import urlparse from ansible.module_utils.basic import env_fallback, missing_required_lib -from ansible.module_utils.urls import generic_urlparse class TaskError(Exception): @@ -117,7 +115,7 @@ def find_obj(content, vimtype, name, first=True, folder=None): def find_dvspg_by_name(dv_switch, portgroup_name): - + portgroup_name = quote_obj_name(portgroup_name) portgroups = dv_switch.portgroup for pg in portgroups: @@ -185,7 +183,7 @@ def find_resource_pool_by_name(content, resource_pool_name): def find_network_by_name(content, network_name): - return find_object_by_name(content, network_name, [vim.Network]) + return find_object_by_name(content, quote_obj_name(network_name), [vim.Network]) def find_vm_by_id(content, vm_id, vm_id_type="vm_name", datacenter=None, @@ -842,6 +840,28 @@ def is_truthy(value): return False +def quote_obj_name(object_name=None): + """ + Replace special characters in object name + with urllib quote equivalent + + """ + if not object_name: + return None + + from collections import OrderedDict + SPECIAL_CHARS = OrderedDict({ + '%': '%25', + '/': '%2f', + '\\': '%5c' + }) + for key in SPECIAL_CHARS.keys(): + if key in object_name: + object_name = object_name.replace(key, SPECIAL_CHARS[key]) + + return object_name + + class PyVmomi(object): def __init__(self, module): """ diff --git a/lib/ansible/modules/cloud/vmware/vmware_guest.py b/lib/ansible/modules/cloud/vmware/vmware_guest.py index 55e62d33e8d..748cf50abab 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_guest.py +++ b/lib/ansible/modules/cloud/vmware/vmware_guest.py @@ -633,7 +633,7 @@ from ansible.module_utils.vmware import (find_obj, gather_vm_facts, get_all_objs compile_folder_path_for_object, serialize_spec, vmware_argument_spec, set_vm_power_state, PyVmomi, find_dvs_by_name, find_dvspg_by_name, wait_for_vm_ip, - wait_for_task, TaskError) + wait_for_task, TaskError, quote_obj_name) def list_or_dict(value): @@ -862,6 +862,8 @@ class PyVmomiCache(object): return objects def get_network(self, network): + network = quote_obj_name(network) + if network not in self.networks: self.networks[network] = self.find_obj(self.content, [vim.Network], network) diff --git a/test/integration/targets/prepare_vmware_tests/tasks/init_real_lab.yml b/test/integration/targets/prepare_vmware_tests/tasks/init_real_lab.yml index c707c3728c4..c4f31072b05 100644 --- a/test/integration/targets/prepare_vmware_tests/tasks/init_real_lab.yml +++ b/test/integration/targets/prepare_vmware_tests/tasks/init_real_lab.yml @@ -27,3 +27,5 @@ when: setup_tag is defined - include_tasks: setup_content_library.yml when: setup_content_library is defined + - include_tasks: setup_dvs_portgroup.yml + when: setup_dvs_portgroup is defined diff --git a/test/integration/targets/prepare_vmware_tests/tasks/setup_dvs_portgroup.yml b/test/integration/targets/prepare_vmware_tests/tasks/setup_dvs_portgroup.yml new file mode 100644 index 00000000000..c0e14fe5399 --- /dev/null +++ b/test/integration/targets/prepare_vmware_tests/tasks/setup_dvs_portgroup.yml @@ -0,0 +1,18 @@ +--- +- name: create basic DVS portgroup + vmware_dvs_portgroup: + switch_name: "{{ dvswitch1 }}" + portgroup_name: '{{ dvpg1 }}' + vlan_id: 0 + num_ports: 32 + portgroup_type: earlyBinding + state: present + +- name: Create the DVS PG with slash in name + vmware_dvs_portgroup: + portgroup_name: '{{ dvpg_with_slash }}' + switch_name: '{{ dvswitch1 }}' + vlan_id: 0 + num_ports: 120 + portgroup_type: earlyBinding + state: present diff --git a/test/integration/targets/prepare_vmware_tests/tasks/teardown.yml b/test/integration/targets/prepare_vmware_tests/tasks/teardown.yml index 3638a749d00..a1d88ba03fe 100644 --- a/test/integration/targets/prepare_vmware_tests/tasks/teardown.yml +++ b/test/integration/targets/prepare_vmware_tests/tasks/teardown.yml @@ -33,6 +33,19 @@ - test_vm2 - test_vm3 +- name: Remove the DVS portgroups + vmware_dvs_portgroup: + switch_name: "{{ dvswitch1 }}" + portgroup_name: '{{ item }}' + vlan_id: 0 + num_ports: 32 + portgroup_type: earlyBinding + state: absent + loop: + - DC0_DVPG0 + - DVPG/1 + ignore_errors: yes + - name: Remove the DVSwitch vmware_dvswitch: datacenter_name: '{{ dc1 }}' diff --git a/test/integration/targets/prepare_vmware_tests/vars/common.yml b/test/integration/targets/prepare_vmware_tests/vars/common.yml index 77e4b5569ad..fe8c9927838 100644 --- a/test/integration/targets/prepare_vmware_tests/vars/common.yml +++ b/test/integration/targets/prepare_vmware_tests/vars/common.yml @@ -10,3 +10,5 @@ esxi2: '{{ esxi_hosts[1] }}' esxi3: '{{ esxi_hosts[2] }}' dvswitch1: DVS0 esxi_user: root +dvpg1: DC0_DVPG0 +dvpg_with_slash: DVPG/1 diff --git a/test/integration/targets/vmware_guest/tasks/main.yml b/test/integration/targets/vmware_guest/tasks/main.yml index 86d85ffd08d..d61f8ea1f30 100644 --- a/test/integration/targets/vmware_guest/tasks/main.yml +++ b/test/integration/targets/vmware_guest/tasks/main.yml @@ -11,6 +11,7 @@ setup_dvswitch: true setup_resource_pool: true setup_virtualmachines: true + setup_dvs_portgroup: true - include_tasks: run_test_playbook.yml with_items: '{{ vmware_guest_test_playbooks }}' diff --git a/test/integration/targets/vmware_guest/tasks/network_with_dvpg.yml b/test/integration/targets/vmware_guest/tasks/network_with_dvpg.yml index 7b5f9183a4b..2c9c6a87a6f 100644 --- a/test/integration/targets/vmware_guest/tasks/network_with_dvpg.yml +++ b/test/integration/targets/vmware_guest/tasks/network_with_dvpg.yml @@ -5,20 +5,6 @@ # Clone from existing VM with DVPG - when: vcsim is not defined block: - - name: create basic DVS portgroup - vmware_dvs_portgroup: - validate_certs: False - hostname: "{{ vcenter_hostname }}" - username: "{{ vcenter_username }}" - password: "{{ vcenter_password }}" - switch_name: "{{ dvswitch1 }}" - portgroup_name: DC0_DVPG0 - vlan_id: 0 - num_ports: 32 - portgroup_type: earlyBinding - state: present - register: dvs_pg_result_0001 - - name: Deploy VM from template vmware_guest: hostname: "{{ vcenter_hostname }}" @@ -38,7 +24,7 @@ memory_mb: 128 num_cpus: 1 networks: - - name: DC0_DVPG0 + - name: '{{ dvpg1 }}' register: no_vm_result - debug: var=no_vm_result - assert: @@ -65,7 +51,7 @@ memory_mb: 128 num_cpus: 1 networks: - - name: "DC0_DVPG0" + - name: '{{ dvpg1 }}' dvswitch_name: "{{ dvswitch1 }}" register: no_vm_result - debug: var=no_vm_result @@ -91,7 +77,59 @@ memory_mb: 128 num_cpus: 1 networks: - - name: "DC0_DVPG0" + - name: '{{ dvpg1 }}' + register: no_vm_result + - debug: var=no_vm_result + - assert: + that: + - not (no_vm_result is changed) + + - name: Deploy new VM with DVPG with slash in name + vmware_guest: + esxi_hostname: "{{ esxi_hosts[0] }}" + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: no + datacenter: "{{ dc1 }}" + state: poweredon + folder: "{{ f0 }}" + name: test_vm3 + disk: + - size: 10gb + autoselect_datastore: yes + guest_id: rhel7_64guest + hardware: + memory_mb: 128 + num_cpus: 1 + networks: + - name: '{{ dvpg_with_slash }}' + dvswitch_name: "{{ dvswitch1 }}" + register: no_vm_result + - debug: var=no_vm_result + - assert: + that: + - no_vm_result is changed + + - name: Deploy same VM again + vmware_guest: + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + validate_certs: no + datacenter: "{{ dc1 }}" + state: poweredon + folder: "{{ f0 }}" + name: test_vm3 + disk: + - size: 10gb + autoselect_datastore: yes + guest_id: rhel7_64guest + hardware: + memory_mb: 128 + num_cpus: 1 + networks: + - name: '{{ dvpg_with_slash }}' register: no_vm_result - debug: var=no_vm_result - assert: @@ -111,16 +149,4 @@ with_items: - test_vm1 - test_vm2 - - when: vcsim is not defined - name: delete basic portgroup - vmware_dvs_portgroup: - validate_certs: False - hostname: "{{ vcenter_hostname }}" - username: "{{ vcenter_username }}" - password: "{{ vcenter_password }}" - switch_name: "{{ dvswitch1 }}" - portgroup_name: DC0_DVPG0 - vlan_id: 0 - num_ports: 32 - portgroup_type: earlyBinding - state: absent + - test_vm3