#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2017, Ansible by Red Hat, inc
# 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


DOCUMENTATION = """
module: iosxr_interface
author:
- Ganesh Nalawade (@ganeshrn)
- Kedar Kekan (@kedarX)
short_description: (deprecated, removed after 2022-06-01) Manage Interface on Cisco
  IOS XR network devices
description:
- This module provides declarative management of Interfaces on Cisco IOS XR network
  devices.
version_added: 1.0.0
deprecated:
  alternative: iosxr_interfaces
  why: Newer and updated modules released with more functionality in Ansible 2.9
  removed_at_date: '2022-06-01'
requirements:
- ncclient >= 0.5.3 when using netconf
- lxml >= 4.1.1 when using netconf
extends_documentation_fragment:
- cisco.iosxr.iosxr
notes:
- This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR
  Platform Options,../network/user_guide/platform_iosxr.html).
- Tested against IOS XRv 6.1.3.
- Preconfiguration of physical interfaces is not supported with C(netconf) transport.
options:
  name:
    description:
    - Name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0)
    type: str
    required: true
  description:
    description:
    - Description of Interface being configured.
    type: str
  enabled:
    description:
    - Removes the shutdown configuration, which removes the forced administrative
      down on the interface, enabling it to move to an up or down state.
    type: bool
    default: true
  active:
    description:
    - Whether the interface is C(active) or C(preconfigured). Preconfiguration allows
      you to configure modular services cards before they are inserted into the router.
      When the cards are inserted, they are instantly configured. Active cards are
      the ones already inserted.
    type: str
    choices:
    - active
    - preconfigure
    default: active
  speed:
    description:
    - Configure the speed for an interface. Default is auto-negotiation when not configured.
    choices:
    - '10'
    - '100'
    - '1000'
    type: str
  mtu:
    description:
    - Sets the MTU value for the interface. Range is between 64 and 65535'
    type: str
  duplex:
    description:
    - Configures the interface duplex mode. Default is auto-negotiation when not configured.
    type: str
    choices:
    - full
    - half
  tx_rate:
    description:
    - Transmit rate in bits per second (bps).
    - This is state check parameter only.
    - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
    type: str
  rx_rate:
    description:
    - Receiver rate in bits per second (bps).
    - This is state check parameter only.
    - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
    type: str
  delay:
    description:
    - Time in seconds to wait before checking for the operational state on remote
      device. This wait is applicable for operational state argument which are I(state)
      with values C(up)/C(down), I(tx_rate) and I(rx_rate).
    default: 10
    type: int
  aggregate:
    description: List of interfaces definition
    type: list
    elements: dict
    suboptions:
      name:
        description:
        - Name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0)
        type: str
      description:
        description:
        - Description of Interface being configured.
        type: str
      enabled:
        description:
        - Removes the shutdown configuration, which removes the forced administrative
          down on the interface, enabling it to move to an up or down state.
        type: bool
      active:
        description:
        - Whether the interface is C(active) or C(preconfigured). Preconfiguration allows
          you to configure modular services cards before they are inserted into the router.
          When the cards are inserted, they are instantly configured. Active cards are
          the ones already inserted.
        type: str
        choices:
        - active
        - preconfigure
      speed:
        description:
        - Configure the speed for an interface. Default is auto-negotiation when not configured.
        choices:
        - '10'
        - '100'
        - '1000'
        type: str
      mtu:
        description:
        - Sets the MTU value for the interface. Range is between 64 and 65535'
        type: str
      duplex:
        description:
        - Configures the interface duplex mode. Default is auto-negotiation when not configured.
        type: str
        choices:
        - full
        - half
      tx_rate:
        description:
        - Transmit rate in bits per second (bps).
        - This is state check parameter only.
        - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
        type: str
      rx_rate:
        description:
        - Receiver rate in bits per second (bps).
        - This is state check parameter only.
        - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
        type: str
      delay:
        description:
        - Time in seconds to wait before checking for the operational state on remote
          device. This wait is applicable for operational state argument which are I(state)
          with values C(up)/C(down), I(tx_rate) and I(rx_rate).
        type: int
      state:
        description:
        - State of the Interface configuration, C(up) means present and operationally
          up and C(down) means present and operationally C(down)
        type: str
        choices:
        - present
        - absent
        - up
        - down
  state:
    description:
    - State of the Interface configuration, C(up) means present and operationally
      up and C(down) means present and operationally C(down)
    type: str
    default: present
    choices:
    - present
    - absent
    - up
    - down


"""

