#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# Copyright: (c) 2018, CrySyS Lab <www.crysys.hu>
# Copyright: (c) 2018, Peter Gyorgy <gyorgy.peter@edu.bme.hu>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

ANSIBLE_METADATA = {
    'metadata_version': '1.1',
    'status': ['preview'],
    'supported_by': 'community'
}

DOCUMENTATION = '''
---
module: vmware_vspan_session
short_description: Create or remove a Port Mirroring session.
description:
   - This module can be used to create, delete or edit different kind of port mirroring sessions.
version_added: '2.8'
author:
- Peter Gyorgy (@gyorgypeter) <gyorgy.peter1996@gmail.com>
notes:
    - Tested on vSphere 6.7
requirements:
    - "python > =  2.6"
    - PyVmomi
options:
    switch:
        description:
            - The name of the distributed vSwitch on which to add or remove the mirroring session.
        required: True
        aliases: [ 'switch_name' ]
    name:
        description:
            - Name of the session.
        required: True
    state:
        choices:
            - 'present'
            - 'absent'
        description:
            - Create or remove the session.
        required: True
    session_type:
        default: 'dvPortMirror'
        choices:
            - 'encapsulatedRemoteMirrorSource'
            - 'remoteMirrorDest'
            - 'remoteMirrorSource'
            - 'dvPortMirror'
        description:
            - Select the mirroring type.
            - '- C(encapsulatedRemoteMirrorSource) (str): In encapsulatedRemoteMirrorSource session, Distributed Ports
            can be used as source entities, and Ip address can be used as destination entities.'
            - '- C(remoteMirrorDest) (str): In remoteMirrorDest session, vlan Ids can be used as source entities, and
            Distributed Ports can be used as destination entities.'
            - '- C(remoteMirrorSource) (str): In remoteMirrorSource session, Distributed Ports can be used as source
            entities, and uplink ports name can be used as destination entities.'
            - '- C(dvPortMirror) (str): In dvPortMirror session, Distributed Ports can be used as both source and
            destination entities.'
        required: False
    enabled:
        type: bool
        default: True
        description:
            - Whether the session is enabled.
    description:
        description:
            - The description for the session.
        required: False
    source_port_transmitted:
        description:
            - Source port for which transmitted packets are mirrored.
        required: False
    source_port_received:
        description:
            - Source port for which received packets are mirrored.
        required: False
    destination_port:
        description:
            - Destination port that received the mirrored packets. Also any port designated in the value of this
             property can not match the source port in any of the Distributed Port Mirroring session.
        required: False
    encapsulation_vlan_id:
        description:
            - VLAN ID used to encapsulate the mirrored traffic.
        required: False
    strip_original_vlan:
        description:
            - Whether to strip the original VLAN tag. if false, the original VLAN tag will be preserved on the mirrored
             traffic. If encapsulationVlanId has been set and this property is false, the frames will be double tagged
             with the original VLAN ID as the inner tag.
        type: bool
        required: False
    mirrored_packet_length:
        description:
            - An integer that describes how much of each frame to mirror. If unset, all of the frame would be mirrored.
             Setting this property to a smaller value is useful when the consumer will look only at the headers.
             The value cannot be less than 60.
        required: False
    normal_traffic_allowed:
        description:
            - Whether or not destination ports can send and receive "normal" traffic. Setting this to false will make
             mirror ports be used solely for mirroring and not double as normal access ports.
        type: bool
        required: False
    sampling_rate:
        description:
            - Sampling rate of the session. If its value is n, one of every n packets is mirrored.
             Valid values are between 1 to 65535, and default value is 1.
        type: int
        required: False
    source_vm_transmitted:
        description:
            - With this parameter it is possible, to add a NIC of a VM to a port mirroring session.
            - 'Valid attributes are:'
            - '- C(name) (str): Name of the VM'
            - '- C(nic_label) (bool): Label of the Network Interface Card to use.'
    source_vm_received:
        description:
            - With this parameter it is possible, to add a NIC of a VM to a port mirroring session.
            - 'Valid attributes are:'
            - '- C(name) (str): Name of the VM'
            - '- C(nic_label) (bool): Label of the Network Interface Card to use.'
    destination_vm:
        description:
            - With this parameter it is possible, to add a NIC of a VM to a port mirroring session.
            - 'Valid attributes are:'
            - '- C(name) (str): Name of the VM'
            - '- C(nic_label) (bool): Label of the Network Interface Card to use.'
        required: False
extends_documentation_fragment: vmware.documentation
'''

