Repository: ansible-collections/community.vmware

Issue:
Teaming and security on vswitch
<!--- Verify first that your feature was not already discussed on GitHub -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
<!--- Describe the new feature/improvement briefly below -->
vmware_vswitch should allow setting of teaming and security parameters
##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
vmware/vswitch
##### ADDITIONAL INFORMATION
<!--- Describe how the feature would be used, why it is needed and what it would solve -->
The vmware_vswitch module allows adding NICs to vswitches, but currently there is no way to configure how those nics are set up for teaming or resilience.  Similarly there is no way to set the security functions on the whole switch.
This functionality is already present in the vmware_portgroup module, but it is not always desirable to set it at the portgroup level - configuring teaming of the uplink NICs is particularly of more use at the vSwitch level.  It seems silly to be using ansible to configure vSwitches and portgroups on hosts consistently, but still have to then go and use other tools to turn on or off settings on those vSwitches.
<!--- Paste example playbooks or commands between quotes below -->
```yaml

```

<!--- HINT: You can also paste gist.github.com links for larger files -->



Discussion:
Files identified in the description:
None

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 --->
!component =vmware/plugins/modules/vmware_vswitch.py

##### ADDITIONAL INFORMATION
This feature will enable a group level adjustment for every port group in a vSwitch that defaults to the vSwitch policies.

Tumble down the vSphere Web Services API rabbit hole with me:
- https://code.vmware.com/apis/704/vsphere/vim.host.NetworkSystem.html#addVirtualSwitch
- https://code.vmware.com/apis/704/vsphere/vim.host.NetworkSystem.html#updateVirtualSwitch
- https://code.vmware.com/apis/704/vsphere/vim.host.VirtualSwitch.Specification.html
- https://code.vmware.com/apis/704/vsphere/vim.host.NetworkPolicy.html
- https://code.vmware.com/apis/704/vsphere/vim.host.NetworkPolicy.SecurityPolicy.html

```yaml
- name: Add a VMware vSwitch
  vmware_vswitch:
    hostname: "{{ esxi_hostname }}"
    esxi_hostname: "{{ esxi_hostname }}"
    username: "{{ esxi_username }}"
    password: "{{ esxi_password }}"
    switch: "vswitch_name"
    nics: "vmnic_name"
    mtu: 9000
    security:
        promiscuous_mode: False
        mac_changes: False
        forged_transmits: False
    traffic_shaping:
        enabled: True
        average_bandwidth: 100000
        peak_bandwidth: 100000
        burst_size: 102400
    teaming:
        load_balancing: failover_explicit
        network_failure_detection: link_status_only
        notify_switches: true
        failback: true
        active_adapters:
            - vmnic0
        standby_adapters:
            - vmnic1
  delegate_to: localhost
```

Current alternative is to run a shell script after creating the vSwitches... Something like:

```sh
#!/bin/sh

esxcli network vswitch standard policy security set -p true -v vswitch_name;
```
I am actually actively working on this for our own internal usage, so I'm interested in working on this issue. We're most interested in routing policy and security settings.

@wells, have you done anything with this code yet? I see you have a sample playbook above, but wasn't sure if there was any code to go with it yet.

I'm also considering adding check mode, but that itself might add lots of conditionals so we'll see.
Looks like this already got developed by @ckotte back in 2018, but never got merged:

https://github.com/ansible/ansible/pull/47015
Good find!