EXAMPLES = """
- name: configure interface
  cisco.iosxr.iosxr_interface:
    name: GigabitEthernet0/0/0/2
    description: test-interface
    speed: 100
    duplex: half
    mtu: 512

- name: remove interface
  cisco.iosxr.iosxr_interface:
    name: GigabitEthernet0/0/0/2
    state: absent

- name: make interface up
  cisco.iosxr.iosxr_interface:
    name: GigabitEthernet0/0/0/2
    enabled: true

- name: make interface down
  cisco.iosxr.iosxr_interface:
    name: GigabitEthernet0/0/0/2
    enabled: false

- name: Create interface using aggregate
  cisco.iosxr.iosxr_interface:
    aggregate:
    - name: GigabitEthernet0/0/0/3
    - name: GigabitEthernet0/0/0/2
    speed: 100
    duplex: full
    mtu: 512
    state: present

- name: Create interface using aggregate along with additional params in aggregate
  cisco.iosxr.iosxr_interface:
    aggregate:
    - {name: GigabitEthernet0/0/0/3, description: test-interface 3}
    - {name: GigabitEthernet0/0/0/2, description: test-interface 2}
    speed: 100
    duplex: full
    mtu: 512
    state: present

- name: Delete interface using aggregate
  cisco.iosxr.iosxr_interface:
    aggregate:
    - name: GigabitEthernet0/0/0/3
    - name: GigabitEthernet0/0/0/2
    state: absent

- name: Check intent arguments
  cisco.iosxr.iosxr_interface:
    name: GigabitEthernet0/0/0/5
    state: up
    delay: 20

- name: Config + intent
  cisco.iosxr.iosxr_interface:
    name: GigabitEthernet0/0/0/5
    enabled: false
    state: down
    delay: 20
"""

RETURN = """
commands:
  description: The list of configuration mode commands sent to device with transport C(cli)
  returned: always (empty list when no commands to send)
  type: list
  sample:
  - interface GigabitEthernet0/0/0/2
  - description test-interface
  - duplex half
  - mtu 512

xml:
  description: NetConf rpc xml sent to device with transport C(netconf)
  returned: always (empty list when no xml rpc to send)
  type: list
  sample:
  - '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
    <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
    <interface-configuration xc:operation="merge">
    <active>act</active>
    <interface-name>GigabitEthernet0/0/0/0</interface-name>
    <description>test-interface-0</description>
    <mtus><mtu>
    <owner>GigabitEthernet</owner>
    <mtu>512</mtu>
    </mtu></mtus>
    <ethernet xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-drivers-media-eth-cfg">
    <speed>100</speed>
    <duplex>half</duplex>
    </ethernet>
    </interface-configuration>
    </interface-configurations></config>'
"""
import re
from time import sleep
from copy import deepcopy
import collections

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import (
    get_config,
    load_config,
    build_xml,
)
from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import (
    run_commands,
    iosxr_argument_spec,
    get_oper,
)
from ansible_collections.cisco.iosxr.plugins.module_utils.network.iosxr.iosxr import (
    is_netconf,
    is_cliconf,
    etree_findall,
    etree_find,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
    conditional,
    remove_default_spec,
)


def validate_mtu(value):
    if value and not 64 <= int(value) <= 65535:
        return False, "mtu must be between 64 and 65535"
    return True, None


class ConfigBase(object):
    def __init__(self, module):
        self._module = module
        self._result = {"changed": False, "warnings": []}
        self._want = list()
        self._have = list()

    def validate_param_values(self, param=None):
        for key, value in param.items():
            # validate the param value (if validator func exists)
            validator = globals().get("validate_%s" % key)
            if callable(validator):
                rc, msg = validator(value)
                if not rc:
                    self._module.fail_json(msg=msg)

    def map_params_to_obj(self):
        aggregate = self._module.params.get("aggregate")
        if aggregate:
            for item in aggregate:
                for key in item:
                    if item.get(key) is None:
                        item[key] = self._module.params[key]

                self.validate_param_values(item)
                d = item.copy()

                match = re.match(r"(^[a-z]+)([0-9/]+$)", d["name"], re.I)
                if match:
                    d["owner"] = match.groups()[0]

                if d["active"] == "preconfigure":
                    d["active"] = "pre"
                else:
                    d["active"] = "act"

                self._want.append(d)

        else:
            self.validate_param_values(self._module.params)
            params = {
                "name": self._module.params["name"],
                "description": self._module.params["description"],
                "speed": self._module.params["speed"],
                "mtu": self._module.params["mtu"],
                "duplex": self._module.params["duplex"],
                "state": self._module.params["state"],
                "delay": self._module.params["delay"],
                "tx_rate": self._module.params["tx_rate"],
                "rx_rate": self._module.params["rx_rate"],
                "enabled": self._module.params["enabled"],
                "active": self._module.params["active"],
            }

            match = re.match(r"(^[a-z]+)([0-9/]+$)", params["name"], re.I)
            if match:
                params["owner"] = match.groups()[0]

                if params["active"] == "preconfigure":
                    params["active"] = "pre"
                else:
                    params["active"] = "act"

            self._want.append(params)