EXAMPLES = '''
- name: Create distributed mirroring session.
  vmware_vspan_session:
    hostname: '{{ vcenter_hostname }}'
    username: '{{ vcenter_username }}'
    password: '{{ vcenter_password }}'
    switch_name: dvSwitch
    state: present
    name: Basic Session
    enabled: True
    description: "Example description"
    source_port_transmitted: 817
    source_port_received: 817
    destination_port: 815
  delegate_to: localhost

- name: Create remote destination mirroring session.
  vmware_vspan_session:
    hostname: '{{ vcenter_hostname }}'
    username: '{{ vcenter_username }}'
    password: '{{ vcenter_password }}'
    switch_name: dvSwitch
    state: present
    name: Remote Session
    enabled: True
    description: "Example description"
    source_port_received: 105
    destination_port: 815
    session_type: "remoteMirrorDest"
  delegate_to: localhost

- name: Create remote destination mirroring session.
  vmware_vspan_session:
    hostname: '{{ vcenter_hostname }}'
    username: '{{ vcenter_username }}'
    password: '{{ vcenter_password }}'
    switch_name: dvSwitch
    state: absent
    name: Remote Session
  delegate_to: localhost
'''

RETURN = """#
"""

try:
    from pyVmomi import vim, vmodl
except ImportError as e:
    pass

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.vmware import (vmware_argument_spec, PyVmomi, find_dvs_by_name,
                                         find_vm_by_name, wait_for_task)


