Repository: ansible-collections/community.vmware

Issue:
vmware_dvs_portgroup add possibility to override the failover
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
Currently it is not possible to override the NIC Teaming failover order of a distributed port group. 

As far as I know the failover order can be specified with the usage of 
`nicTeaming.nicOrder.activeNic` and` nicTeaming.nicOrder.standbyNic`.

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
vmware_dvs_portgroup

##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->
With the new setting "activeNic" only the LAG1 inside the failover order should be set to active. With the empty value of "standbyNic" all other Interfaces should be set to "unused"

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: Create vlan portgroup
  community.vmware.vmware_dvs_portgroup:
    hostname: '{{ vcenter_hostname }}'
    username: '{{ vcenter_username }}'
    password: '{{ vcenter_password }}'
    portgroup_name: vlan-123-portrgoup
    switch_name: dvSwitch
    vlan_id: 123
    num_ports: 120
    portgroup_type: earlyBinding
    state: present
    teaming_policy:
      activeNic: ['LAG1']
      standbyNic: []
  delegate_to: localhost
```

<!--- HINT: You can also paste gist.github.com links for larger files -->



Discussion:
Files identified in the description:
* [`plugins/modules/vmware_dvs_portgroup.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/vmware_dvs_portgroup.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 @Tomorrow9 @goneri @lparkes @pdellaert @pgbidkar @warthog9
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/vmware_dvs_portgroup.py b/plugins/modules/vmware_dvs_portgroup.py
--- a/plugins/modules/vmware_dvs_portgroup.py
+++ b/plugins/modules/vmware_dvs_portgroup.py
@@ -19,7 +19,7 @@
     - Joseph Callen (@jcpowermac)
     - Philippe Dellaert (@pdellaert) <philippe@dellaert.org>
 notes:
-    - Tested on vSphere 5.5, 6.5
+    - Tested on vSphere 7.0
 requirements:
     - "python >= 2.6"
     - PyVmomi
@@ -45,17 +45,35 @@
     num_ports:
         description:
             - The number of ports the portgroup should contain.
-        required: True
         type: int
     portgroup_type:
         description:
             - See VMware KB 1022312 regarding portgroup types.
-        required: True
+            - Deprecated. Will be removed 2021-12-01.
         choices:
             - 'earlyBinding'
             - 'lateBinding'
             - 'ephemeral'
         type: str