class CliConfiguration(ConfigBase):
    def __init__(self, module):
        super(CliConfiguration, self).__init__(module)

    def parse_shutdown(self, intf_config):
        for cfg in intf_config:
            match = re.search(r"%s" % "shutdown", cfg, re.M)
            if match:
                return True
        return False

    def parse_config_argument(self, intf_config, arg):
        for cfg in intf_config:
            match = re.search(r"%s (.+)$" % arg, cfg, re.M)
            if match:
                return match.group(1)

    def search_obj_in_list(self, name):
        for obj in self._have:
            if obj["name"] == name:
                return obj
        return None

    def map_config_to_obj(self):
        data = get_config(self._module, config_filter="interface")
        data_lines = data.splitlines()
        start_indexes = [
            i for i, e in enumerate(data_lines) if e.startswith("interface")
        ]
        end_indexes = [i for i, e in enumerate(data_lines) if e == "!"]

        intf_configs = list()
        for start_index, end_index in zip(start_indexes, end_indexes):
            intf_configs.append(
                [i.strip() for i in data_lines[start_index:end_index]]
            )

        if not intf_configs:
            return list()

        for intf_config in intf_configs:
            name = intf_config[0].strip().split()[1]

            active = "act"
            if name == "preconfigure":
                active = "pre"
                name = intf_config[0].strip().split()[2]

            obj = {
                "name": name,
                "description": self.parse_config_argument(
                    intf_config, "description"
                ),
                "speed": self.parse_config_argument(intf_config, "speed"),
                "duplex": self.parse_config_argument(intf_config, "duplex"),
                "mtu": self.parse_config_argument(intf_config, "mtu"),
                "enabled": not bool(self.parse_shutdown(intf_config)),
                "active": active,
                "state": "present",
            }
            self._have.append(obj)

    def map_obj_to_commands(self):
        commands = list()

        args = ("speed", "description", "duplex", "mtu")
        for want_item in self._want:
            name = want_item["name"]
            disable = not want_item["enabled"]
            state = want_item["state"]

            obj_in_have = self.search_obj_in_list(name)
            interface = "interface " + name

            if state == "absent" and obj_in_have:
                commands.append("no " + interface)

            elif state in ("present", "up", "down"):
                if obj_in_have:
                    for item in args:
                        candidate = want_item.get(item)
                        running = obj_in_have.get(item)
                        if candidate != running:
                            if candidate:
                                cmd = (
                                    interface
                                    + " "
                                    + item
                                    + " "
                                    + str(candidate)
                                )
                                commands.append(cmd)

                    if disable and obj_in_have.get("enabled", False):
                        commands.append(interface + " shutdown")
                    elif not disable and not obj_in_have.get("enabled", False):
                        commands.append("no " + interface + " shutdown")
                else:
                    for item in args:
                        value = want_item.get(item)
                        if value:
                            commands.append(
                                interface + " " + item + " " + str(value)
                            )
                    if not disable:
                        commands.append("no " + interface + " shutdown")
        self._result["commands"] = commands

        if commands:
            commit = not self._module.check_mode
            diff = load_config(self._module, commands, commit=commit)
            if diff:
                self._result["diff"] = dict(prepared=diff)
            self._result["changed"] = True

    def check_declarative_intent_params(self):
        failed_conditions = []
        for want_item in self._want:
            want_state = want_item.get("state")
            want_tx_rate = want_item.get("tx_rate")
            want_rx_rate = want_item.get("rx_rate")
            if (
                want_state not in ("up", "down")
                and not want_tx_rate
                and not want_rx_rate
            ):
                continue

            if self._result["changed"]:
                sleep(want_item["delay"])

            command = "show interfaces {0!s}".format(want_item["name"])
            out = run_commands(self._module, command)[0]

            if want_state in ("up", "down"):
                match = re.search(r"%s (\w+)" % "line protocol is", out, re.M)
                have_state = None
                if match:
                    have_state = match.group(1)
                    if have_state.strip() == "administratively":
                        match = re.search(
                            r"%s (\w+)" % "administratively", out, re.M
                        )
                        if match:
                            have_state = match.group(1)

                if have_state is None or not conditional(
                    want_state, have_state.strip()
                ):
                    failed_conditions.append(
                        "state " + "eq({0!s})".format(want_state)
                    )

            if want_tx_rate:
                match = re.search(r"%s (\d+)" % "output rate", out, re.M)
                have_tx_rate = None
                if match:
                    have_tx_rate = match.group(1)

                if have_tx_rate is None or not conditional(
                    want_tx_rate, have_tx_rate.strip(), cast=int
                ):
                    failed_conditions.append("tx_rate " + want_tx_rate)

            if want_rx_rate:
                match = re.search(r"%s (\d+)" % "input rate", out, re.M)
                have_rx_rate = None
                if match:
                    have_rx_rate = match.group(1)

                if have_rx_rate is None or not conditional(
                    want_rx_rate, have_rx_rate.strip(), cast=int
                ):
                    failed_conditions.append("rx_rate " + want_rx_rate)

        if failed_conditions:
            msg = "One or more conditional statements have not been satisfied"
            self._module.fail_json(
                msg=msg, failed_conditions=failed_conditions
            )

    def run(self):
        self.map_params_to_obj()
        self.map_config_to_obj()
        self.map_obj_to_commands()
        self.check_declarative_intent_params()

        return self._result