@ckotte, is it possible to get a new PR opened on to ansible-collections/community.vmware for this code?
I invested so much time in improving modules, but several of my PRs were not merged because some people in charge here just don't care. I also created a lot of new modules where I didn't even created PRs anymore. Anyway. I don't waste my time with this any longer. 
Files identified in the description:
* [`vmware/plugins/modules/vmware_vswitch.py`](https://github.com/['ansible-collections/community.vmware']/blob/main/vmware/plugins/modules/vmware_vswitch.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 --->
Files identified in the description:
* [`vmware/plugins/modules/vmware_vswitch.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/vmware/plugins/modules/vmware_vswitch.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 --->
@ckotte Would like to re-open https://github.com/ansible/ansible/pull/47015? Thanks.

needs_info
@Akasurde  I'm still waiting for your help with https://github.com/ansible-collections/community.vmware/pull/531. I won't open anything until this is merged..
Is there an ETA for this fix? Currently have a workaround but would like to replace it...
@sjwk This issue is waiting for your response. Please respond or the issue will be closed.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: needs_info_base --->
I don't believe anyone is waiting on me..
@sjwk Just for your information, there are several issues open for `vmware_vswitch`. I found three, and I've opened #1035 because I think it might be easier to address them all in one go instead of one by one.

Resolving patch:
diff --git a/plugins/modules/vmware_vswitch.py b/plugins/modules/vmware_vswitch.py
--- a/plugins/modules/vmware_vswitch.py
+++ b/plugins/modules/vmware_vswitch.py
@@ -61,6 +61,81 @@
     - Manage the vSwitch using this ESXi host system.
     aliases: [ 'host' ]
     type: str
+  security:
+    description:
+    - Network policy specifies layer 2 security settings for a
+      portgroup such as promiscuous mode, where guest adapter listens
+      to all the packets, MAC address changes and forged transmits.
+    - Dict which configures the different security values for portgroup.
+    version_added: '2.4.0'
+    suboptions:
+      promiscuous_mode:
+        type: bool
+        description: Indicates whether promiscuous mode is allowed.
+      forged_transmits:
+        type: bool
+        description: Indicates whether forged transmits are allowed.
+      mac_changes:
+        type: bool
+        description: Indicates whether mac changes are allowed.
+    required: False
+    aliases: [ 'security_policy', 'network_policy' ]
+    type: dict
+  teaming:
+    description:
+      - Dictionary which configures the different teaming values for portgroup.
+    version_added: '2.4.0'
+    suboptions:
+      load_balancing:
+        type: str
+        description:
+        - Network adapter teaming policy.
+        choices: [ loadbalance_ip, loadbalance_srcmac, loadbalance_srcid, failover_explicit, None ]
+        aliases: [ 'load_balance_policy' ]
+      network_failure_detection:
+        type: str
+        description: Network failure detection.
+        choices: [ link_status_only, beacon_probing ]
+      notify_switches:
+        type: bool
+        description: Indicate whether or not to notify the physical switch if a link fails.
+      failback:
+        type: bool
+        description: Indicate whether or not to use a failback when restoring links.
+      active_adapters:
+        type: list
+        description:
+        - List of active adapters used for load balancing.
+        - All vmnics are used as active adapters if C(active_adapters) and C(standby_adapters) are not defined.
+        elements: str
+      standby_adapters:
+        type: list
+        description:
+        - List of standby adapters used for failover.
+        - All vmnics are used as active adapters if C(active_adapters) and C(standby_adapters) are not defined.
+        elements: str
+    required: False
+    aliases: [ 'teaming_policy' ]
+    type: dict
+  traffic_shaping:
+    description:
+      - Dictionary which configures traffic shaping for the switch.
+    version_added: '2.4.0'
+    suboptions:
+      enabled:
+        type: bool
+        description: Status of Traffic Shaping Policy.
+      average_bandwidth:
+        type: int
+        description: Average bandwidth (kbit/s).
+      peak_bandwidth:
+        type: int
+        description: Peak bandwidth (kbit/s).
+      burst_size:
+        type: int
+        description: Burst size (KB).
+    required: False
+    type: dict
 extends_documentation_fragment:
 - community.vmware.vmware.documentation
 
@@ -109,6 +184,53 @@
     mtu: 9000
   delegate_to: localhost
 
+- name: Add a VMware vSwitch to a specific host system with Promiscuous Mode Enabled
+  community.vmware.vmware_vswitch:
+    hostname: '{{ esxi_hostname }}'
+    username: '{{ esxi_username }}'
+    password: '{{ esxi_password }}'
+    esxi_hostname: DC0_H0
+    switch_name: vswitch_001
+    nic_name: vmnic0
+    mtu: 9000
+    security:
+        promiscuous_mode: True
+  delegate_to: localhost
+
+- name: Add a VMware vSwitch to a specific host system with active/standby teaming
+  community.vmware.vmware_vswitch:
+    hostname: '{{ esxi_hostname }}'
+    username: '{{ esxi_username }}'
+    password: '{{ esxi_password }}'
+    esxi_hostname: DC0_H0
+    switch_name: vswitch_001
+    nic_name:
+      - vmnic0
+      - vmnic1
+    teaming:
+      active_adapters:
+        - vmnic0
+      standby_adapters:
+        - vmnic1
+  delegate_to: localhost
+
+- name: Add a VMware vSwitch to a specific host system with traffic shaping
+  community.vmware.vmware_vswitch:
+    hostname: '{{ esxi_hostname }}'
+    username: '{{ esxi_username }}'
+    password: '{{ esxi_password }}'
+    esxi_hostname: DC0_H0
+    switch_name: vswitch_001
+    nic_name:
+      - vmnic0
+      - vmnic1
+    traffic_shaping:
+        enabled: True
+        average_bandwidth: 100000
+        peak_bandwidth: 100000
+        burst_size: 102400
+  delegate_to: localhost
+
 - name: Delete a VMware vSwitch in a specific host system
   community.vmware.vmware_vswitch:
     hostname: '{{ esxi_hostname }}'
@@ -157,12 +279,15 @@ def __init__(self, module):
             self.module.fail_json(msg="Failed to get details of ESXi server."
                                       " Please specify esxi_hostname.")
 
+        self.network_mgr = self.host_system.configManager.networkSystem
+        if not self.network_mgr:
+            self.module.fail_json(msg="Failed to find network manager for ESXi system.")
+
         if self.params.get('state') == 'present':
             # Gather information about all vSwitches and Physical NICs
-            network_manager = self.host_system.configManager.networkSystem
-            available_pnic = [pnic.device for pnic in network_manager.networkInfo.pnic]
+            available_pnic = [pnic.device for pnic in self.network_mgr.networkInfo.pnic]
             self.available_vswitches = dict()
-            for available_vswitch in network_manager.networkInfo.vswitch:
+            for available_vswitch in self.network_mgr.networkInfo.vswitch:
                 used_pnic = []
                 for pnic in available_vswitch.pnic:
                     # vSwitch contains all PNICs as string in format of 'key-vim.host.PhysicalNic-vmnic0'
@@ -223,40 +348,62 @@ def state_create_vswitch(self):
         vss_spec.mtu = self.mtu
         if self.nics:
             vss_spec.bridge = vim.host.VirtualSwitch.BondBridge(nicDevice=self.nics)
-        try:
-            network_mgr = self.host_system.configManager.networkSystem
-            if network_mgr:
-                network_mgr.AddVirtualSwitch(vswitchName=self.switch,
-                                             spec=vss_spec)
-                results['changed'] = True
+
+        if self.module.check_mode:
+            results['msg'] = "vSwitch '%s' would be created" % self.switch
+        else:
+            try:
+                self.network_mgr.AddVirtualSwitch(vswitchName=self.switch,
+                                                  spec=vss_spec)
+
+                changed = False
+                spec = self.find_vswitch_by_name(self.host_system, self.switch).spec
+
+                # Check Security Policy
+                if self.update_security_policy(spec, results):
+                    changed = True
+
+                # Check Teaming Policy
+                if self.update_teaming_policy(spec, results):
+                    changed = True
+
+                # Check Traffic Shaping Policy
+                if self.update_traffic_shaping_policy(spec, results):
+                    changed = True
+
+                if changed:
+                    self.network_mgr.UpdateVirtualSwitch(vswitchName=self.switch,
+                                                         spec=spec)
+
                 results['result'] = "vSwitch '%s' is created successfully" % self.switch
-            else:
-                self.module.fail_json(msg="Failed to find network manager for ESXi system")
-        except vim.fault.AlreadyExists as already_exists:
-            results['result'] = "vSwitch with name %s already exists: %s" % (self.switch,
-                                                                             to_native(already_exists.msg))
-        except vim.fault.ResourceInUse as resource_used:
-            self.module.fail_json(msg="Failed to add vSwitch '%s' as physical network adapter"
-                                      " being bridged is already in use: %s" % (self.switch,
-                                                                                to_native(resource_used.msg)))
-        except vim.fault.HostConfigFault as host_config_fault:
-            self.module.fail_json(msg="Failed to add vSwitch '%s' due to host"
-                                      " configuration fault : %s" % (self.switch,
-                                                                     to_native(host_config_fault.msg)))
-        except vmodl.fault.InvalidArgument as invalid_argument:
-            self.module.fail_json(msg="Failed to add vSwitch '%s', this can be due to either of following :"
-                                      " 1. vSwitch Name exceeds the maximum allowed length,"
-                                      " 2. Number of ports specified falls out of valid range,"
-                                      " 3. Network policy is invalid,"
-                                      " 4. Beacon configuration is invalid : %s" % (self.switch,
-                                                                                    to_native(invalid_argument.msg)))
-        except vmodl.fault.SystemError as system_error:
-            self.module.fail_json(msg="Failed to add vSwitch '%s' due to : %s" % (self.switch,
-                                                                                  to_native(system_error.msg)))
-        except Exception as generic_exc:
-            self.module.fail_json(msg="Failed to add vSwitch '%s' due to"
-                                      " generic exception : %s" % (self.switch,
-                                                                   to_native(generic_exc)))
+            except vim.fault.AlreadyExists as already_exists:
+                results['result'] = "vSwitch with name %s already exists: %s" % (self.switch,
+                                                                                 to_native(already_exists.msg))
+            except vim.fault.ResourceInUse as resource_used:
+                self.module.fail_json(msg="Failed to add vSwitch '%s' as physical network adapter"
+                                          " being bridged is already in use: %s" % (self.switch,
+                                                                                    to_native(resource_used.msg)))
+            except vim.fault.HostConfigFault as host_config_fault:
+                self.module.fail_json(msg="Failed to add vSwitch '%s' due to host"
+                                          " configuration fault : %s" % (self.switch,
+                                                                         to_native(host_config_fault.msg)))
+            except vmodl.fault.InvalidArgument as invalid_argument:
+                self.module.fail_json(msg="Failed to add vSwitch '%s', this can be due to either of following :"
+                                          " 1. vSwitch Name exceeds the maximum allowed length,"
+                                          " 2. Number of ports specified falls out of valid range,"
+                                          " 3. Network policy is invalid,"
+                                          " 4. Beacon configuration is invalid : %s" % (self.switch,
+                                                                                        to_native(invalid_argument.msg)))
+            except vmodl.fault.SystemError as system_error:
+                self.module.fail_json(msg="Failed to add vSwitch '%s' due to : %s" % (self.switch,
+                                                                                      to_native(system_error.msg)))
+            except Exception as generic_exc:
+                self.module.fail_json(msg="Failed to add vSwitch '%s' due to"
+                                          " generic exception : %s" % (self.switch,
+                                                                       to_native(generic_exc)))
+
+        results['changed'] = True
+
         self.module.exit_json(**results)
 
     def state_exit_unchanged(self):
@@ -272,26 +419,30 @@ def state_destroy_vswitch(self):
         """
         results = dict(changed=False, result="")
 
-        try:
-            self.host_system.configManager.networkSystem.RemoveVirtualSwitch(self.vss.name)
-            results['changed'] = True
-            results['result'] = "vSwitch '%s' removed successfully." % self.vss.name
-        except vim.fault.NotFound as vswitch_not_found:
-            results['result'] = "vSwitch '%s' not available. %s" % (self.switch,
-                                                                    to_native(vswitch_not_found.msg))
-        except vim.fault.ResourceInUse as vswitch_in_use:
-            self.module.fail_json(msg="Failed to remove vSwitch '%s' as vSwitch"
-                                      " is used by several virtual"
-                                      " network adapters: %s" % (self.switch,
-                                                                 to_native(vswitch_in_use.msg)))
-        except vim.fault.HostConfigFault as host_config_fault:
-            self.module.fail_json(msg="Failed to remove vSwitch '%s' due to host"
-                                      " configuration fault : %s" % (self.switch,
-                                                                     to_native(host_config_fault.msg)))
-        except Exception as generic_exc:
-            self.module.fail_json(msg="Failed to remove vSwitch '%s' due to generic"
-                                      " exception : %s" % (self.switch,
-                                                           to_native(generic_exc)))
+        if self.module.check_mode:
+            results['msg'] = "vSwitch '%s' would be removed" % self.vss.name
+        else:
+            try:
+                self.host_system.configManager.networkSystem.RemoveVirtualSwitch(self.vss.name)
+                results['result'] = "vSwitch '%s' removed successfully." % self.vss.name
+            except vim.fault.NotFound as vswitch_not_found:
+                results['result'] = "vSwitch '%s' not available. %s" % (self.switch,
+                                                                        to_native(vswitch_not_found.msg))
+            except vim.fault.ResourceInUse as vswitch_in_use:
+                self.module.fail_json(msg="Failed to remove vSwitch '%s' as vSwitch"
+                                          " is used by several virtual"
+                                          " network adapters: %s" % (self.switch,
+                                                                     to_native(vswitch_in_use.msg)))
+            except vim.fault.HostConfigFault as host_config_fault:
+                self.module.fail_json(msg="Failed to remove vSwitch '%s' due to host"
+                                          " configuration fault : %s" % (self.switch,
+                                                                         to_native(host_config_fault.msg)))
+            except Exception as generic_exc:
+                self.module.fail_json(msg="Failed to remove vSwitch '%s' due to generic"
+                                          " exception : %s" % (self.switch,
+                                                               to_native(generic_exc)))
+
+        results['changed'] = True
 
         self.module.exit_json(**results)
 
@@ -300,78 +451,95 @@ def state_update_vswitch(self):
         Update vSwitch
 
         """
+        changed = False
         results = dict(changed=False, result="No change in vSwitch '%s'" % self.switch)
-        vswitch_pnic_info = self.available_vswitches[self.switch]
-        pnic_add = []
-        for desired_pnic in self.nics:
-            if desired_pnic not in vswitch_pnic_info['pnic']:
-                pnic_add.append(desired_pnic)
-        pnic_remove = []
-        for configured_pnic in vswitch_pnic_info['pnic']:
-            if configured_pnic not in self.nics:
-                pnic_remove.append(configured_pnic)
-        diff = False
-        # Update all nics
-        all_nics = vswitch_pnic_info['pnic']
-        if pnic_add or pnic_remove:
-            diff = True
-            if pnic_add:
-                all_nics += pnic_add
-            if pnic_remove:
-                for pnic in pnic_remove:
-                    all_nics.remove(pnic)
-
-        if vswitch_pnic_info['mtu'] != self.mtu or \
-                vswitch_pnic_info['num_ports'] != self.number_of_ports:
-            diff = True
-
-        try:
-            if diff:
-                vss_spec = vim.host.VirtualSwitch.Specification()
-                if all_nics:
-                    vss_spec.bridge = vim.host.VirtualSwitch.BondBridge(nicDevice=all_nics)
-                vss_spec.numPorts = self.number_of_ports
-                vss_spec.mtu = self.mtu
-
-                network_mgr = self.host_system.configManager.networkSystem
-                if network_mgr:
-                    network_mgr.UpdateVirtualSwitch(vswitchName=self.switch,
-                                                    spec=vss_spec)
-                    results['changed'] = True
+        spec = self.vss.spec
+
+        # Check MTU
+        if self.vss.mtu != self.mtu:
+            spec.mtu = self.mtu
+            changed = True
+
+        # Check Number of Ports
+        if spec.numPorts != self.number_of_ports:
+            spec.numPorts = self.number_of_ports
+            changed = True
+
+        # Check nics
+        nics_current = set(map(lambda n: n.rsplit('-', 1)[1], self.vss.pnic))
+        if nics_current != set(self.nics):
+            if self.nics:
+                spec.bridge = vim.host.VirtualSwitch.BondBridge(nicDevice=self.nics)
+            else:
+                spec.bridge = None
+            changed = True
+
+            # Update teaming if not configured specifigaly
+            if not self.params['teaming']:
+                nicOrder = spec.policy.nicTeaming.nicOrder
+                # Remove missing nics from policy
+                if nicOrder.activeNic != [i for i in nicOrder.activeNic if i in self.nics]:
+                    nicOrder.activeNic = [i for i in nicOrder.activeNic if i in self.nics]
+                if nicOrder.standbyNic != [i for i in nicOrder.standbyNic if i in self.nics]:
+                    nicOrder.standbyNic = [i for i in nicOrder.standbyNic if i in self.nics]
+                # Set new nics as active
+                if set(self.nics) - nics_current:
+                    nicOrder.activeNic += set(self.nics) - nics_current
+
+        # Check Security Policy
+        if self.update_security_policy(spec, results):
+            changed = True
+
+        # Check Teaming Policy
+        if self.update_teaming_policy(spec, results):
+            changed = True
+
+        # Check Traffic Shaping Policy
+        if self.update_traffic_shaping_policy(spec, results):
+            changed = True
+
+        if changed:
+            if self.module.check_mode:
+                results['msg'] = "vSwitch '%s' would be updated" % self.switch
+            else:
+                try:
+                    self.network_mgr.UpdateVirtualSwitch(vswitchName=self.switch,
+                                                         spec=spec)
                     results['result'] = "vSwitch '%s' is updated successfully" % self.switch
-                else:
-                    self.module.fail_json(msg="Failed to find network manager for ESXi system.")
-        except vim.fault.ResourceInUse as resource_used:
-            self.module.fail_json(msg="Failed to update vSwitch '%s' as physical network adapter"
-                                      " being bridged is already in use: %s" % (self.switch,
-                                                                                to_native(resource_used.msg)))
-        except vim.fault.NotFound as not_found:
-            self.module.fail_json(msg="Failed to update vSwitch with name '%s'"
-                                      " as it does not exists: %s" % (self.switch,
-                                                                      to_native(not_found.msg)))
-
-        except vim.fault.HostConfigFault as host_config_fault:
-            self.module.fail_json(msg="Failed to update vSwitch '%s' due to host"
-                                      " configuration fault : %s" % (self.switch,
-                                                                     to_native(host_config_fault.msg)))
-        except vmodl.fault.InvalidArgument as invalid_argument:
-            self.module.fail_json(msg="Failed to update vSwitch '%s', this can be due to either of following :"
-                                      " 1. vSwitch Name exceeds the maximum allowed length,"
-                                      " 2. Number of ports specified falls out of valid range,"
-                                      " 3. Network policy is invalid,"
-                                      " 4. Beacon configuration is invalid : %s" % (self.switch,
-                                                                                    to_native(invalid_argument.msg)))
-        except vmodl.fault.SystemError as system_error:
-            self.module.fail_json(msg="Failed to update vSwitch '%s' due to : %s" % (self.switch,
-                                                                                     to_native(system_error.msg)))
-        except vmodl.fault.NotSupported as not_supported:
-            self.module.fail_json(msg="Failed to update vSwitch '%s' as network adapter teaming policy"
-                                      " is set but is not supported : %s" % (self.switch,
-                                                                             to_native(not_supported.msg)))
-        except Exception as generic_exc:
-            self.module.fail_json(msg="Failed to update vSwitch '%s' due to"
-                                      " generic exception : %s" % (self.switch,
-                                                                   to_native(generic_exc)))
+                except vim.fault.ResourceInUse as resource_used:
+                    self.module.fail_json(msg="Failed to update vSwitch '%s' as physical network adapter"
+                                              " being bridged is already in use: %s" % (self.switch,
+                                                                                        to_native(resource_used.msg)))
+                except vim.fault.NotFound as not_found:
+                    self.module.fail_json(msg="Failed to update vSwitch with name '%s'"
+                                              " as it does not exists: %s" % (self.switch,
+                                                                              to_native(not_found.msg)))
+
+                except vim.fault.HostConfigFault as host_config_fault:
+                    self.module.fail_json(msg="Failed to update vSwitch '%s' due to host"
+                                              " configuration fault : %s" % (self.switch,
+                                                                             to_native(host_config_fault.msg)))
+                except vmodl.fault.InvalidArgument as invalid_argument:
+                    self.module.fail_json(msg="Failed to update vSwitch '%s', this can be due to either of following :"
+                                              " 1. vSwitch Name exceeds the maximum allowed length,"
+                                              " 2. Number of ports specified falls out of valid range,"
+                                              " 3. Network policy is invalid,"
+                                              " 4. Beacon configuration is invalid : %s" % (self.switch,
+                                                                                            to_native(invalid_argument.msg)))
+                except vmodl.fault.SystemError as system_error:
+                    self.module.fail_json(msg="Failed to update vSwitch '%s' due to : %s" % (self.switch,
+                                                                                             to_native(system_error.msg)))
+                except vmodl.fault.NotSupported as not_supported:
+                    self.module.fail_json(msg="Failed to update vSwitch '%s' as network adapter teaming policy"
+                                              " is set but is not supported : %s" % (self.switch,
+                                                                                     to_native(not_supported.msg)))
+                except Exception as generic_exc:
+                    self.module.fail_json(msg="Failed to update vSwitch '%s' due to"
+                                              " generic exception : %s" % (self.switch,
+                                                                           to_native(generic_exc)))
+
+            results['changed'] = True
+
         self.module.exit_json(**results)
 
     def check_vswitch_configuration(self):
@@ -402,6 +570,176 @@ def find_vswitch_by_name(host, vswitch_name):
                 return vss
         return None
 
+    def update_security_policy(self, spec, results):
+        """
+        Update the security policy according to the parameters
+        Args:
+            spec: The vSwitch spec
+            results: The results dict
+
+        Returns: True if changes have been made, else false
+        """
+        if not self.params['security'] or not spec.policy.security:
+            return False
+
+        security_policy = spec.policy.security
+        changed = False
+        sec_promiscuous_mode = self.params['security'].get('promiscuous_mode')
+        sec_forged_transmits = self.params['security'].get('forged_transmits')
+        sec_mac_changes = self.params['security'].get('mac_changes')
+
+        if sec_promiscuous_mode is not None:
+            results['sec_promiscuous_mode'] = sec_promiscuous_mode
+            if security_policy.allowPromiscuous is not sec_promiscuous_mode:
+                results['sec_promiscuous_mode_previous'] = security_policy.allowPromiscuous
+                security_policy.allowPromiscuous = sec_promiscuous_mode
+                changed = True
+
+        if sec_mac_changes is not None:
+            results['sec_mac_changes'] = sec_mac_changes
+            if security_policy.macChanges is not sec_mac_changes:
+                results['sec_mac_changes_previous'] = security_policy.macChanges
+                security_policy.macChanges = sec_mac_changes
+                changed = True
+
+        if sec_forged_transmits is not None:
+            results['sec_forged_transmits'] = sec_forged_transmits
+            if security_policy.forgedTransmits is not sec_forged_transmits:
+                results['sec_forged_transmits_previous'] = security_policy.forgedTransmits
+                security_policy.forgedTransmits = sec_forged_transmits
+                changed = True
+
+        return changed
+
+    def update_teaming_policy(self, spec, results):
+        """
+        Update the teaming policy according to the parameters
+        Args:
+            spec: The vSwitch spec
+            results: The results dict
+
+        Returns: True if changes have been made, else false
+        """
+        if not self.params['teaming'] or not spec.policy.nicTeaming:
+            return False
+
+        teaming_policy = spec.policy.nicTeaming
+        changed = False
+        teaming_load_balancing = self.params['teaming'].get('load_balancing')
+        teaming_failure_detection = self.params['teaming'].get('network_failure_detection')
+        teaming_notify_switches = self.params['teaming'].get('notify_switches')
+        teaming_failback = self.params['teaming'].get('failback')
+        teaming_failover_order_active = self.params['teaming'].get('active_adapters')
+        teaming_failover_order_standby = self.params['teaming'].get('standby_adapters')
+
+        # Check teaming policy
+        if teaming_load_balancing is not None:
+            results['load_balancing'] = teaming_load_balancing
+            if teaming_policy.policy != teaming_load_balancing:
+                results['load_balancing_previous'] = teaming_policy.policy
+                teaming_policy.policy = teaming_load_balancing
+                changed = True
+
+        # Check teaming notify switches
+        if teaming_notify_switches is not None:
+            results['notify_switches'] = teaming_notify_switches
+            if teaming_policy.notifySwitches is not teaming_notify_switches:
+                results['notify_switches_previous'] = teaming_policy.notifySwitches
+                teaming_policy.notifySwitches = teaming_notify_switches
+                changed = True
+
+        # Check failback
+        if teaming_failback is not None:
+            results['failback'] = teaming_failback
+            if teaming_policy.rollingOrder is not teaming_failback:
+                results['notify_switches_previous'] = teaming_policy.rollingOrder
+                teaming_policy.rollingOrder = teaming_failback
+                changed = True
+
+        # Check teaming failover order
+        if teaming_failover_order_active is not None:
+            results['failover_active'] = teaming_failover_order_active
+            if teaming_policy.nicOrder.activeNic != teaming_failover_order_active:
+                results['failover_active_previous'] = teaming_policy.nicOrder.activeNic
+                teaming_policy.nicOrder.activeNic = teaming_failover_order_active
+                changed = True
+        if teaming_failover_order_standby is not None:
+            results['failover_standby'] = teaming_failover_order_standby
+            if teaming_policy.nicOrder.standbyNic != teaming_failover_order_standby:
+                results['failover_standby_previous'] = teaming_policy.nicOrder.standbyNic
+                teaming_policy.nicOrder.standbyNic = teaming_failover_order_standby
+                changed = True
+
+        # Check teaming failure detection
+        if teaming_failure_detection is not None:
+            results['failure_detection'] = teaming_failure_detection
+            if teaming_failure_detection == "link_status_only":
+                if teaming_policy.failureCriteria.checkBeacon is True:
+                    results['failure_detection_previous'] = "beacon_probing"
+                    teaming_policy.failureCriteria.checkBeacon = False
+                    changed = True
+            elif teaming_failure_detection == "beacon_probing":
+                if teaming_policy.failureCriteria.checkBeacon is False:
+                    results['failure_detection_previous'] = "link_status_only"
+                    teaming_policy.failureCriteria.checkBeacon = True
+                    changed = True
+
+        return changed
+
+    def update_traffic_shaping_policy(self, spec, results):
+        """
+        Update the traffic shaping policy according to the parameters
+        Args:
+            spec: The vSwitch spec
+            results: The results dict
+
+        Returns: True if changes have been made, else false
+        """
+        if not self.params['traffic_shaping'] or not spec.policy.nicTeaming:
+            return False
+
+        ts_policy = spec.policy.shapingPolicy
+        changed = False
+        ts_enabled = self.params['traffic_shaping'].get('enabled')
+
+        # Check if traffic shaping needs to be disabled
+        if not ts_enabled:
+            if ts_policy.enabled:
+                ts_policy.enabled = False
+                changed = True
+            return changed
+
+        for value in ['average_bandwidth', 'peak_bandwidth', 'burst_size']:
+            if not self.params['traffic_shaping'].get(value):
+                self.module.fail_json(msg="traffic_shaping.%s is a required parameter if traffic_shaping is enabled." % value)
+        ts_average_bandwidth = self.params['traffic_shaping'].get('average_bandwidth') * 1000
+        ts_peak_bandwidth = self.params['traffic_shaping'].get('peak_bandwidth') * 1000
+        ts_burst_size = self.params['traffic_shaping'].get('burst_size') * 1024
+
+        if not ts_policy.enabled:
+            ts_policy.enabled = True
+            changed = True
+
+        if ts_policy.averageBandwidth != ts_average_bandwidth:
+            results['traffic_shaping_avg_bandw'] = ts_average_bandwidth
+            results['traffic_shaping_avg_bandw_previous'] = ts_policy.averageBandwidth
+            ts_policy.averageBandwidth = ts_average_bandwidth
+            changed = True
+
+        if ts_policy.peakBandwidth != ts_peak_bandwidth:
+            results['traffic_shaping_peak_bandw'] = ts_peak_bandwidth
+            results['traffic_shaping_peak_bandw_previous'] = ts_policy.peakBandwidth
+            ts_policy.peakBandwidth = ts_peak_bandwidth
+            changed = True
+
+        if ts_policy.burstSize != ts_burst_size:
+            results['traffic_shaping_burst'] = ts_burst_size
+            results['traffic_shaping_burst_previous'] = ts_policy.burstSize
+            ts_policy.burstSize = ts_burst_size
+            changed = True
+
+        return changed
+
 
 def main():
     argument_spec = vmware_argument_spec()
@@ -410,12 +748,55 @@ def main():
         nics=dict(type='list', aliases=['nic_name'], default=[], elements='str'),
         number_of_ports=dict(type='int', default=128),
         mtu=dict(type='int', default=1500),
-        state=dict(type='str', default='present', choices=['absent', 'present'])),
+        state=dict(type='str', default='present', choices=['absent', 'present']),
         esxi_hostname=dict(type='str', aliases=['host']),
-    )
+        security=dict(
+            type='dict',
+            options=dict(
+                promiscuous_mode=dict(type='bool'),
+                forged_transmits=dict(type='bool'),
+                mac_changes=dict(type='bool'),
+            ),
+            aliases=['security_policy', 'network_policy']
+        ),
+        teaming=dict(
+            type='dict',
+            options=dict(
+                load_balancing=dict(
+                    type='str',
+                    choices=[
+                        None,
+                        'loadbalance_ip',
+                        'loadbalance_srcmac',
+                        'loadbalance_srcid',
+                        'failover_explicit',
+                    ],
+                    aliases=['load_balance_policy'],
+                ),
+                network_failure_detection=dict(
+                    type='str',
+                    choices=['link_status_only', 'beacon_probing']
+                ),
+                notify_switches=dict(type='bool'),
+                failback=dict(type='bool'),
+                active_adapters=dict(type='list', elements='str'),
+                standby_adapters=dict(type='list', elements='str'),
+            ),
+            aliases=['teaming_policy']
+        ),
+        traffic_shaping=dict(
+            type='dict',
+            options=dict(
+                enabled=dict(type='bool'),
+                average_bandwidth=dict(type='int'),
+                peak_bandwidth=dict(type='int'),
+                burst_size=dict(type='int'),
+            ),
+        ),
+    ))
 
     module = AnsibleModule(argument_spec=argument_spec,
-                           supports_check_mode=False)
+                           supports_check_mode=True)
 
     host_virtual_switch = VMwareHostVirtualSwitch(module)
     host_virtual_switch.process_state()


Test patch:
diff --git a/tests/integration/targets/vmware_vswitch/tasks/main.yml b/tests/integration/targets/vmware_vswitch/tasks/main.yml
--- a/tests/integration/targets/vmware_vswitch/tasks/main.yml
+++ b/tests/integration/targets/vmware_vswitch/tasks/main.yml
@@ -109,6 +109,115 @@
   - assert:
       that:
       - add_vswitch_with_host_system is changed
+
+  - name: Add a vSwitch with a network policy
+    vmware_vswitch:
+      hostname: '{{ esxi1 }}'
+      username: '{{ esxi_user }}'
+      password: '{{ esxi_password }}'
+      validate_certs: false
+      switch: vmswitch_0001
+      nics:
+        - vmnic1
+        - vmnic2
+      state: present
+      security:
+        forged_transmits: true
+        mac_changes: true
+      traffic_shaping:
+        enabled: true
+        average_bandwidth: 100000
+        peak_bandwidth: 100000
+        burst_size: 102400
+      teaming:
+        active_adapters: vmnic1
+        standby_adapters: vmnic2
+    register: add_vswitch_netpol_run
+  - debug: var=add_vswitch_netpol_run
+  - assert:
+      that:
+      - add_vswitch_netpol_run.changed == true
+
+  - name: Add a vSwitch with a network policy again (idempotency check)
+    vmware_vswitch:
+      hostname: '{{ esxi1 }}'
+      username: '{{ esxi_user }}'
+      password: '{{ esxi_password }}'
+      validate_certs: false
+      switch: vmswitch_0001
+      nics:
+        - vmnic1
+        - vmnic2
+      state: present
+      security:
+        forged_transmits: true
+        mac_changes: true
+      traffic_shaping:
+        enabled: true
+        average_bandwidth: 100000
+        peak_bandwidth: 100000
+        burst_size: 102400
+      teaming:
+        active_adapters: vmnic1
+        standby_adapters: vmnic2
+    register: add_vswitch_netpol_again_run
+  - assert:
+      that:
+      - add_vswitch_netpol_again_run.changed == false
+
+  - name: Update a vSwitch network policy
+    vmware_vswitch:
+      hostname: '{{ esxi1 }}'
+      username: '{{ esxi_user }}'
+      password: '{{ esxi_password }}'
+      validate_certs: false
+      switch: vmswitch_0001
+      nics:
+        - vmnic1
+        - vmnic2
+      state: present
+      security:
+        forged_transmits: false
+        mac_changes: true
+      traffic_shaping:
+        enabled: false
+      teaming:
+        active_adapters:
+          - vmnic1
+          - vmnic2
+        standby_adapters: []
+    register: update_vswitch_netpol_run
+  - debug: var=update_vswitch_netpol_run
+  - assert:
+      that:
+      - update_vswitch_netpol_run.changed == true
+
+  - name: Update a vSwitch network policy again (idempotency check)
+    vmware_vswitch:
+      hostname: '{{ esxi1 }}'
+      username: '{{ esxi_user }}'
+      password: '{{ esxi_password }}'
+      validate_certs: false
+      switch: vmswitch_0001
+      nics:
+        - vmnic1
+        - vmnic2
+      state: present
+      security:
+        forged_transmits: false
+        mac_changes: true
+      traffic_shaping:
+        enabled: false
+      teaming:
+        active_adapters:
+          - vmnic1
+          - vmnic2
+        standby_adapters: []
+    register: update_vswitch_netpol_again_run
+  - assert:
+      that:
+      - update_vswitch_netpol_again_run.changed == false
+
   always:
   - include_tasks: teardown.yml