+    port_binding:
+        description:
+            - The type of port binding determines when ports in a port group are assigned to virtual machines.
+            - See VMware KB 1022312 U(https://kb.vmware.com/s/article/1022312) for more details.
+        type: str
+        choices:
+            - 'static'
+            - 'ephemeral'
+        version_added: '1.10.0'
+    port_allocation:
+        description:
+            - Elastic port groups automatically increase or decrease the number of ports as needed.
+            - Only valid if I(port_binding) is set to C(static).
+            - Will be C(elastic) if not specified and I(port_binding) is set to C(static).
+        type: str
+        choices:
+            - 'elastic'
+            - 'fixed'
+        version_added: '1.10.0'
     state:
         description:
             - Determines if the portgroup should be present or not.
@@ -78,6 +96,31 @@
         required: False
         default: False
         type: bool
+    mac_learning:
+        description:
+            - Dictionary which configures MAC learning for portgroup.
+        suboptions:
+            allow_unicast_flooding:
+                type: bool
+                description: The flag to allow flooding of unlearned MAC for ingress traffic.
+                required: False
+            enabled:
+                type: bool
+                description: The flag to indicate if source MAC address learning is allowed.
+                required: False
+            limit:
+                type: int
+                description: The maximum number of MAC addresses that can be learned.
+                required: False
+            limit_policy:
+                type: str
+                description: The default switching policy after MAC limit is exceeded.
+                required: False
+                choices:
+                    - 'allow'
+                    - 'drop'
+        type: dict
+        version_added: '1.10.0'
     network_policy:
         description:
             - Dictionary which configures the different security values for portgroup.
@@ -126,16 +169,26 @@
                 description:
                 - Indicate whether or not the teaming policy is applied to inbound frames as well.
                 type: bool
-                default: False
             rolling_order:
                 description:
                 - Indicate whether or not to use a rolling policy when restoring links.
                 default: False
                 type: bool
+            active_uplinks:
+                description:
+                - List of active uplinks used for load balancing.
+                type: list
+                elements: str
+                version_added: '1.10.0'
+            standby_uplinks:
+                description:
+                - List of standby uplinks used for failover.
+                type: list
+                elements: str
+                version_added: '1.10.0'
         default: {
             'notify_switches': True,
             'load_balance_policy': 'loadbalance_srcid',
-            'inbound_policy': False,
             'rolling_order': False
         }
         type: dict
@@ -228,7 +281,7 @@
     switch_name: dvSwitch
     vlan_id: 123
     num_ports: 120
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   delegate_to: localhost
 
@@ -242,7 +295,7 @@
     vlan_id: 1-1000, 1005, 1100-1200
     vlan_trunk: True
     num_ports: 120
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   delegate_to: localhost
 
@@ -256,7 +309,7 @@
     vlan_id: 1001
     vlan_private: True
     num_ports: 120
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   delegate_to: localhost
 
@@ -269,7 +322,7 @@
     switch_name: dvSwitch
     vlan_id: 0
     num_ports: 120
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   delegate_to: localhost
 
@@ -282,7 +335,7 @@
     switch_name: dvSwitch
     vlan_id: 123
     num_ports: 120
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
     network_policy:
       promiscuous: true
@@ -323,6 +376,21 @@ def __init__(self, module):
         self.dvs_portgroup = None
         self.dv_switch = None
 
+        # Some sanity checks
+        if self.module.params['port_allocation'] == 'elastic':
+            if self.module.params['port_binding'] == 'ephemeral':
+                self.module.fail_json(
+                    msg="'elastic' port allocation is not supported on an 'ephemeral' portgroup."
+                )
+
+            if self.module.params['num_ports']:
+                self.module.fail_json(
+                    msg="The number of ports cannot be configured when port allocation is set to 'elastic'."
+                )
+
+    def supports_mac_learning(self):
+        return hasattr(self.dv_switch.capability.featuresSupported, 'macLearningSupported') and self.dv_switch.capability.featuresSupported.macLearningSupported
+
     def create_vlan_list(self):
         vlan_id_list = []
         for vlan_id_splitted in self.module.params['vlan_id'].split(','):
@@ -357,7 +425,9 @@ def build_config(self):
 
         # Basic config
         config.name = self.module.params['portgroup_name']
-        config.numPorts = self.module.params['num_ports']
+
+        if self.module.params['port_allocation'] != 'elastic' and self.module.params['port_binding'] != 'ephemeral':
+            config.numPorts = self.module.params['num_ports']
 
         # Default port config
         config.defaultPortConfig = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy()
@@ -377,18 +447,49 @@ def build_config(self):
         else:
             config.defaultPortConfig.vlan = vim.dvs.VmwareDistributedVirtualSwitch.VlanIdSpec()
             config.defaultPortConfig.vlan.vlanId = int(self.module.params['vlan_id'])
+
         config.defaultPortConfig.vlan.inherited = False
-        config.defaultPortConfig.securityPolicy = vim.dvs.VmwareDistributedVirtualSwitch.SecurityPolicy()
-        config.defaultPortConfig.securityPolicy.allowPromiscuous = vim.BoolPolicy(value=self.module.params['network_policy']['promiscuous'])
-        config.defaultPortConfig.securityPolicy.forgedTransmits = vim.BoolPolicy(value=self.module.params['network_policy']['forged_transmits'])
-        config.defaultPortConfig.securityPolicy.macChanges = vim.BoolPolicy(value=self.module.params['network_policy']['mac_changes'])
+
+        # If the dvSwitch supports MAC learning, it's a version where securityPolicy is deprecated
+        if self.supports_mac_learning():
+            config.defaultPortConfig.macManagementPolicy = vim.dvs.VmwareDistributedVirtualSwitch.MacManagementPolicy()
+            config.defaultPortConfig.macManagementPolicy.allowPromiscuous = self.module.params['network_policy']['promiscuous']
+            config.defaultPortConfig.macManagementPolicy.forgedTransmits = self.module.params['network_policy']['forged_transmits']
+            config.defaultPortConfig.macManagementPolicy.macChanges = self.module.params['network_policy']['mac_changes']
+
+            macLearning = self.module.params['mac_learning']
+            if macLearning:
+                macLearningPolicy = vim.dvs.VmwareDistributedVirtualSwitch.MacLearningPolicy()
+                if macLearning['allow_unicast_flooding'] is not None:
+                    macLearningPolicy.allowUnicastFlooding = macLearning['allow_unicast_flooding']
+                if macLearning['enabled'] is not None:
+                    macLearningPolicy.enabled = macLearning['enabled']
+                if macLearning['limit'] is not None:
+                    macLearningPolicy.limit = macLearning['limit']
+                if macLearning['limit_policy']:
+                    macLearningPolicy.limitPolicy = macLearning['limit_policy']
+                config.defaultPortConfig.macManagementPolicy.macLearningPolicy = macLearningPolicy
+        else:
+            config.defaultPortConfig.securityPolicy = vim.dvs.VmwareDistributedVirtualSwitch.SecurityPolicy()
+            config.defaultPortConfig.securityPolicy.allowPromiscuous = vim.BoolPolicy(value=self.module.params['network_policy']['promiscuous'])
+            config.defaultPortConfig.securityPolicy.forgedTransmits = vim.BoolPolicy(value=self.module.params['network_policy']['forged_transmits'])
+            config.defaultPortConfig.securityPolicy.macChanges = vim.BoolPolicy(value=self.module.params['network_policy']['mac_changes'])
 
         # Teaming Policy
         teamingPolicy = vim.dvs.VmwareDistributedVirtualSwitch.UplinkPortTeamingPolicy()
         teamingPolicy.policy = vim.StringPolicy(value=self.module.params['teaming_policy']['load_balance_policy'])
-        teamingPolicy.reversePolicy = vim.BoolPolicy(value=self.module.params['teaming_policy']['inbound_policy'])
+        if self.module.params['teaming_policy']['inbound_policy'] is not None:
+            teamingPolicy.reversePolicy = vim.BoolPolicy(value=self.module.params['teaming_policy']['inbound_policy'])
         teamingPolicy.notifySwitches = vim.BoolPolicy(value=self.module.params['teaming_policy']['notify_switches'])
         teamingPolicy.rollingOrder = vim.BoolPolicy(value=self.module.params['teaming_policy']['rolling_order'])
+
+        if self.module.params['teaming_policy']['active_uplinks'] or self.module.params['teaming_policy']['standby_uplinks']:
+            teamingPolicy.uplinkPortOrder = vim.dvs.VmwareDistributedVirtualSwitch.UplinkPortOrderPolicy()
+            if self.module.params['teaming_policy']['active_uplinks']:
+                teamingPolicy.uplinkPortOrder.activeUplinkPort = self.module.params['teaming_policy']['active_uplinks']
+            if self.module.params['teaming_policy']['standby_uplinks']:
+                teamingPolicy.uplinkPortOrder.standbyUplinkPort = self.module.params['teaming_policy']['standby_uplinks']
+
         config.defaultPortConfig.uplinkTeamingPolicy = teamingPolicy
 
         # PG policy (advanced_policy)
@@ -406,7 +507,19 @@ def build_config(self):
         config.policy.vlanOverrideAllowed = self.module.params['port_policy']['vlan_override']
 
         # PG Type
-        config.type = self.module.params['portgroup_type']
+        # NOTE: 'portgroup_type' is deprecated.
+        if self.module.params['portgroup_type']:
+            config.type = self.module.params['portgroup_type']
+        elif self.module.params['port_binding'] == 'ephemeral':
+            config.type = 'ephemeral'
+        else:
+            config.type = 'earlyBinding'
+
+        if self.module.params['port_allocation']:
+            if self.module.params['port_allocation'] == 'elastic':
+                config.autoExpand = True
+            else:
+                config.autoExpand = False
 
         return config
 
@@ -491,9 +604,9 @@ def check_dvspg_state(self):
             return 'absent'
 
         # Check config
-        # Basic config
-        if self.dvs_portgroup.config.numPorts != self.module.params['num_ports']:
-            return 'update'
+        if self.module.params['port_allocation'] != 'elastic' and self.module.params['port_binding'] != 'ephemeral':
+            if self.dvs_portgroup.config.numPorts != self.module.params['num_ports']:
+                return 'update'
 
         # Default port config
         defaultPortConfig = self.dvs_portgroup.config.defaultPortConfig
@@ -513,19 +626,50 @@ def check_dvspg_state(self):
             if defaultPortConfig.vlan.vlanId != int(self.module.params['vlan_id']):
                 return 'update'
 
-        if defaultPortConfig.securityPolicy.allowPromiscuous.value != self.module.params['network_policy']['promiscuous'] or \
-                defaultPortConfig.securityPolicy.forgedTransmits.value != self.module.params['network_policy']['forged_transmits'] or \
-                defaultPortConfig.securityPolicy.macChanges.value != self.module.params['network_policy']['mac_changes']:
-            return 'update'
+        # If the dvSwitch supports MAC learning, it's a version where securityPolicy is deprecated
+        if self.supports_mac_learning():
+            if defaultPortConfig.macManagementPolicy.allowPromiscuous != self.module.params['network_policy']['promiscuous'] or \
+                    defaultPortConfig.macManagementPolicy.forgedTransmits != self.module.params['network_policy']['forged_transmits'] or \
+                    defaultPortConfig.macManagementPolicy.macChanges != self.module.params['network_policy']['mac_changes']:
+                return 'update'
+
+            macLearning = self.module.params['mac_learning']
+            if macLearning:
+                macLearningPolicy = defaultPortConfig.macManagementPolicy.macLearningPolicy
+                if macLearning['allow_unicast_flooding'] is not None and macLearningPolicy.allowUnicastFlooding != macLearning['allow_unicast_flooding']:
+                    return 'update'
+                if macLearning['enabled'] is not None and macLearningPolicy.enabled != macLearning['enabled']:
+                    return 'update'
+                if macLearning['limit'] is not None and macLearningPolicy.limit != macLearning['limit']:
+                    return 'update'
+                if macLearning['limit_policy'] and macLearningPolicy.limitPolicy != macLearning['limit_policy']:
+                    return 'update'
+        else:
+            if defaultPortConfig.securityPolicy.allowPromiscuous.value != self.module.params['network_policy']['promiscuous'] or \
+                    defaultPortConfig.securityPolicy.forgedTransmits.value != self.module.params['network_policy']['forged_transmits'] or \
+                    defaultPortConfig.securityPolicy.macChanges.value != self.module.params['network_policy']['mac_changes']:
+                return 'update'
 
         # Teaming Policy
         teamingPolicy = self.dvs_portgroup.config.defaultPortConfig.uplinkTeamingPolicy
+
+        if self.module.params['teaming_policy']['inbound_policy'] is not None and \
+                teamingPolicy.reversePolicy.value != self.module.params['teaming_policy']['inbound_policy']:
+            return 'update'
+
         if teamingPolicy.policy.value != self.module.params['teaming_policy']['load_balance_policy'] or \
-                teamingPolicy.reversePolicy.value != self.module.params['teaming_policy']['inbound_policy'] or \
                 teamingPolicy.notifySwitches.value != self.module.params['teaming_policy']['notify_switches'] or \
                 teamingPolicy.rollingOrder.value != self.module.params['teaming_policy']['rolling_order']:
             return 'update'
 
+        if self.module.params['teaming_policy']['active_uplinks'] and \
+                teamingPolicy.uplinkPortOrder.activeUplinkPort != self.module.params['teaming_policy']['active_uplinks']:
+            return 'update'
+
+        if self.module.params['teaming_policy']['standby_uplinks'] and \
+                teamingPolicy.uplinkPortOrder.standbyUplinkPort != self.module.params['teaming_policy']['standby_uplinks']:
+            return 'update'
+
         # PG policy (advanced_policy)
         policy = self.dvs_portgroup.config.policy
         if policy.blockOverrideAllowed != self.module.params['port_policy']['block_override'] or \
@@ -542,9 +686,23 @@ def check_dvspg_state(self):
             return 'update'
 
         # PG Type
-        if self.dvs_portgroup.config.type != self.module.params['portgroup_type']:
+        # NOTE: 'portgroup_type' is deprecated.
+        if self.module.params['portgroup_type']:
+            if self.dvs_portgroup.config.type != self.module.params['portgroup_type']:
+                return 'update'
+        elif self.module.params['port_binding'] == 'ephemeral':
+            if self.dvs_portgroup.config.type != 'ephemeral':
+                return 'update'
+        elif self.dvs_portgroup.config.type != 'earlyBinding':
             return 'update'
 
+        # Check port allocation
+        if self.module.params['port_allocation']:
+            if self.module.params['port_allocation'] == 'elastic' and self.dvs_portgroup.config.autoExpand is False:
+                return 'update'
+            elif self.module.params['port_allocation'] == 'fixed' and self.dvs_portgroup.config.autoExpand is True:
+                return 'update'
+
         return 'present'
 
 
@@ -555,8 +713,15 @@ def main():
             portgroup_name=dict(required=True, type='str'),
             switch_name=dict(required=True, type='str'),
             vlan_id=dict(required=True, type='str'),
-            num_ports=dict(required=True, type='int'),
-            portgroup_type=dict(required=True, choices=['earlyBinding', 'lateBinding', 'ephemeral'], type='str'),
+            num_ports=dict(type='int'),
+            portgroup_type=dict(
+                type='str',
+                choices=['earlyBinding', 'lateBinding', 'ephemeral'],
+                removed_at_date='2021-12-01',
+                removed_from_collection='community.vmware',
+            ),
+            port_binding=dict(type='str', choices=['static', 'ephemeral']),
+            port_allocation=dict(type='str', choices=['fixed', 'elastic']),
             state=dict(required=True, choices=['present', 'absent'], type='str'),
             vlan_trunk=dict(type='bool', default=False),
             vlan_private=dict(type='bool', default=False),
@@ -576,7 +741,7 @@ def main():
             teaming_policy=dict(
                 type='dict',
                 options=dict(
-                    inbound_policy=dict(type='bool', default=False),
+                    inbound_policy=dict(type='bool'),
                     notify_switches=dict(type='bool', default=True),
                     rolling_order=dict(type='bool', default=False),
                     load_balance_policy=dict(type='str',
@@ -588,10 +753,11 @@ def main():
                                                  'loadbalance_loadbased',
                                                  'failover_explicit',
                                              ],
-                                             )
+                                             ),
+                    active_uplinks=dict(type='list', elements='str'),
+                    standby_uplinks=dict(type='list', elements='str'),
                 ),
                 default=dict(
-                    inbound_policy=False,
                     notify_switches=True,
                     rolling_order=False,
                     load_balance_policy='loadbalance_srcid',
@@ -624,14 +790,28 @@ def main():
                     uplink_teaming_override=False,
                     vendor_config_override=False,
                     vlan_override=False
-                )
+                ),
+            ),
+            mac_learning=dict(
+                type='dict',
+                options=dict(
+                    allow_unicast_flooding=dict(type='bool'),
+                    enabled=dict(type='bool'),
+                    limit=dict(type='int'),
+                    limit_policy=dict(type='str', choices=['allow', 'drop']),
+                ),
             )
         )
     )
 
     module = AnsibleModule(argument_spec=argument_spec,
+                           required_one_of=[
+                               ['portgroup_type', 'port_binding'],
+                           ],
                            mutually_exclusive=[
-                               ['vlan_trunk', 'vlan_private']
+                               ['portgroup_type', 'port_binding'],
+                               ['portgroup_type', 'port_allocation'],
+                               ['vlan_trunk', 'vlan_private'],
                            ],
                            supports_check_mode=True)
 
