Repository: ansible/ansible

Issue:
[vmware_guest] - Carry over portkey fact from vsphere_guest when joining vm nic to portgroup
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
Always add information AFTER of these html comments.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
<!---
Insert below this comment the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
vmware_guest
##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python3.5/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
ANSIBLE_SSH_ARGS(/etc/ansible/ansible.cfg) = -o StrictHostKeyChecking=no -o Serv
ANSIBLE_SSH_RETRIES(/etc/ansible/ansible.cfg) = 2
HOST_KEY_CHECKING(/etc/ansible/ansible.cfg) = False
PERSISTENT_CONNECT_RETRY_TIMEOUT(/etc/ansible/ansible.cfg) = 45
PERSISTENT_CONNECT_TIMEOUT(/etc/ansible/ansible.cfg) = 90
##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
Ubuntu 16.04.3
##### SUMMARY
<!--- Explain the problem briefly -->
In the deprecated module vsphere_guest when adding a NIC to a portgroup on a dvswitch you would receive the portkey that nic receives when joining the portgroup. This functionality should be carried over to the vmware_guest module.
##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
"hw_eth0": [
    "addresstype": "assigned",
    "ipaddresses": "null",
    "label": "Network adapter 1",
    "macaddress": "some:mac:address",
    "macaddress_dash: "some-mac-address",
    "summary": "DVSwitch: hex output here",
    "portgroup_portkey": "1234"


Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/vmware/vmware_guest.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/vmware/vmware_guest.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @Akasurde @akasurde @dagwieers @dav1x @jctanner @nerzhul @pdellaert
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py
--- a/lib/ansible/module_utils/vmware.py
+++ b/lib/ansible/module_utils/vmware.py
@@ -103,7 +103,7 @@ def find_entity_child_by_path(content, entityRootFolder, path):
 
         if entity.name == paths[-1]:
             return entity
-    except:
+    except BaseException:
         pass
 
     return None
@@ -337,7 +337,7 @@ def gather_vm_facts(content, vm):
             for item in vm.layout.disk:
                 for disk in item.diskFile:
                     facts['hw_files'].append(disk)
-    except:
+    except BaseException:
         pass
 
     facts['hw_folder'] = PyVmomi.get_vm_path(content, vm)
@@ -380,6 +380,14 @@ def gather_vm_facts(content, vm):
         else:
             mac_addr = mac_addr_dash = None
 
+        if (hasattr(entry, 'backing') and hasattr(entry.backing, 'port') and
+                hasattr(entry.backing.port, 'portKey') and hasattr(entry.backing.port, 'portgroupKey')):
+            port_group_key = entry.backing.port.portgroupKey
+            port_key = entry.backing.port.portKey
+        else:
+            port_group_key = None
+            port_key = None
+
         factname = 'hw_eth' + str(ethernet_idx)
         facts[factname] = {
             'addresstype': entry.addressType,
@@ -388,6 +396,8 @@ def gather_vm_facts(content, vm):
             'ipaddresses': net_dict.get(entry.macAddress, None),
             'macaddress_dash': mac_addr_dash,
             'summary': entry.deviceInfo.summary,
+            'portgroup_portkey': port_key,
+            'portgroup_key': port_group_key,
         }
         facts['hw_interfaces'].append('eth' + str(ethernet_idx))
         ethernet_idx += 1
@@ -1048,7 +1058,7 @@ def get_vm_path(content, vm_name):
                 folder_name = fp.name + '/' + folder_name
                 try:
                     fp = fp.parent
-                except:
+                except BaseException:
                     break
             folder_name = '/' + folder_name
         return folder_name


Test patch:
diff --git a/test/integration/targets/vmware_guest/tasks/main.yml b/test/integration/targets/vmware_guest/tasks/main.yml
--- a/test/integration/targets/vmware_guest/tasks/main.yml
+++ b/test/integration/targets/vmware_guest/tasks/main.yml
@@ -27,8 +27,10 @@
 - include: delete_vm.yml
 - include: non_existent_vm_ops.yml
 - include: network_negative_test.yml
+# VCSIM does not return list of portgroups for dvswitch so commenting following TC
+#- include: network_with_portgroup.yml
 # 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: vapp_d1_c1_f0.yml
 - include: disk_size_d1_c1_f0.yml
diff --git a/test/integration/targets/vmware_guest/tasks/network_with_portgroup.yml b/test/integration/targets/vmware_guest/tasks/network_with_portgroup.yml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/vmware_guest/tasks/network_with_portgroup.yml
@@ -0,0 +1,96 @@
+# Test code for the vmware_guest module.
+# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>, Tim Steinbach <tim@nequissimus.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- 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 VMS from vcsim
+  uri:
+    url: http://{{ vcsim }}:5000/govc_find?filter=VM
+  register: vmlist
+
+- debug: var=vcsim_instance
+
+- debug: var=vmlist
+
+- set_fact:
+   vm1: "{{ vmlist['json'][0] }}"
+
+- debug: var=vm1
+
+- name: Create dvswitch
+  vmware_dvswitch:
+    validate_certs: False
+    hostname: "{{ vcsim }}"
+    username: "{{ vcsim_instance['json']['username'] }}"
+    password: "{{ vcsim_instance['json']['password'] }}"
+    datacenter_name: "{{ (vm1 | basename).split('_')[0] }}"
+    switch_name: dvSwitch
+    uplink_quantity: 2
+    discovery_proto: lldp
+    discovery_operation: both
+    mtu: 9000
+    state: present
+  register: dvswitch
+
+- name: Add portgroup
+  vmware_dvs_portgroup:
+    validate_certs: False
+    hostname: "{{ vcsim }}"
+    username: "{{ vcsim_instance['json']['username'] }}"
+    password: "{{ vcsim_instance['json']['password'] }}"
+    portgroup_name: "portgroup_network"
+    switch_name: "{{ dvswitch.switch_name }}"
+    vlan_id: "1"
+    num_ports: 2
+    portgroup_type: earlyBinding
+    state: present
+  register: dvsportgroup
+
+- name: create new VMs with portgroup
+  vmware_guest:
+    validate_certs: False
+    hostname: "{{ vcsim }}"
+    username: "{{ vcsim_instance['json']['username'] }}"
+    password: "{{ vcsim_instance['json']['password'] }}"
+    name: new_vm_pg
+    guest_id: centos64Guest
+    datacenter: "{{ (vm1 | basename).split('_')[0] }}"
+    disk:
+      - size: 3mb
+        type: thin
+        autoselect_datastore: yes
+    networks:
+      - name: "{{ dvsportgroup.portgroup_name }}"
+    hardware:
+        num_cpus: 1
+        memory_mb: 512
+    state: poweredoff
+    folder: "{{ vm1 | dirname }}"
+  register: vm_with_portgroup
+  ignore_errors: no
+
+- debug: var=vm_with_portgroup
+
+- assert:
+    that:
+      - "vm_with_portgroup.changed"
diff --git a/test/integration/targets/vmware_guest_facts/tasks/main.yml b/test/integration/targets/vmware_guest_facts/tasks/main.yml
--- a/test/integration/targets/vmware_guest_facts/tasks/main.yml
+++ b/test/integration/targets/vmware_guest_facts/tasks/main.yml
@@ -83,6 +83,8 @@
       - "guest_facts_0001['instance']['hw_folder'] is defined"
       - "guest_facts_0001['instance']['guest_question'] is defined"
       - "guest_facts_0001['instance']['guest_consolidation_needed'] is defined"
+      - "'portgroup_portkey' in guest_facts_0001['instance']['hw_eth0']"
+      - "'portgroup_key' in guest_facts_0001['instance']['hw_eth0']"
 
 - set_fact: vm1_uuid="{{ guest_facts_0001['instance']['hw_product_uuid'] }}"