class NCConfiguration(ConfigBase):
    def __init__(self, module):
        super(NCConfiguration, self).__init__(module)

        self._intf_meta = collections.OrderedDict()
        self._shut_meta = collections.OrderedDict()
        self._data_rate_meta = collections.OrderedDict()
        self._line_state_meta = collections.OrderedDict()

    def map_obj_to_xml_rpc(self):
        self._intf_meta.update(
            [
                (
                    "interface-configuration",
                    {
                        "xpath": "interface-configurations/interface-configuration",
                        "tag": True,
                        "attrib": "operation",
                    },
                ),
                (
                    "a:active",
                    {
                        "xpath": "interface-configurations/interface-configuration/active",
                        "operation": "edit",
                    },
                ),
                (
                    "a:name",
                    {
                        "xpath": "interface-configurations/interface-configuration/interface-name"
                    },
                ),
                (
                    "a:description",
                    {
                        "xpath": "interface-configurations/interface-configuration/description",
                        "operation": "edit",
                    },
                ),
                (
                    "mtus",
                    {
                        "xpath": "interface-configurations/interface-configuration/mtus",
                        "tag": True,
                        "operation": "edit",
                    },
                ),
                (
                    "mtu",
                    {
                        "xpath": "interface-configurations/interface-configuration/mtus/mtu",
                        "tag": True,
                        "operation": "edit",
                    },
                ),
                (
                    "a:owner",
                    {
                        "xpath": "interface-configurations/interface-configuration/mtus/mtu/owner",
                        "operation": "edit",
                    },
                ),
                (
                    "a:mtu",
                    {
                        "xpath": "interface-configurations/interface-configuration/mtus/mtu/mtu",
                        "operation": "edit",
                    },
                ),
                (
                    "CEthernet",
                    {
                        "xpath": "interface-configurations/interface-configuration/ethernet",
                        "tag": True,
                        "operation": "edit",
                        "ns": True,
                    },
                ),
                (
                    "a:speed",
                    {
                        "xpath": "interface-configurations/interface-configuration/ethernet/speed",
                        "operation": "edit",
                    },
                ),
                (
                    "a:duplex",
                    {
                        "xpath": "interface-configurations/interface-configuration/ethernet/duplex",
                        "operation": "edit",
                    },
                ),
            ]
        )

        self._shut_meta.update(
            [
                (
                    "interface-configuration",
                    {
                        "xpath": "interface-configurations/interface-configuration",
                        "tag": True,
                    },
                ),
                (
                    "a:active",
                    {
                        "xpath": "interface-configurations/interface-configuration/active",
                        "operation": "edit",
                    },
                ),
                (
                    "a:name",
                    {
                        "xpath": "interface-configurations/interface-configuration/interface-name"
                    },
                ),
                (
                    "shutdown",
                    {
                        "xpath": "interface-configurations/interface-configuration/shutdown",
                        "tag": True,
                        "operation": "edit",
                        "attrib": "operation",
                    },
                ),
            ]
        )
        state = self._module.params["state"]

        _get_filter = build_xml(
            "interface-configurations",
            xmap=self._intf_meta,
            params=self._want,
            opcode="filter",
        )

        running = get_config(
            self._module, source="running", config_filter=_get_filter
        )
        intfcfg_nodes = etree_findall(running, "interface-configuration")

        intf_list = set()
        shut_list = set()
        for item in intfcfg_nodes:
            intf_name = etree_find(item, "interface-name").text
            if intf_name is not None:
                intf_list.add(intf_name)

                if etree_find(item, "shutdown") is not None:
                    shut_list.add(intf_name)

        intf_params = list()
        shut_params = list()
        noshut_params = list()
        for index, item in enumerate(self._want):
            if item["name"] in intf_list:
                intf_params.append(item)
            if not item["enabled"]:
                shut_params.append(item)
            if item["name"] in shut_list and item["enabled"]:
                noshut_params.append(item)

        opcode = None
        if state == "absent":
            if intf_params:
                opcode = "delete"
        elif state in ("present", "up", "down"):
            intf_params = self._want
            opcode = "merge"

        self._result["xml"] = []
        _edit_filter_list = list()
        if opcode:
            _edit_filter_list.append(
                build_xml(
                    "interface-configurations",
                    xmap=self._intf_meta,
                    params=intf_params,
                    opcode=opcode,
                )
            )

            if opcode == "merge":
                if len(shut_params):
                    _edit_filter_list.append(
                        build_xml(
                            "interface-configurations",
                            xmap=self._shut_meta,
                            params=shut_params,
                            opcode="merge",
                        )
                    )
                if len(noshut_params):
                    _edit_filter_list.append(
                        build_xml(
                            "interface-configurations",
                            xmap=self._shut_meta,
                            params=noshut_params,
                            opcode="delete",
                        )
                    )
            diff = None
            if len(_edit_filter_list):
                commit = not self._module.check_mode
                diff = load_config(
                    self._module,
                    _edit_filter_list,
                    commit=commit,
                    running=running,
                    nc_get_filter=_get_filter,
                )

            if diff:
                if self._module._diff:
                    self._result["diff"] = dict(prepared=diff)

                self._result["xml"] = _edit_filter_list
                self._result["changed"] = True

    def check_declarative_intent_params(self):
        failed_conditions = []

        self._data_rate_meta.update(
            [
                (
                    "interfaces",
                    {"xpath": "infra-statistics/interfaces", "tag": True},
                ),
                (
                    "interface",
                    {
                        "xpath": "infra-statistics/interfaces/interface",
                        "tag": True,
                    },
                ),
                (
                    "a:name",
                    {
                        "xpath": "infra-statistics/interfaces/interface/interface-name"
                    },
                ),
                (
                    "cache",
                    {
                        "xpath": "infra-statistics/interfaces/interface/cache",
                        "tag": True,
                    },
                ),
                (
                    "data-rate",
                    {
                        "xpath": "infra-statistics/interfaces/interface/cache/data-rate",
                        "tag": True,
                    },
                ),
                (
                    "input-data-rate",
                    {
                        "xpath": "infra-statistics/interfaces/interface/cache/data-rate/input-data-rate",
                        "tag": True,
                    },
                ),
                (
                    "output-data-rate",
                    {
                        "xpath": "infra-statistics/interfaces/interface/cache/data-rate/output-data-rate",
                        "tag": True,
                    },
                ),
            ]
        )

        self._line_state_meta.update(
            [
                (
                    "data-nodes",
                    {"xpath": "interface-properties/data-nodes", "tag": True},
                ),
                (
                    "data-node",
                    {
                        "xpath": "interface-properties/data-nodes/data-node",
                        "tag": True,
                    },
                ),
                (
                    "system-view",
                    {
                        "xpath": "interface-properties/data-nodes/data-node/system-view",
                        "tag": True,
                    },
                ),
                (
                    "interfaces",
                    {
                        "xpath": "interface-properties/data-nodes/data-node/system-view/interfaces",
                        "tag": True,
                    },
                ),
                (
                    "interface",
                    {
                        "xpath": "interface-properties/data-nodes/data-node/system-view/interfaces/interface",
                        "tag": True,
                    },
                ),
                (
                    "a:name",
                    {
                        "xpath": "interface-properties/data-nodes/data-node/system-view/interfaces/interface/interface-name"
                    },
                ),
                (
                    "line-state",
                    {
                        "xpath": "interface-properties/data-nodes/data-node/system-view/interfaces/interface/line-state",
                        "tag": True,
                    },
                ),
            ]
        )

        _rate_filter = build_xml(
            "infra-statistics",
            xmap=self._data_rate_meta,
            params=self._want,
            opcode="filter",
        )
        out = get_oper(self._module, filter=_rate_filter)
        data_rate_list = etree_findall(out, "interface")
        data_rate_map = dict()
        for item in data_rate_list:
            data_rate_map.update(
                {etree_find(item, "interface-name").text: dict()}
            )
            data_rate_map[etree_find(item, "interface-name").text].update(
                {
                    "input-data-rate": etree_find(
                        item, "input-data-rate"
                    ).text,
                    "output-data-rate": etree_find(
                        item, "output-data-rate"
                    ).text,
                }
            )

        _line_state_filter = build_xml(
            "interface-properties",
            xmap=self._line_state_meta,
            params=self._want,
            opcode="filter",
        )
        out = get_oper(self._module, filter=_line_state_filter)
        line_state_list = etree_findall(out, "interface")
        line_state_map = dict()
        for item in line_state_list:
            line_state_map.update(
                {
                    etree_find(item, "interface-name")
                    .text: etree_find(item, "line-state")
                    .text
                }
            )

        for want_item in self._want:
            want_state = want_item.get("state")
            want_tx_rate = want_item.get("tx_rate")
            want_rx_rate = want_item.get("rx_rate")
            if (
                want_state not in ("up", "down")
                and not want_tx_rate
                and not want_rx_rate
            ):
                continue

            if self._result["changed"]:
                sleep(want_item["delay"])

            if want_state in ("up", "down"):
                if want_state not in line_state_map[want_item["name"]]:
                    failed_conditions.append(
                        "state " + "eq({0!s})".format(want_state)
                    )

            if want_tx_rate:
                if (
                    want_tx_rate
                    != data_rate_map[want_item["name"]]["output-data-rate"]
                ):
                    failed_conditions.append("tx_rate " + want_tx_rate)

            if want_rx_rate:
                if (
                    want_rx_rate
                    != data_rate_map[want_item["name"]]["input-data-rate"]
                ):
                    failed_conditions.append("rx_rate " + want_rx_rate)

        if failed_conditions:
            msg = "One or more conditional statements have not been satisfied"
            self._module.fail_json(
                msg=msg, failed_conditions=failed_conditions
            )

    def run(self):
        self.map_params_to_obj()
        self.map_obj_to_xml_rpc()
        self.check_declarative_intent_params()
        return self._result