class VMwareVspanSession(PyVmomi):
    def __init__(self, module):
        super(VMwareVspanSession, self).__init__(module)
        self.switch = module.params['switch']
        self.name = module.params['name']
        self.session_type = module.params['session_type']
        self.enabled = module.params['enabled']
        self.state = module.params['state']
        self.description = module.params['description']
        self.source_port_transmitted = module.params['source_port_transmitted']
        self.source_port_received = module.params['source_port_received']
        self.destination_port = module.params['destination_port']
        self.encapsulation_vlan_id = module.params['encapsulation_vlan_id']
        self.strip_original_vlan = module.params['strip_original_vlan']
        self.mirrored_packet_length = module.params['mirrored_packet_length']
        self.normal_traffic_allowed = module.params['normal_traffic_allowed']
        self.sampling_rate = module.params['sampling_rate']
        self.dv_switch = find_dvs_by_name(self.content, self.switch)
        if self.dv_switch is None:
            self.module.fail_json(msg="There is no dvSwitch with the name: {0:s}.".format(self.switch))
        self.operation = None
        self.modified_ports = dict()
        self.deleted_session = None
        if module.params['source_vm_transmitted'] is not None:
            if (module.params['source_vm_transmitted']['name'] is None or
                    module.params['source_vm_transmitted']['nic_label'] is None):
                self.module.fail_json(msg="Please provide both VM name and NIC Label")
            self.source_vm_transmitted_name = module.params['source_vm_transmitted']['name']
            self.source_vm_transmitted_nic_label = module.params['source_vm_transmitted']['nic_label']
        if module.params['source_vm_received'] is not None:
            if (module.params['source_vm_received']['name'] is None or
                    module.params['source_vm_received']['nic_label'] is None):
                self.module.fail_json(msg="Please provide both VM name and NIC Label")
            self.source_vm_received_name = module.params['source_vm_received']['name']
            self.source_vm_received_nic_label = module.params['source_vm_received']['nic_label']
        if module.params['destination_vm'] is not None:
            if (module.params['destination_vm']['name'] is None or
                    module.params['destination_vm']['nic_label'] is None):
                self.module.fail_json(msg="Please provide both VM name and NIC Label")
            self.destination_vm_name = module.params['destination_vm']['name']
            self.destination_vm_nic_label = module.params['destination_vm']['nic_label']

    def set_operation(self):
        """Sets the operation according to state"""
        if self.state == 'absent':
            self.operation = 'remove'
        elif self.state == 'present' and self.find_session_by_name() is None:
            self.operation = 'add'
        else:
            self.operation = 'edit'

    def find_session_by_name(self):
        """Finds a session by name
        Returns
        -------
        vim.dvs.VmwareDistributedVirtualSwitch.VspanSession
            The session if there was a session by the given name, else returns None
        """
        for vspan_session in self.dv_switch.config.vspanSession:
            if vspan_session.name == self.name:
                return vspan_session
        return None

    def get_vm_port(self, vm_name, nic_label):
        """Finds the port of the VM
        Returns
        -------
        str
            the port number as a string, or None if the NIC couldnt be found
        """
        vm = find_vm_by_name(self.content, vm_name)
        if vm is None:
            self.module.fail_json(msg="There is no VM with the name: {0:s}.".format(vm_name))
        for hardware in vm.config.hardware.device:
            if isinstance(hardware, vim.vm.device.VirtualVmxnet3):
                if hardware.deviceInfo.label == nic_label:
                    return hardware.backing.port.portKey
        return None

    def set_port_for_vm(self):
        """Sets the ports, to the VM's specified port."""
        if hasattr(self, 'source_vm_transmitted_name') and hasattr(self, 'source_vm_transmitted_nic_label'):
            port = self.get_vm_port(self.source_vm_transmitted_name, self.source_vm_transmitted_nic_label)
            if port is not None:
                self.source_port_transmitted = port
            else:
                self.module.fail_json(
                    msg="No port could be found for VM: {0:s} NIC: {1:s}".format(self.source_vm_transmitted_name,
                                                                                 self.source_vm_transmitted_nic_label))
        if hasattr(self, 'source_vm_received_name') and hasattr(self, 'source_vm_received_nic_label'):
            port = self.get_vm_port(self.source_vm_received_name, self.source_vm_received_nic_label)
            if port is not None:
                self.source_port_received = port
            else:
                self.module.fail_json(
                    msg="No port could be found for VM: {0:s} NIC: {1:s}".format(self.source_vm_received_name,
                                                                                 self.source_vm_received_nic_label))
        if hasattr(self, 'destination_vm_name') and hasattr(self, 'destination_vm_nic_label'):
            port = self.get_vm_port(self.destination_vm_name, self.destination_vm_nic_label)
            if port is not None:
                self.destination_port = port
            else:
                self.module.fail_json(
                    msg="No port could be found for VM: {0:s} NIC: {1:s}".format(self.destination_vm_name,
                                                                                 self.destination_vm_nic_label))

    def process_operation(self):
        """Calls the create or delete function based on the operation"""
        self.set_operation()
        if self.operation == 'remove':
            results = self.remove_vspan_session()
            self.module.exit_json(**results)
        if self.operation == 'add':
            self.set_port_for_vm()
            results = self.add_vspan_session()
            self.module.exit_json(**results)
        if self.operation == 'edit':
            self.remove_vspan_session()
            self.set_port_for_vm()
            results = self.add_vspan_session()
            self.module.exit_json(**results)

    def set_port_security_promiscuous(self, ports, state):
        """Set the given port to the given promiscuous state.
        Parameters
        ----------
        port : str[]
            PortKey
        state: bool
            State of the promiscuous mode, if true its allowed, else not.
        """
        # Creating the new port policy
        port_spec = []
        vim_bool = vim.BoolPolicy(value=state)
        port_policy = vim.dvs.VmwareDistributedVirtualSwitch.SecurityPolicy(allowPromiscuous=vim_bool)
        port_settings = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy(securityPolicy=port_policy)
        for port in ports:
            temp_port_spec = vim.dvs.DistributedVirtualPort.ConfigSpec(
                operation="edit",
                key=port,
                setting=port_settings
            )
            port_spec.append(temp_port_spec)

        task = self.dv_switch.ReconfigureDVPort_Task(port_spec)
        try:
            wait_for_task(task)
        except Exception:
            self.restore_original_state()
            self.module.fail_json(msg=task.info.error.msg)

    def turn_off_promiscuous(self):
        """Disable all promiscuous mode ports, and give them back in a list.
        Returns
        -------
        list
            Contains every port, where promiscuous mode has been turned off
        """
        # Ports that are in mirror sessions
        ports = []
        ports_of_selected_session = []
        for vspan_session in self.dv_switch.config.vspanSession:
            if vspan_session.sourcePortReceived is not None:
                session_ports = vspan_session.sourcePortReceived.portKey
                for port in session_ports:
                    if vspan_session.name == self.name:
                        ports_of_selected_session.append(port)
                    elif not(port in ports):
                        ports.append(port)
            if vspan_session.sourcePortTransmitted is not None:
                session_ports = vspan_session.sourcePortTransmitted.portKey
                for port in session_ports:
                    if vspan_session.name == self.name:
                        ports_of_selected_session.append(port)
                    elif not(port in ports):
                        ports.append(port)
            if vspan_session.destinationPort is not None:
                session_ports = vspan_session.destinationPort.portKey
                for port in session_ports:
                    if vspan_session.name == self.name:
                        ports_of_selected_session.append(port)
                    elif not(port in ports):
                        ports.append(port)
        promiscuous_ports = []
        if ports:
            dv_ports = self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=ports))
            # If a port is promiscuous set disable it, and add it to the array to enable it after the changes are made.
            for dv_port in dv_ports:
                if dv_port.config.setting.securityPolicy.allowPromiscuous.value:
                    self.set_port_security_promiscuous([dv_port.key], False)
                    self.modified_ports.update({dv_port.key: True})
                    promiscuous_ports.append(dv_port.key)
        if ports_of_selected_session:
            current_dv_ports = self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=ports_of_selected_session))
            for dv_port in current_dv_ports:
                if dv_port.config.setting.securityPolicy.allowPromiscuous.value:
                    self.set_port_security_promiscuous([dv_port.key], False)
                    self.modified_ports.update({dv_port.key: True})
        # Return the promiscuous ports array, to set them back after the config is finished.
        return promiscuous_ports

    def delete_mirroring_session(self, key):
        """Deletes the mirroring session.
        Parameters
        ----------
        key : str
            Key of the Session
        """
        session = vim.dvs.VmwareDistributedVirtualSwitch.VspanSession(
            key=key
        )
        config_version = self.dv_switch.config.configVersion
        s_spec = vim.dvs.VmwareDistributedVirtualSwitch.VspanConfigSpec(vspanSession=session, operation="remove")
        c_spec = vim.dvs.VmwareDistributedVirtualSwitch.ConfigSpec(vspanConfigSpec=[s_spec], configVersion=config_version)
        task = self.dv_switch.ReconfigureDvs_Task(c_spec)
        try:
            wait_for_task(task)
        except Exception:
            self.restore_original_state()
            self.module.fail_json(msg=task.info.error.msg)

    def restore_original_state(self):
        """In case of failure restore, the changes we made."""
        for port, state in self.modified_ports.items():
            self.set_port_security_promiscuous([port], state)
        if self.deleted_session is not None:
            session = self.deleted_session
            config_version = self.dv_switch.config.configVersion
            s_spec = vim.dvs.VmwareDistributedVirtualSwitch.VspanConfigSpec(vspanSession=session, operation="add")
            c_spec = vim.dvs.VmwareDistributedVirtualSwitch.ConfigSpec(vspanConfigSpec=[s_spec], configVersion=config_version)
            # Revert the delete
            task = self.dv_switch.ReconfigureDvs_Task(c_spec)
            try:
                wait_for_task(task)
            except Exception:
                self.restore_original_state()
                self.module.fail_json(msg=task.info.error.msg)

    def remove_vspan_session(self):
        """Calls the necessary functions to delete a VSpanSession."""
        results = dict(changed=False, result="")
        mirror_session = self.find_session_by_name()
        if mirror_session is None:
            results['result'] = "There is no VSpanSession with the name: {0:s}.".format(self.name)
            return results
        promiscuous_ports = self.turn_off_promiscuous()
        session_key = mirror_session.key
        # Delete Mirroring Session
        self.delete_mirroring_session(session_key)
        # Session
        self.deleted_session = mirror_session
        # Set back the promiscuous ports
        if promiscuous_ports:
            self.set_port_security_promiscuous(promiscuous_ports, True)
        results['changed'] = True
        results['result'] = 'VSpan Session has been deleted'
        return results

    def check_if_session_name_is_free(self):
        """Checks whether the name is used or not
        Returns
        -------
        bool
            True if the name is free and False if it is used.
        """
        for vspan_session in self.dv_switch.config.vspanSession:
            if vspan_session.name == self.name:
                return False
        return True

    def create_vspan_session(self):
        """Builds up the session, adds the parameters that we specified, then creates it on the vSwitch"""

        session = vim.dvs.VmwareDistributedVirtualSwitch.VspanSession(
            name=self.name,
            enabled=True
        )
        if self.session_type is not None:
            session.sessionType = self.session_type
            if self.session_type == 'encapsulatedRemoteMirrorSource':
                if self.source_port_received is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(portKey=str(self.source_port_received))
                    if not self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=port.portKey)):
                        self.module.fail_json(msg="Couldn't find port: {0:s}".format(self.source_port_received))
                    session.sourcePortReceived = port
                if self.source_port_transmitted is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(portKey=str(self.source_port_transmitted))
                    if not self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=port.portKey)):
                        self.module.fail_json(msg="Couldn't find port: {0:s}".format(self.source_port_transmitted))
                    session.sourcePortTransmitted = port
                if self.destination_port is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(ipAddress=str(self.destination_port))
                    session.destinationPort = port
            if self.session_type == 'remoteMirrorSource':
                if self.source_port_received is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(portKey=str(self.source_port_received))
                    if not self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=port.portKey)):
                        self.module.fail_json(msg="Couldn't find port: {0:s}".format(self.source_port_received))
                    session.sourcePortReceived = port
                if self.source_port_transmitted is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(portKey=str(self.source_port_transmitted))
                    if not self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=port.portKey)):
                        self.module.fail_json(msg="Couldn't find port: {0:s}".format(self.source_port_transmitted))
                    session.sourcePortTransmitted = port
                if self.destination_port is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(uplinkPortName=str(self.destination_port))
                    session.destinationPort = port
            if self.session_type == 'remoteMirrorDest':
                if self.source_port_received is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(vlans=[int(self.source_port_received)])
                    if int(self.source_port_received) not in self.dv_switch.QueryUsedVlanIdInDvs():
                        self.module.fail_json(msg="Couldn't find vlan: {0:s}".format(self.source_port_received))
                    session.sourcePortReceived = port
                if self.destination_port is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(portKey=str(self.destination_port))
                    if not self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=port.portKey)):
                        self.module.fail_json(msg="Couldn't find port: {0:s}".format(self.destination_port))
                    session.destinationPort = port
            if self.session_type == 'dvPortMirror':
                if self.source_port_received is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(portKey=str(self.source_port_received))
                    if not self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=port.portKey)):
                        self.module.fail_json(msg="Couldn't find port: {0:s}".format(self.source_port_received))
                    session.sourcePortReceived = port
                if self.source_port_transmitted is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(portKey=str(self.source_port_transmitted))
                    if not self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=port.portKey)):
                        self.module.fail_json(msg="Couldn't find port: {0:s}".format(self.source_port_transmitted))
                    session.sourcePortTransmitted = port
                if self.destination_port is not None:
                    port = vim.dvs.VmwareDistributedVirtualSwitch.VspanPorts(portKey=str(self.destination_port))
                    if not self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=port.portKey)):
                        self.module.fail_json(msg="Couldn't find port: {0:s}".format(self.destination_port))
                    session.destinationPort = port
        if self.description is not None:
            session.description = self.description
        if self.encapsulation_vlan_id is not None:
            session.encapsulationVlanId = self.encapsulation_vlan_id
        if self.strip_original_vlan is not None:
            session.stripOriginalVlan = self.strip_original_vlan
        if self.mirrored_packet_length is not None:
            session.mirroredPacketLength = self.mirrored_packet_length
        if self.normal_traffic_allowed is not None:
            session.normalTrafficAllowed = self.normal_traffic_allowed
        if self.sampling_rate is not None:
            session.samplingRate = self.sampling_rate
        config_version = self.dv_switch.config.configVersion
        s_spec = vim.dvs.VmwareDistributedVirtualSwitch.VspanConfigSpec(vspanSession=session, operation="add")
        c_spec = vim.dvs.VmwareDistributedVirtualSwitch.ConfigSpec(vspanConfigSpec=[s_spec], configVersion=config_version)
        task = self.dv_switch.ReconfigureDvs_Task(c_spec)
        try:
            wait_for_task(task)
        except Exception:
            self.restore_original_state()
            self.module.fail_json(msg=task.info.error.msg)

    def add_vspan_session(self):
        """Calls the necessary functions to create a VSpanSession"""
        results = dict(changed=False, result="")
        promiscous_ports = self.turn_off_promiscuous()
        if not self.check_if_session_name_is_free():
            self.module.fail_json(msg="There is another VSpan Session with the name: {0:s}.".format(self.name))
        # Locate the ports, we want to use
        dv_ports = None
        ports = [str(self.source_port_received), str(self.source_port_transmitted), str(self.destination_port)]
        if ports:
            dv_ports = self.dv_switch.FetchDVPorts(vim.dvs.PortCriteria(portKey=ports))
        for dv_port in dv_ports:
            if dv_port.config.setting.securityPolicy.allowPromiscuous.value:
                self.set_port_security_promiscuous([dv_port.key], False)
                self.modified_ports.update({dv_port.key: True})
        # Now we can create the VspanSession
        self.create_vspan_session()
        # Finally we can set the destination port to promiscuous mode
        if self.session_type == 'dvPortMirror' or self.session_type == 'remoteMirrorDest':
            self.set_port_security_promiscuous([str(self.destination_port)], True)
        # Set Back the Promiscuous ports
        if promiscous_ports:
            self.set_port_security_promiscuous(promiscous_ports, True)
        results['changed'] = True
        results['result'] = 'Mirroring session has been created.'
        return results