diff --git a/plugins/modules/vmware_dvs_portgroup_info.py b/plugins/modules/vmware_dvs_portgroup_info.py
--- a/plugins/modules/vmware_dvs_portgroup_info.py
+++ b/plugins/modules/vmware_dvs_portgroup_info.py
@@ -18,7 +18,7 @@
 author:
 - Abhijeet Kasurde (@Akasurde)
 notes:
-- Tested on vSphere 6.5
+- Tested on vSphere 7.0
 requirements:
 - python >= 2.6
 - PyVmomi
@@ -33,6 +33,12 @@
     - Name of a dvswitch to look for.
     required: false
     type: str
+  show_mac_learning:
+    description:
+    - Show or hide MAC learning information of the DVS portgroup.
+    type: bool
+    default: True
+    version_added: '1.10.0'
   show_network_policy:
     description:
     - Show or hide network policies of DVS portgroup.
@@ -48,6 +54,12 @@
     - Show or hide teaming policies of DVS portgroup.
     type: bool
     default: True
+  show_uplinks:
+    description:
+    - Show or hide uplinks of DVS portgroup.
+    type: bool
+    default: True
+    version_added: '1.10.0'
   show_vlan_info:
     description:
     - Show or hide vlan information of the DVS portgroup.
@@ -153,6 +165,9 @@ def __init__(self, module):
             # default behaviour, gather information about all dvswitches
             self.dvsls = get_all_objs(self.content, [vim.DistributedVirtualSwitch], folder=datacenter.networkFolder)
 