def main():
    """ main entry point for module execution
    """
    element_spec = dict(
        name=dict(type="str"),
        description=dict(type="str"),
        speed=dict(choices=["10", "100", "1000"]),
        mtu=dict(),
        duplex=dict(choices=["full", "half"]),
        enabled=dict(default=True, type="bool"),
        active=dict(
            type="str", choices=["active", "preconfigure"], default="active"
        ),
        tx_rate=dict(),
        rx_rate=dict(),
        delay=dict(default=10, type="int"),
        state=dict(
            default="present", choices=["present", "absent", "up", "down"]
        ),
    )

    aggregate_spec = deepcopy(element_spec)
    aggregate_spec["name"] = dict(required=True)

    # remove default in aggregate spec, to handle common arguments
    remove_default_spec(aggregate_spec)

    argument_spec = dict(
        aggregate=dict(type="list", elements="dict", options=aggregate_spec)
    )

    argument_spec.update(element_spec)
    argument_spec.update(iosxr_argument_spec)

    required_one_of = [["name", "aggregate"]]
    mutually_exclusive = [["name", "aggregate"]]

    module = AnsibleModule(
        argument_spec=argument_spec,
        required_one_of=required_one_of,
        mutually_exclusive=mutually_exclusive,
        supports_check_mode=True,
    )

    config_object = None
    if is_cliconf(module):
        # Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported
        # module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead",
        #                  version='2.9')
        config_object = CliConfiguration(module)
    elif is_netconf(module):
        if module.params["active"] == "preconfigure":
            module.fail_json(
                msg="Physical interface pre-configuration is not supported with transport 'netconf'"
            )
        config_object = NCConfiguration(module)

    result = {}
    if config_object:
        result = config_object.run()
    module.exit_json(**result)


if __name__ == "__main__":
    main()