def main():
    argument_spec = vmware_argument_spec()
    argument_spec.update(dict(
        switch=dict(type='str', required=True, aliases=['switch_name']),
        name=dict(type='str', required=True),
        state=dict(type='str', required=True, choices=['present', 'absent']),
        session_type=dict(type='str', default='dvPortMirror', choices=['dvPortMirror',
                                                                       'encapsulatedRemoteMirrorSource',
                                                                       'remoteMirrorDest',
                                                                       'remoteMirrorSource']),
        enabled=dict(type='bool', default=True),
        description=dict(type='str'),
        source_port_transmitted=dict(type='str'),
        source_port_received=dict(type='str'),
        destination_port=dict(type='str'),
        encapsulation_vlan_id=dict(type='int'),
        strip_original_vlan=dict(type='bool'),
        mirrored_packet_length=dict(type='int'),
        normal_traffic_allowed=dict(type='bool'),
        sampling_rate=dict(type='int'),
        source_vm_transmitted=dict(type='dict',
                                   options=dict(
                                       name=dict(type='str'),
                                       nic_label=dict(type='str'))),
        source_vm_received=dict(type='dict',
                                options=dict(
                                    name=dict(type='str'),
                                    nic_label=dict(type='str'))),
        destination_vm=dict(type='dict',
                            options=dict(
                                name=dict(type='str'),
                                nic_label=dict(type='str'))),
    ))
    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
    session = VMwareVspanSession(module)
    session.process_operation()


if __name__ == '__main__':
    main()