+    def supports_mac_learning(self, dvs):
+        return hasattr(dvs.capability.featuresSupported, 'macLearningSupported') and dvs.capability.featuresSupported.macLearningSupported
+
     def get_vlan_info(self, vlan_obj=None):
         """
         Return vlan information from given object
@@ -184,19 +199,51 @@ def gather_dvs_portgroup_info(self):
         dvs_lists = self.dvsls
         result = dict()
         for dvs in dvs_lists:
+            switch_supports_mac_learning = self.supports_mac_learning(dvs)
             result[dvs.name] = list()
             for dvs_pg in dvs.portgroup:
+                mac_learning = dict()
                 network_policy = dict()
                 teaming_policy = dict()
                 port_policy = dict()
                 vlan_info = dict()
+                active_uplinks = list()
+                standby_uplinks = list()
+
+                if dvs_pg.config.type == 'ephemeral':
+                    port_binding = 'ephemeral'
+                else:
+                    port_binding = 'static'
+
+                if dvs_pg.config.autoExpand is True:
+                    port_allocation = 'elastic'
+                else:
+                    port_allocation = 'fixed'
+
+                # If the dvSwitch supports MAC learning, it's a version where securityPolicy is deprecated
+                if self.module.params['show_network_policy']:
+                    if switch_supports_mac_learning and dvs_pg.config.defaultPortConfig.macManagementPolicy:
+                        network_policy = dict(
+                            forged_transmits=dvs_pg.config.defaultPortConfig.macManagementPolicy.forgedTransmits,
+                            promiscuous=dvs_pg.config.defaultPortConfig.macManagementPolicy.allowPromiscuous,
+                            mac_changes=dvs_pg.config.defaultPortConfig.macManagementPolicy.macChanges
+                        )
+                    elif dvs_pg.config.defaultPortConfig.securityPolicy:
+                        network_policy = dict(
+                            forged_transmits=dvs_pg.config.defaultPortConfig.securityPolicy.forgedTransmits.value,
+                            promiscuous=dvs_pg.config.defaultPortConfig.securityPolicy.allowPromiscuous.value,
+                            mac_changes=dvs_pg.config.defaultPortConfig.securityPolicy.macChanges.value
+                        )
 
-                if self.module.params['show_network_policy'] and dvs_pg.config.defaultPortConfig.securityPolicy:
-                    network_policy = dict(
-                        forged_transmits=dvs_pg.config.defaultPortConfig.securityPolicy.forgedTransmits.value,
-                        promiscuous=dvs_pg.config.defaultPortConfig.securityPolicy.allowPromiscuous.value,
-                        mac_changes=dvs_pg.config.defaultPortConfig.securityPolicy.macChanges.value
+                if self.module.params['show_mac_learning'] and switch_supports_mac_learning:
+                    macLearningPolicy = dvs_pg.config.defaultPortConfig.macManagementPolicy.macLearningPolicy
+                    mac_learning = dict(
+                        allow_unicast_flooding=macLearningPolicy.allowUnicastFlooding,
+                        enabled=macLearningPolicy.enabled,
+                        limit=macLearningPolicy.limit,
+                        limit_policy=macLearningPolicy.limitPolicy
                     )
+
                 if self.module.params['show_teaming_policy']:
                     # govcsim does not have uplinkTeamingPolicy, remove this check once
                     # PR https://github.com/vmware/govmomi/pull/1524 merged.
@@ -208,6 +255,12 @@ def gather_dvs_portgroup_info(self):
                             rolling_order=dvs_pg.config.defaultPortConfig.uplinkTeamingPolicy.rollingOrder.value,
                         )
 
+                if self.module.params['show_uplinks'] and \
+                        dvs_pg.config.defaultPortConfig.uplinkTeamingPolicy and \
+                        dvs_pg.config.defaultPortConfig.uplinkTeamingPolicy.uplinkPortOrder:
+                    active_uplinks = dvs_pg.config.defaultPortConfig.uplinkTeamingPolicy.uplinkPortOrder.activeUplinkPort
+                    standby_uplinks = dvs_pg.config.defaultPortConfig.uplinkTeamingPolicy.uplinkPortOrder.standbyUplinkPort
+
                 if self.params['show_port_policy']:
                     # govcsim does not have port policy
                     if dvs_pg.config.policy:
@@ -234,11 +287,16 @@ def gather_dvs_portgroup_info(self):
                     dvswitch_name=dvs_pg.config.distributedVirtualSwitch.name,
                     description=dvs_pg.config.description,
                     type=dvs_pg.config.type,
+                    port_binding=port_binding,
+                    port_allocation=port_allocation,
                     teaming_policy=teaming_policy,
                     port_policy=port_policy,
+                    mac_learning=mac_learning,
                     network_policy=network_policy,
                     vlan_info=vlan_info,
                     key=dvs_pg.key,
+                    active_uplinks=active_uplinks,
+                    standby_uplinks=standby_uplinks,
                 )
                 result[dvs.name].append(dvpg_details)
 
@@ -249,8 +307,10 @@ def main():
     argument_spec = vmware_argument_spec()
     argument_spec.update(
         datacenter=dict(type='str', required=True),
+        show_mac_learning=dict(type='bool', default=True),
         show_network_policy=dict(type='bool', default=True),
         show_teaming_policy=dict(type='bool', default=True),
+        show_uplinks=dict(type='bool', default=True),
         show_port_policy=dict(type='bool', default=True),
         dvswitch=dict(),
         show_vlan_info=dict(type='bool', default=False),


Test patch:
diff --git a/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvswitch.yml b/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvswitch.yml
--- a/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvswitch.yml
+++ b/tests/integration/targets/prepare_vmware_tests/tasks/setup_dvswitch.yml
@@ -3,7 +3,7 @@
   vmware_dvswitch:
     datacenter_name: '{{ dc1 }}'
     switch_name: '{{ dvswitch1 }}'
-    switch_version: 6.5.0
+    switch_version: 6.6.0
     mtu: 9000
     uplink_quantity: 2
     discovery_proto: lldp
diff --git a/tests/integration/targets/vmware_dvs_portgroup/tasks/main.yml b/tests/integration/targets/vmware_dvs_portgroup/tasks/main.yml
--- a/tests/integration/targets/vmware_dvs_portgroup/tasks/main.yml
+++ b/tests/integration/targets/vmware_dvs_portgroup/tasks/main.yml
@@ -17,7 +17,7 @@
     portgroup_name: "basic"
     vlan_id: 0
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0001
 
@@ -29,6 +29,104 @@
     that:
         - dvs_pg_result_0001.changed
 
+- when: vcsim is not defined
+  block:
+    - name: enable MAC learning
+      vmware_dvs_portgroup:
+        validate_certs: false
+        hostname: "{{ vcenter_hostname }}"
+        username: "{{ vcenter_username }}"
+        password: "{{ vcenter_password }}"
+        switch_name: "{{ dvswitch1 }}"
+        portgroup_name: "basic"
+        vlan_id: 0
+        num_ports: 32
+        port_binding: static
+        mac_learning:
+          enabled: true
+        state: present
+      register: enable_mac_learning_result
+
+    - debug:
+        var: enable_mac_learning_result
+
+    - name: ensure MAC learning is enabled
+      assert:
+        that:
+            - enable_mac_learning_result.changed
+
+    - name: enable MAC learning again (idempotency)
+      vmware_dvs_portgroup:
+        validate_certs: false
+        hostname: "{{ vcenter_hostname }}"
+        username: "{{ vcenter_username }}"
+        password: "{{ vcenter_password }}"
+        switch_name: "{{ dvswitch1 }}"
+        portgroup_name: "basic"
+        vlan_id: 0
+        num_ports: 32
+        port_binding: static
+        mac_learning:
+          enabled: true
+        state: present
+      register: enable_mac_learning_again_result
+
+    - debug:
+        var: enable_mac_learning_again_result
+
+    - name: ensure MAC learning is not enabled again
+      assert:
+        that:
+            - not enable_mac_learning_again_result.changed
+
+    - name: disable MAC learning
+      vmware_dvs_portgroup:
+        validate_certs: false
+        hostname: "{{ vcenter_hostname }}"
+        username: "{{ vcenter_username }}"
+        password: "{{ vcenter_password }}"
+        switch_name: "{{ dvswitch1 }}"
+        portgroup_name: "basic"
+        vlan_id: 0
+        num_ports: 32
+        port_binding: static
+        mac_learning:
+          enabled: false
+        state: present
+      register: disable_mac_learning_result
+
+    - debug:
+        var: disable_mac_learning_result
+
+    - name: ensure MAC learning is disabled
+      assert:
+        that:
+            - disable_mac_learning_result.changed
+
+    - name: disable MAC learning again (idempotency)
+      vmware_dvs_portgroup:
+        validate_certs: false
+        hostname: "{{ vcenter_hostname }}"
+        username: "{{ vcenter_username }}"
+        password: "{{ vcenter_password }}"
+        switch_name: "{{ dvswitch1 }}"
+        portgroup_name: "basic"
+        vlan_id: 0
+        num_ports: 32
+        port_binding: static
+        mac_learning:
+          enabled: false
+        state: present
+      register: disable_mac_learning_again_result
+
+    - debug:
+        var: disable_mac_learning_again_result
+
+    - name: ensure MAC learning is not disabled again
+      assert:
+        that:
+            - not disable_mac_learning_again_result.changed
+
 - name: create basic VLAN portgroup
   vmware_dvs_portgroup:
     validate_certs: false
@@ -39,7 +137,7 @@
     portgroup_name: "basic-vlan10"
     vlan_id: 10
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0002
 
@@ -59,7 +157,7 @@
     vlan_id: 1-4094
     vlan_trunk: true
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0003
 
@@ -78,7 +176,7 @@
     portgroup_name: "basic"
     vlan_id: 0
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0004
 
@@ -97,7 +195,7 @@
     portgroup_name: "basic-all-enabled"
     vlan_id: 0
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
     network_policy:
       promiscuous: true
@@ -132,7 +230,7 @@
     portgroup_name: "basic-some-enabled"
     vlan_id: 0
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
     network_policy:
       promiscuous: true
@@ -157,7 +255,7 @@
     portgroup_name: "basic-some-enabled"
     vlan_id: 0
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
     network_policy:
       promiscuous: true
@@ -182,7 +280,7 @@
     portgroup_name: "basic-some-enabled"
     vlan_id: 0
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
     network_policy:
       promiscuous: true
@@ -207,7 +305,7 @@
     portgroup_name: "basic-some-enabled"
     vlan_id: 0
     num_ports: 16
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
     network_policy:
       promiscuous: true
@@ -232,7 +330,7 @@
     portgroup_name: "basic-some-enabled"
     vlan_id: 0
     num_ports: 16
-    portgroup_type: ephemeral
+    port_binding: ephemeral
     state: present
     network_policy:
       promiscuous: true
@@ -257,7 +355,7 @@
     portgroup_name: "basic"
     vlan_id: 0
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: absent
   register: dvs_pg_result_0011
 
@@ -276,7 +374,7 @@
     portgroup_name: "basic"
     vlan_id: 0
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: absent
   register: dvs_pg_result_0012
 
@@ -296,7 +394,7 @@
     vlan_id: 1-4096
     vlan_trunk: true
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0013
   ignore_errors: true
@@ -317,7 +415,7 @@
     portgroup_name: "basic-vlan10"
     vlan_id: 20
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0014
 
@@ -337,7 +435,7 @@
     vlan_id: 1000-2000
     vlan_trunk: true
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0015
 
@@ -357,7 +455,7 @@
     vlan_id: 1-1000, 1005, 1100-1200
     vlan_trunk: true
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0016
 
@@ -377,7 +475,7 @@
     vlan_id: 1-1000, 1006, 1100-1200
     vlan_trunk: true
     num_ports: 32
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
   register: dvs_pg_result_0017
 
@@ -396,7 +494,7 @@
     vlan_id: 10
     vlan_private: true
     num_ports: 12
-    portgroup_type: earlyBinding
+    port_binding: static
     state: present
     validate_certs: false
   register: dvs_pg_result_0018
@@ -460,7 +558,7 @@
         vlan_id: 1
         vlan_private: true
         num_ports: 12
-        portgroup_type: earlyBinding
+        port_binding: static
         state: present
         validate_certs: false
       register: dvs_pg_result_0019
@@ -480,7 +578,7 @@
         vlan_id: 2
         vlan_private: true
         num_ports: 12
-        portgroup_type: earlyBinding
+        port_binding: static
         state: present
         validate_certs: false
       register: dvs_pg_result_0020
@@ -499,7 +597,7 @@
         switch_name: dvswitch_0001
         vlan_id: 5
         num_ports: 12
-        portgroup_type: earlyBinding
+        port_binding: static
         state: present
         validate_certs: false
       register: dvs_pg_result_0021
@@ -550,7 +648,7 @@
             portgroup_name: 'dvportgroup\/%'
             vlan_id: 1
             num_ports: 8
-            portgroup_type: earlyBinding
+            port_binding: static
             state: present
           register: create_dvportgroup_with_special_characters_result
 
@@ -568,7 +666,7 @@
             portgroup_name: 'dvportgroup\/%'
             vlan_id: 1
             num_ports: 8
-            portgroup_type: earlyBinding
+            port_binding: static
             state: present
           register: create_dvportgroup_with_special_characters_idempotency_check_result
 
@@ -586,7 +684,7 @@
             portgroup_name: 'dvportgroup\/%'
             vlan_id: 1
             num_ports: 8
-            portgroup_type: earlyBinding
+            port_binding: static
             state: absent
           register: delete_dvportgroup_with_special_characters_result
 
@@ -604,7 +702,7 @@
             portgroup_name: 'dvportgroup\/%'
             vlan_id: 1
             num_ports: 8
-            portgroup_type: earlyBinding
+            port_binding: static
             state: absent
           register: delete_dvportgroup_with_special_characters_idempotency_check_result
 
@@ -643,7 +741,7 @@
         vlan_id: "1500,1501"
         vlan_trunk: true
         num_ports: 120
-        portgroup_type: earlyBinding
+        port_binding: static
         network_policy:
           promiscuous: false
           forged_transmits: true
@@ -666,7 +764,7 @@
         vlan_id: "1500,1501"
         vlan_trunk: true
         num_ports: 120
-        portgroup_type: earlyBinding
+        port_binding: static
         network_policy:
           promiscuous: false
           forged_transmits: true
@@ -689,7 +787,7 @@
         vlan_id: "1500,1501"
         vlan_trunk: true
         num_ports: 120
-        portgroup_type: earlyBinding
+        port_binding: static
         network_policy:
           promiscuous: false
           forged_transmits: true