From 4f83f5cf80ca92bf91792a0cd4621744b4f1c37e Mon Sep 17 00:00:00 2001
From: GomathiselviS <gomathiselvi@gmail.com>
Date: Fri, 26 Mar 2021 20:48:39 -0400
Subject: Add vyos_bgp_address_family resource module. (#132)

Add vyos_bgp_address_family resource module.

Reviewed-by: https://github.com/apps/ansible-zuul
---
 .../fragments/132-add-vyos-bgp-address-family.yml  |    3 +
 meta/runtime.yml                                   |    6 +
 .../vyos/argspec/bgp_address_family/__init__.py    |    0
 .../bgp_address_family/bgp_address_family.py       |  200 +++
 .../vyos/config/bgp_address_family/__init__.py     |    0
 .../bgp_address_family/bgp_address_family.py       |  369 +++++
 .../vyos/facts/bgp_address_family/__init__.py      |    0
 .../facts/bgp_address_family/bgp_address_family.py |  101 ++
 plugins/module_utils/network/vyos/facts/facts.py   |    4 +
 .../vyos/rm_templates/bgp_address_family.py        | 1421 ++++++++++++++++++++
 plugins/modules/vyos_bgp_address_family.py         | 1192 ++++++++++++++++
 .../vyos_bgp_address_family/defaults/main.yaml     |    3 +
 .../targets/vyos_bgp_address_family/meta/main.yaml |    3 +
 .../targets/vyos_bgp_address_family/tasks/cli.yaml |   19 +
 .../vyos_bgp_address_family/tasks/main.yaml        |    4 +
 .../tests/cli/_parsed_config.cfg                   |    9 +
 .../tests/cli/_populate.yaml                       |   17 +
 .../tests/cli/_preconfig.yaml                      |   11 +
 .../tests/cli/_remove_config.yaml                  |   12 +
 .../vyos_bgp_address_family/tests/cli/deleted.yaml |   49 +
 .../tests/cli/empty_config.yaml                    |   60 +
 .../tests/cli/gathered.yaml                        |   24 +
 .../vyos_bgp_address_family/tests/cli/merged.yaml  |   70 +
 .../tests/cli/overridden.yaml                      |   58 +
 .../vyos_bgp_address_family/tests/cli/parsed.yaml  |   16 +
 .../tests/cli/rendered.yaml                        |   44 +
 .../tests/cli/replaced.yaml                        |   58 +
 .../vyos_bgp_address_family/tests/cli/rtt.yaml     |   90 ++
 .../targets/vyos_bgp_address_family/vars/main.yaml |   99 ++
 .../fixtures/vyos_bgp_address_family_config.cfg    |    7 +
 .../network/vyos/test_vyos_bgp_address_family.py   |  684 ++++++++++
 tox.ini                                            |    2 +-
 32 files changed, 4634 insertions(+), 1 deletion(-)
 create mode 100644 changelogs/fragments/132-add-vyos-bgp-address-family.yml
 create mode 100644 plugins/module_utils/network/vyos/argspec/bgp_address_family/__init__.py
 create mode 100644 plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py
 create mode 100644 plugins/module_utils/network/vyos/config/bgp_address_family/__init__.py
 create mode 100644 plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py
 create mode 100644 plugins/module_utils/network/vyos/facts/bgp_address_family/__init__.py
 create mode 100644 plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py
 create mode 100644 plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py
 create mode 100644 plugins/modules/vyos_bgp_address_family.py
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/defaults/main.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/meta/main.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tasks/cli.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tasks/main.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/_parsed_config.cfg
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/_populate.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/_preconfig.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/_remove_config.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/deleted.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/empty_config.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/gathered.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/merged.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/overridden.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/parsed.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/rendered.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/replaced.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/tests/cli/rtt.yaml
 create mode 100644 tests/integration/targets/vyos_bgp_address_family/vars/main.yaml
 create mode 100644 tests/unit/modules/network/vyos/fixtures/vyos_bgp_address_family_config.cfg
 create mode 100644 tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py

diff --git a/changelogs/fragments/132-add-vyos-bgp-address-family.yml b/changelogs/fragments/132-add-vyos-bgp-address-family.yml
new file mode 100644
index 00000000..9dc18940
--- /dev/null
+++ b/changelogs/fragments/132-add-vyos-bgp-address-family.yml
@@ -0,0 +1,3 @@
+---
+minor_changes:
+  - Add vyos BGP address_family resource module (https://github.com/ansible-collections/vyos.vyos/pull/132).
diff --git a/meta/runtime.yml b/meta/runtime.yml
index 1f990576..f8db7ccf 100644
--- a/meta/runtime.yml
+++ b/meta/runtime.yml
@@ -10,6 +10,10 @@ plugin_routing:
       redirect: vyos.vyos.vyos
     bgp_global:
       redirect: vyos.vyos.vyos
+    vyos_bgp_address_family:
+      redirect: vyos.vyos.vyos
+    bgp_address_family:
+      redirect: vyos.vyos.vyos
     vyos_command:
       redirect: vyos.vyos.vyos
     command:
@@ -119,6 +123,8 @@ plugin_routing:
       redirect: vyos.vyos.vyos_banner
     bgp_global:
       redirect: vyos.vyos.vyos_bgp_global
+    bgp_address_family:
+      redirect: vyos.vyos.vyos_bgp_address_family
     command:
       redirect: vyos.vyos.vyos_command
     config:
diff --git a/plugins/module_utils/network/vyos/argspec/bgp_address_family/__init__.py b/plugins/module_utils/network/vyos/argspec/bgp_address_family/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py
new file mode 100644
index 00000000..8cbbc505
--- /dev/null
+++ b/plugins/module_utils/network/vyos/argspec/bgp_address_family/bgp_address_family.py
@@ -0,0 +1,200 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+#############################################
+#                WARNING                    #
+#############################################
+#
+# This file is auto generated by the resource
+#   module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+#   by the resource module builder.
+#
+# Changes should be made in the model used to
+#   generate this file or in the resource module
+#   builder template.
+#
+#############################################
+
+"""
+The arg spec for the vyos_bgp_address_family module
+"""
+
+
+class Bgp_address_familyArgs(object):  # pylint: disable=R0903
+    """The arg spec for the vyos_bgp_address_family module"""
+
+    def __init__(self, **kwargs):
+        pass
+
+    argument_spec = {
+        "running_config": {},
+        "state": {
+            "default": "merged",
+            "type": "str",
+            "choices": [
+                "merged",
+                "replaced",
+                "deleted",
+                "gathered",
+                "parsed",
+                "rendered",
+                "purged",
+                "overridden",
+            ],
+        },
+        "config": {
+            "type": "dict",
+            "options": {
+                "neighbors": {
+                    "elements": "dict",
+                    "type": "list",
+                    "options": {
+                        "address_family": {
+                            "elements": "dict",
+                            "type": "list",
+                            "options": {
+                                "nexthop_local": {"type": "bool"},
+                                "soft_reconfiguration": {"type": "bool"},
+                                "unsupress_map": {"type": "str"},
+                                "nexthop_self": {"type": "bool"},
+                                "weight": {"type": "int"},
+                                "prefix_list": {
+                                    "elements": "dict",
+                                    "type": "list",
+                                    "options": {
+                                        "action": {
+                                            "type": "str",
+                                            "choices": ["export", "import"],
+                                        },
+                                        "prefix_list": {"type": "str"},
+                                    },
+                                },
+                                "default_originate": {"type": "str"},
+                                "distribute_list": {
+                                    "elements": "dict",
+                                    "type": "list",
+                                    "options": {
+                                        "action": {
+                                            "type": "str",
+                                            "choices": ["export", "import"],
+                                        },
+                                        "acl": {"type": "int"},
+                                    },
+                                },
+                                "allowas_in": {"type": "int"},
+                                "filter_list": {
+                                    "elements": "dict",
+                                    "type": "list",
+                                    "options": {
+                                        "action": {
+                                            "type": "str",
+                                            "choices": ["export", "import"],
+                                        },
+                                        "path_list": {"type": "str"},
+                                    },
+                                },
+                                "route_server_client": {"type": "bool"},
+                                "attribute_unchanged": {
+                                    "type": "dict",
+                                    "options": {
+                                        "as_path": {"type": "bool"},
+                                        "med": {"type": "bool"},
+                                        "next_hop": {"type": "bool"},
+                                    },
+                                },
+                                "peer_group": {"type": "str"},
+                                "maximum_prefix": {"type": "int"},
+                                "route_reflector_client": {"type": "bool"},
+                                "route_map": {
+                                    "elements": "dict",
+                                    "type": "list",
+                                    "options": {
+                                        "action": {
+                                            "type": "str",
+                                            "choices": ["export", "import"],
+                                        },
+                                        "route_map": {"type": "str"},
+                                    },
+                                },
+                                "capability": {
+                                    "type": "dict",
+                                    "options": {
+                                        "orf": {
+                                            "type": "str",
+                                            "choices": ["send", "receive"],
+                                        },
+                                        "dynamic": {"type": "bool"},
+                                    },
+                                },
+                                "remove_private_as": {"type": "bool"},
+                                "as_override": {"type": "bool"},
+                                "afi": {
+                                    "type": "str",
+                                    "choices": ["ipv4", "ipv6"],
+                                },
+                            },
+                        },
+                        "neighbor_address": {"type": "str"},
+                    },
+                },
+                "as_number": {"type": "int"},
+                "address_family": {
+                    "elements": "dict",
+                    "type": "list",
+                    "options": {
+                        "afi": {"type": "str", "choices": ["ipv4", "ipv6"]},
+                        "redistribute": {
+                            "elements": "dict",
+                            "type": "list",
+                            "options": {
+                                "table": {"type": "str"},
+                                "metric": {"type": "int"},
+                                "protocol": {
+                                    "type": "str",
+                                    "choices": [
+                                        "connected",
+                                        "kernel",
+                                        "ospf",
+                                        "ospfv3",
+                                        "rip",
+                                        "ripng",
+                                        "static",
+                                    ],
+                                },
+                                "route_map": {"type": "str"},
+                            },
+                        },
+                        "networks": {
+                            "elements": "dict",
+                            "type": "list",
+                            "options": {
+                                "backdoor": {"type": "bool"},
+                                "prefix": {"type": "str"},
+                                "path_limit": {"type": "int"},
+                                "route_map": {"type": "str"},
+                            },
+                        },
+                        "aggregate_address": {
+                            "elements": "dict",
+                            "type": "list",
+                            "options": {
+                                "summary_only": {"type": "bool"},
+                                "prefix": {"type": "str"},
+                                "as_set": {"type": "bool"},
+                            },
+                        },
+                    },
+                },
+            },
+        },
+    }  # pylint: disable=C0301
diff --git a/plugins/module_utils/network/vyos/config/bgp_address_family/__init__.py b/plugins/module_utils/network/vyos/config/bgp_address_family/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py
new file mode 100644
index 00000000..876402fe
--- /dev/null
+++ b/plugins/module_utils/network/vyos/config/bgp_address_family/bgp_address_family.py
@@ -0,0 +1,369 @@
+#
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The vyos_bgp_address_family config file.
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to its desired end-state is
+created.
+"""
+
+import re
+
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
+    dict_merge,
+)
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.resource_module import (
+    ResourceModule,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import (
+    Facts,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family import (
+    Bgp_address_familyTemplate,
+)
+
+
+class Bgp_address_family(ResourceModule):
+    """
+    The vyos_bgp_address_family config class
+    """
+
+    def __init__(self, module):
+        super(Bgp_address_family, self).__init__(
+            empty_fact_val={},
+            facts_module=Facts(module),
+            module=module,
+            resource="bgp_address_family",
+            tmplt=Bgp_address_familyTemplate(),
+        )
+        self.parsers = []
+
+    def execute_module(self):
+        """Execute the module
+
+        :rtype: A dictionary
+        :returns: The result from module execution
+        """
+        if self.state not in ["parsed", "gathered"]:
+            self.generate_commands()
+            self.run_commands()
+        return self.result
+
+    def generate_commands(self):
+        """Generate configuration commands to send based on
+        want, have and desired state.
+        """
+        wantd = {}
+        haved = {}
+
+        if (
+            self.want.get("as_number") == self.have.get("as_number")
+            or not self.have
+        ):
+            if self.want:
+                wantd = {self.want["as_number"]: self.want}
+            if self.have:
+                haved = {self.have["as_number"]: self.have}
+        else:
+            self._module.fail_json(
+                msg="Only one bgp instance is allowed per device"
+            )
+
+        # turn all lists of dicts into dicts prior to merge
+        for entry in wantd, haved:
+            self._bgp_af_list_to_dict(entry)
+
+        # if state is merged, merge want onto have and then compare
+        if self.state == "merged":
+            wantd = dict_merge(haved, wantd)
+
+        # if state is deleted, empty out wantd and set haved to wantd
+        if self.state == "deleted":
+            for k, have in iteritems(haved):
+                self._delete_af(wantd, have)
+            wantd = {}
+
+        if self.state == "overridden":
+            for k, have in iteritems(haved):
+                if k not in wantd:
+                    self._compare(want={}, have=have)
+
+        for k, want in iteritems(wantd):
+            self._compare(want=want, have=haved.pop(k, {}))
+
+    def _compare(self, want, have):
+        """Leverages the base class `compare()` method and
+        populates the list of commands to be run by comparing
+        the `want` and `have` data with the `parsers` defined
+        for the Bgp_address_family network resource.
+        """
+        self._compare_af(want, have)
+        self._compare_neighbors(want, have)
+        # Do the negation first
+        command_set = []
+        for cmd in self.commands:
+            if cmd not in command_set:
+                if "delete" in cmd:
+                    command_set.insert(0, cmd)
+                else:
+                    command_set.append(cmd)
+        self.commands = command_set
+
+    def _compare_af(self, want, have):
+        waf = want.get("address_family", {})
+        haf = have.get("address_family", {})
+        for name, entry in iteritems(waf):
+            self._compare_lists(
+                entry,
+                have=haf.get(name, {}),
+                as_number=want["as_number"],
+                afi=name,
+            )
+        for name, entry in iteritems(haf):
+            if name not in waf.keys() and self.state == "replaced":
+                continue
+            self._compare_lists(
+                {}, entry, as_number=have["as_number"], afi=name
+            )
+
+    def _delete_af(self, want, have):
+        for as_num, entry in iteritems(want):
+            for afi, af_entry in iteritems(entry.get("address_family", {})):
+                if have.get("address_family"):
+                    for hafi, hentry in iteritems(have["address_family"]):
+                        if hafi == afi:
+                            self.commands.append(
+                                self._tmplt.render(
+                                    {
+                                        "as_number": as_num,
+                                        "address_family": {"afi": afi},
+                                    },
+                                    "address_family",
+                                    True,
+                                )
+                            )
+            for neigh, neigh_entry in iteritems(entry.get("neighbors", {})):
+                if have.get("neighbors"):
+                    for hneigh, hnentry in iteritems(have["neighbors"]):
+                        if hneigh == neigh:
+                            if not neigh_entry.get("address_family"):
+                                self.commands.append(
+                                    self._tmplt.render(
+                                        {
+                                            "as_number": as_num,
+                                            "neighbors": {
+                                                "neighbor_address": neigh
+                                            },
+                                        },
+                                        "neighbors",
+                                        True,
+                                    )
+                                )
+                            else:
+                                for k in neigh_entry["address_family"].keys():
+                                    if (
+                                        hnentry.get("address_family")
+                                        and k
+                                        in hnentry["address_family"].keys()
+                                    ):
+                                        self.commands.append(
+                                            self._tmplt.render(
+                                                {
+                                                    "as_number": as_num,
+                                                    "neighbors": {
+                                                        "neighbor_address": neigh,
+                                                        "address_family": {
+                                                            "afi": k
+                                                        },
+                                                    },
+                                                },
+                                                "neighbors.address_family",
+                                                True,
+                                            )
+                                        )
+
+    def _compare_neighbors(self, want, have):
+        parsers = [
+            "neighbors.allowas_in",
+            "neighbors.as_override",
+            "neighbors.attribute_unchanged.as_path",
+            "neighbors.attribute_unchanged.med",
+            "neighbors.attribute_unchanged.next_hop",
+            "neighbors.capability_dynamic",
+            "neighbors.capability_orf",
+            "neighbors.default_originate",
+            "neighbors.distribute_list",
+            "neighbors.prefix_list",
+            "neighbors.filter_list",
+            "neighbors.maximum_prefix",
+            "neighbors.nexthop_local",
+            "neighbors.nexthop_self",
+            "neighbors.peer_group",
+            "neighbors.remove_private_as",
+            "neighbors.route_map",
+            "neighbors.route_reflector_client",
+            "neighbors.route_server_client",
+            "neighbors.soft_reconfiguration",
+            "neighbors.unsuppress_map",
+            "neighbors.weight",
+        ]
+        wneigh = want.get("neighbors", {})
+        hneigh = have.get("neighbors", {})
+        for name, entry in iteritems(wneigh):
+            for afi, af_entry in iteritems(entry.get("address_family")):
+                for k, val in iteritems(af_entry):
+                    w = {
+                        "as_number": want["as_number"],
+                        "neighbors": {
+                            "neighbor_address": name,
+                            "address_family": {"afi": afi, k: val},
+                        },
+                    }
+                    h = {}
+                    if hneigh.get(name):
+                        if hneigh[name]["address_family"].get(afi):
+                            if hneigh[name]["address_family"][afi].get(k):
+                                h = {
+                                    "as_number": want["as_number"],
+                                    "neighbors": {
+                                        "neighbor_address": name,
+                                        "address_family": {
+                                            "afi": afi,
+                                            k: hneigh[name]["address_family"][
+                                                afi
+                                            ].pop(k, {}),
+                                        },
+                                    },
+                                }
+                    self.compare(
+                        parsers=parsers,
+                        want=w,
+                        have=h,
+                    )
+        for name, entry in iteritems(hneigh):
+            if name not in wneigh.keys():
+                # remove surplus config for overridden and replaced
+                if self.state != "replaced":
+                    self.commands.append(
+                        self._tmplt.render(
+                            {
+                                "as_number": have["as_number"],
+                                "neighbors": {"neighbor_address": name},
+                            },
+                            "neighbors",
+                            True,
+                        )
+                    )
+                continue
+
+            for hafi, haf_entry in iteritems(entry.get("address_family")):
+                # remove surplus configs for given neighbor - replace and overridden
+                for k, val in iteritems(haf_entry):
+                    h = {
+                        "as_number": have["as_number"],
+                        "neighbors": {
+                            "neighbor_address": name,
+                            "address_family": {"afi": hafi, k: val},
+                        },
+                    }
+                    self.compare(parsers=parsers, want={}, have=h)
+
+    def _compare_lists(self, want, have, as_number, afi):
+        parsers = [
+            "aggregate_address",
+            "network.backdoor",
+            "network.path_limit",
+            "network.route_map",
+            "redistribute.metric",
+            "redistribute.route_map",
+            "redistribute.table",
+        ]
+        for attrib in ["redistribute", "networks", "aggregate_address"]:
+            wdict = want.pop(attrib, {})
+            hdict = have.pop(attrib, {})
+            for key, entry in iteritems(wdict):
+                if entry != hdict.get(key, {}):
+                    self.compare(
+                        parsers=parsers,
+                        want={
+                            "as_number": as_number,
+                            "address_family": {"afi": afi, attrib: entry},
+                        },
+                        have={
+                            "as_number": as_number,
+                            "address_family": {
+                                "afi": afi,
+                                attrib: hdict.pop(key, {}),
+                            },
+                        },
+                    )
+                hdict.pop(key, {})
+            # remove remaining items in have for replaced
+            if not wdict and hdict:
+                attrib = re.sub("_", "-", attrib)
+                attrib = re.sub("networks", "network", attrib)
+                self.commands.append(
+                    "delete protocols bgp "
+                    + str(as_number)
+                    + " "
+                    + "address-family "
+                    + afi
+                    + " "
+                    + attrib
+                )
+                hdict = {}
+            for key, entry in iteritems(hdict):
+                self.compare(
+                    parsers=parsers,
+                    want={},
+                    have={
+                        "as_number": as_number,
+                        "address_family": {"afi": afi, attrib: entry},
+                    },
+                )
+
+    def _bgp_af_list_to_dict(self, entry):
+        for name, proc in iteritems(entry):
+            if "address_family" in proc:
+                af_dict = {}
+                for entry in proc.get("address_family"):
+                    if "networks" in entry:
+                        network_dict = {}
+                        for n_entry in entry.get("networks", []):
+                            network_dict.update({n_entry["prefix"]: n_entry})
+                        entry["networks"] = network_dict
+
+                    if "aggregate_address" in entry:
+                        agg_dict = {}
+                        for a_entry in entry.get("aggregate_address", []):
+                            agg_dict.update({a_entry["prefix"]: a_entry})
+                        entry["aggregate_address"] = agg_dict
+
+                    if "redistribute" in entry:
+                        redis_dict = {}
+                        for r_entry in entry.get("redistribute", []):
+                            proto_key = r_entry.get("protocol", "table")
+                            redis_dict.update({proto_key: r_entry})
+                        entry["redistribute"] = redis_dict
+
+                for af in proc.get("address_family"):
+                    af_dict.update({af["afi"]: af})
+                proc["address_family"] = af_dict
+
+            if "neighbors" in proc:
+                neigh_dict = {}
+                for entry in proc.get("neighbors", []):
+                    neigh_dict.update({entry["neighbor_address"]: entry})
+                proc["neighbors"] = neigh_dict
+                self._bgp_af_list_to_dict(proc["neighbors"])
diff --git a/plugins/module_utils/network/vyos/facts/bgp_address_family/__init__.py b/plugins/module_utils/network/vyos/facts/bgp_address_family/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py
new file mode 100644
index 00000000..a7296f24
--- /dev/null
+++ b/plugins/module_utils/network/vyos/facts/bgp_address_family/bgp_address_family.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The vyos bgp_address_family fact class
+It is in this file the configuration is collected from the device
+for a given resource, parsed, and the facts tree is populated
+based on the configuration.
+"""
+
+import re
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import (
+    utils,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.bgp_address_family import (
+    Bgp_address_familyTemplate,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import (
+    Bgp_address_familyArgs,
+)
+
+
+class Bgp_address_familyFacts(object):
+    """The vyos bgp_address_family facts class"""
+
+    def __init__(self, module, subspec="config", options="options"):
+        self._module = module
+        self.argument_spec = Bgp_address_familyArgs.argument_spec
+
+    def get_device_data(self, connection):
+        return connection.get(
+            'show configuration commands |  match "set protocols bgp"'
+        )
+
+    def populate_facts(self, connection, ansible_facts, data=None):
+        """Populate the facts for Bgp_address_family network resource
+
+        :param connection: the device connection
+        :param ansible_facts: Facts dictionary
+        :param data: previously collected conf
+
+        :rtype: dictionary
+        :returns: facts
+        """
+        facts = {}
+        objs = []
+        config_lines = []
+
+        if not data:
+            data = self.get_device_data(connection)
+
+        for resource in data.splitlines():
+            if "address-family" in resource:
+                config_lines.append(re.sub("'", "", resource))
+
+        # parse native config using the Bgp_address_family template
+        bgp_address_family_parser = Bgp_address_familyTemplate(
+            lines=config_lines
+        )
+        objs = bgp_address_family_parser.parse()
+        if objs:
+            if "address_family" in objs:
+                objs["address_family"] = list(objs["address_family"].values())
+                for af in objs["address_family"]:
+                    if "networks" in af:
+                        af["networks"] = sorted(
+                            af["networks"], key=lambda k: k["prefix"]
+                        )
+                    if "aggregate_address" in af:
+                        af["aggregate_address"] = sorted(
+                            af["aggregate_address"], key=lambda k: k["prefix"]
+                        )
+            if "neighbors" in objs:
+                objs["neighbors"] = list(objs["neighbors"].values())
+                objs["neighbors"] = sorted(
+                    objs["neighbors"], key=lambda k: k["neighbor_address"]
+                )
+                for neigh in objs["neighbors"]:
+                    if "address_family" in neigh:
+                        neigh["address_family"] = list(
+                            neigh["address_family"].values()
+                        )
+
+        ansible_facts["ansible_network_resources"].pop(
+            "bgp_address_family", None
+        )
+
+        params = utils.remove_empties(
+            utils.validate_config(self.argument_spec, {"config": objs})
+        )
+
+        facts["bgp_address_family"] = params.get("config", [])
+        ansible_facts["ansible_network_resources"].update(facts)
+
+        return ansible_facts
diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py
index 95eff2ea..1a2d7861 100644
--- a/plugins/module_utils/network/vyos/facts/facts.py
+++ b/plugins/module_utils/network/vyos/facts/facts.py
@@ -52,6 +52,9 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.ospf_
 from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_global.bgp_global import (
     Bgp_globalFacts,
 )
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.bgp_address_family.bgp_address_family import (
+    Bgp_address_familyFacts,
+)
 from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import (
     Default,
     Neighbors,
@@ -74,6 +77,7 @@ FACT_RESOURCE_SUBSETS = dict(
     ospfv2=Ospfv2Facts,
     ospf_interfaces=Ospf_interfacesFacts,
     bgp_global=Bgp_globalFacts,
+    bgp_address_family=Bgp_address_familyFacts,
 )
 
 
diff --git a/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py
new file mode 100644
index 00000000..55e22001
--- /dev/null
+++ b/plugins/module_utils/network/vyos/rm_templates/bgp_address_family.py
@@ -0,0 +1,1421 @@
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# 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
+
+"""
+The Bgp_address_family parser templates file. This contains
+a list of parser definitions and associated functions that
+facilitates both facts gathering and native command generation for
+the given network resource.
+"""
+
+import re
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network_template import (
+    NetworkTemplate,
+)
+
+
+def _tmplt_bgp_af_aggregate_address(config_data):
+    afi = config_data["address_family"]["afi"] + "-unicast"
+    command = "protocols bgp {as_number} address-family ".format(**config_data)
+    config_data = config_data["address_family"]
+    if config_data["aggregate_address"].get("as_set"):
+        command += afi + " aggregate-address {prefix} as-set".format(
+            **config_data["aggregate_address"]
+        )
+    if config_data["aggregate_address"].get("summary_only"):
+        command += afi + " aggregate-address {prefix} summary-only".format(
+            **config_data["aggregate_address"]
+        )
+    return command
+
+
+def _tmplt_bgp_af_redistribute_metric(config_data):
+    if config_data["address_family"]["redistribute"].get("metric"):
+        afi = config_data["address_family"]["afi"] + "-unicast"
+        command = "protocols bgp {as_number} address-family ".format(
+            **config_data
+        )
+        if config_data["address_family"]["redistribute"].get("metric"):
+            command += afi + " redistribute {protocol} metric {metric}".format(
+                **config_data["address_family"]["redistribute"]
+            )
+        return command
+
+
+def _tmplt_bgp_af_redistribute_route_map(config_data):
+    if config_data["address_family"]["redistribute"].get("route_map"):
+        afi = config_data["address_family"]["afi"] + "-unicast"
+        command = "protocols bgp {as_number} address-family ".format(
+            **config_data
+        )
+        if config_data["address_family"]["redistribute"].get("route_map"):
+            command += (
+                afi
+                + " redistribute {protocol} route-map {route_map}".format(
+                    **config_data["address_family"]["redistribute"]
+                )
+            )
+        return command
+
+
+def _tmplt_bgp_af_redistribute_table(config_data):
+    if config_data["address_family"]["redistribute"].get("table"):
+        afi = config_data["address_family"]["afi"] + "-unicast"
+        command = "protocols bgp {as_number} address-family ".format(
+            **config_data
+        )
+        if config_data["address_family"]["redistribute"].get("table"):
+            command += afi + " table {table}".format(
+                **config_data["address_family"]["redistribute"]
+            )
+        return command
+
+
+def _tmplt_bgp_af_delete_redistribute(config_data):
+    afi = config_data["address_family"]["afi"] + "-unicast"
+    command = "protocols bgp {as_number} address-family ".format(**config_data)
+    config_data = config_data["address_family"]
+    command += afi + " redistribute {protocol}".format(
+        **config_data["redistribute"]
+    )
+    return command
+
+
+def _tmplt_bgp_af_neighbor_distribute_list(config_data):
+    command = []
+    afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast"
+    cmd = "protocols bgp {as_number} neighbor ".format(**config_data)
+    cmd += "{neighbor_address} address-family ".format(
+        **config_data["neighbors"]
+    )
+    config_data = config_data["neighbors"]["address_family"]
+    for list_el in config_data["distribute_list"]:
+        command.append(
+            cmd
+            + afi
+            + " distribute-list "
+            + list_el["action"]
+            + " "
+            + str(list_el["acl"])
+        )
+    return command
+
+
+def _tmplt_bgp_af_neighbor_route_map(config_data):
+    command = []
+    afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast"
+    cmd = "protocols bgp {as_number} neighbor ".format(**config_data)
+    cmd += "{neighbor_address} address-family ".format(
+        **config_data["neighbors"]
+    )
+    config_data = config_data["neighbors"]["address_family"]
+    for list_el in config_data["route_map"]:
+        command.append(
+            cmd
+            + afi
+            + " route-map "
+            + list_el["action"]
+            + " "
+            + str(list_el["route_map"])
+        )
+    return command
+
+
+def _tmplt_bgp_af_neighbor_prefix_list(config_data):
+    command = []
+    afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast"
+    cmd = "protocols bgp {as_number} neighbor ".format(**config_data)
+    cmd += "{neighbor_address} address-family ".format(
+        **config_data["neighbors"]
+    )
+    config_data = config_data["neighbors"]["address_family"]
+    for list_el in config_data["prefix_list"]:
+        command.append(
+            cmd
+            + afi
+            + " prefix-list "
+            + list_el["action"]
+            + " "
+            + str(list_el["prefix_list"])
+        )
+    return command
+
+
+def _tmplt_bgp_af_neighbor_filter_list(config_data):
+    command = []
+    afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast"
+    cmd = "protocols bgp {as_number} neighbor ".format(**config_data)
+    cmd += "{neighbor_address} address-family ".format(
+        **config_data["neighbors"]
+    )
+    config_data = config_data["neighbors"]["address_family"]
+    for list_el in config_data["filter_list"]:
+        command.append(
+            cmd
+            + afi
+            + " filter-list "
+            + list_el["action"]
+            + " "
+            + str(list_el["path_list"])
+        )
+    return command
+
+
+def _tmplt_bgp_af_neighbor_attribute(config_data):
+    command = []
+    afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast"
+    cmd = "protocols bgp {as_number} neighbor ".format(**config_data)
+    cmd += "{neighbor_address} address-family ".format(
+        **config_data["neighbors"]
+    )
+    config_data = config_data["neighbors"]["address_family"]
+    for k in config_data["attribute_unchanged"].keys():
+        if config_data["attribute_unchanged"][k]:
+            k = re.sub("_", "-", k)
+            c = cmd + afi + " attribute-unchanged " + k
+            command.append(c)
+    return command
+
+
+def _tmplt_bgp_af_neighbor_delete(config_data):
+    afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast"
+    command = "protocols bgp {as_number} ".format(**config_data)
+    command += (
+        "neighbor {neighbor_address} address-family ".format(
+            **config_data["neighbors"]
+        )
+        + afi
+    )
+    config_data = config_data["neighbors"]["address_family"]
+    if config_data.get("allowas_in"):
+        command += " allowas-in"
+    elif config_data.get("as_override"):
+        command += " as-override"
+    elif config_data.get("attribute_unchanged"):
+        command += " attribute-unchanged"
+    elif config_data.get("capability"):
+        command += " capability"
+    elif config_data.get("default_originate"):
+        command += " default-originate"
+    elif config_data.get("maximum_prefix"):
+        command += " maximum-prefix"
+    elif config_data.get("nexthop_local"):
+        command += " nexthop-local"
+    elif config_data.get("nexthop_self"):
+        command += " nexthop-self"
+    elif config_data.get("peer_group"):
+        command += " peer-group"
+    elif config_data.get("remote_private_as"):
+        command += " remote-private-as"
+    elif config_data.get("route_reflector_client"):
+        command += " route-reflector-client"
+    elif config_data.get("route_server_client"):
+        command += " route-server-client"
+    elif config_data.get("soft_reconfiguration"):
+        command += " soft-reconfiguration"
+    elif config_data.get("unsuppress_map"):
+        command += " unsuppress-map"
+    elif config_data.get("weight"):
+        command += " weight"
+    elif config_data.get("filter_list"):
+        command += " filter-list"
+    elif config_data.get("prefix_list"):
+        command += " prefix-list"
+    elif config_data.get("distribute_list"):
+        command += " distribute-list"
+    elif config_data.get("route_map"):
+        command += " route-map"
+    return command
+
+
+def _tmplt_bgp_af_neighbor(config_data):
+    afi = config_data["neighbors"]["address_family"]["afi"] + "-unicast"
+    command = "protocols bgp {as_number} ".format(**config_data)
+    command += (
+        "neighbor {neighbor_address} address-family ".format(
+            **config_data["neighbors"]
+        )
+        + afi
+    )
+    config_data = config_data["neighbors"]["address_family"]
+    if config_data.get("allowas_in"):
+        command += " allowas-in number {allowas_in}".format(**config_data)
+    elif config_data.get("as_override"):
+        command += " as-override"
+    elif config_data.get("capability"):
+        command += " capability "
+        if config_data["capability"].get("dynamic"):
+            command += "dynamic"
+        elif config_data["capability"].get("orf"):
+            command += " prefix-list {orf}".format(**config_data["capability"])
+    elif config_data.get("default_originate"):
+        command += " default-originate route-map {default_originate}".format(
+            **config_data
+        )
+    elif config_data.get("maximum_prefix"):
+        command += " maximum-prefix {maximum_prefix}".format(**config_data)
+    elif config_data.get("nexthop_local"):
+        command += " nexthop-local"
+    elif config_data.get("nexthop_self"):
+        command += " nexthop-self"
+    elif config_data.get("peer_group"):
+        command += " peer-group {peer_group}".format(**config_data)
+    elif config_data.get("remote_private_as"):
+        command += " remote-private-as"
+    elif config_data.get("route_reflector_client"):
+        command += " route-reflector-client"
+    elif config_data.get("route_server_client"):
+        command += " route-server-client"
+    elif config_data.get("soft_reconfiguration"):
+        command += " soft-reconfiguration inbound"
+    elif config_data.get("unsuppress_map"):
+        command += " unsuppress-map {unsuppress_map}".format(**config_data)
+    elif config_data.get("weight"):
+        command += " weight {weight}".format(**config_data)
+    return command
+
+
+class Bgp_address_familyTemplate(NetworkTemplate):
+    def __init__(self, lines=None):
+        prefix = {"set": "set", "remove": "delete"}
+        super(Bgp_address_familyTemplate, self).__init__(
+            lines=lines, tmplt=self, prefix=prefix
+        )
+
+    # fmt: off
+    PARSERS = [
+        {
+            "name": "address_family",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast",
+            "compval": "as_number",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "address_family": {
+                    "{{ afi }}": {
+                        "afi": "{{ afi }}",
+                    }
+                }
+            }
+        },
+        {
+            "name": "aggregate_address",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+aggregate-address
+                \s+(?P<address>\S+)
+                \s*(?P<as_set>as-set)*
+                \s*(?P<summary_only>summary-only)*
+                $""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_aggregate_address,
+            "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast aggregate-address" +
+                      " {{ address_family.aggregate_address.prefix }}",
+            "compval": "address_family.aggregate_address",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "address_family": {
+                    "{{ afi }}": {
+                        "afi": "{{ afi }}",
+                        "aggregate_address": [
+                            {
+                                "prefix": "{{ address }}",
+                                "as_set": "{{ True if as_set is defined }}",
+                                "summary_only": "{{ True if summary_only is defined }}"
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "name": "network.backdoor",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+network
+                \s+(?P<address>\S+)
+                \s+backdoor
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }} backdoor",
+            "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }}",
+            "compval": "address_family.networks.backdoor",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "address_family": {
+                    "{{ afi }}": {
+                        "afi": "{{ afi }}",
+                        "networks": [
+                            {
+                                "prefix": "{{ address }}",
+                                "backdoor": "{{ True }}"
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "name": "network.path_limit",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+network
+                \s+(?P<address>\S+)
+                \s+path-limit
+                \s+(?P<limit>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network" +
+                      "{{ address_family.networks.prefix }} path-limit {{ address_family.networks.path_limit }}",
+            "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.address }}",
+            "compval": "address_family.networks.path_limit",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "address_family": {
+                    "{{ afi }}": {
+                        "afi": "{{ afi }}",
+                        "networks": [
+                            {
+                                "prefix": "{{ address }}",
+                                "path_limit": "{{ limit|int }}"
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "name": "network.route_map",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+network
+                \s+(?P<address>\S+)
+                \s+route-map
+                \s+(?P<map>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network" +
+                      " {{ address_family.networks.prefix }} route-map {{ address_family.networks.route_map }}",
+            "remval": "protocols bgp {{ as_number }} address-family {{ address_family.afi }}-unicast network {{ address_family.networks.prefix }}",
+            "compval": "address_family.networks.route_map",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "address_family": {
+                    "{{ afi }}": {
+                        "afi": "{{ afi }}",
+                        "networks": [
+                            {
+                                "prefix": "{{ address }}",
+                                "route_map": "{{ map }}"
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "name": "redistribute.metric",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+redistribute
+                \s+(?P<proto>\S+)
+                \s+metric
+                \s+(?P<val>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_redistribute_metric,
+            "remval": _tmplt_bgp_af_delete_redistribute,
+            "compval": "address_family.redistribute.metric",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "address_family": {
+                    "{{ afi }}": {
+                        "afi": "{{ afi }}",
+                        "redistribute": [
+                            {
+                                "protocol": "{{ proto }}",
+                                "metric": "{{ val|int }}"
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "name": "redistribute.route_map",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+redistribute
+                \s+(?P<proto>\S+)
+                \s+route-map
+                \s+(?P<map>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_redistribute_route_map,
+            "remval": _tmplt_bgp_af_delete_redistribute,
+            "compval": "address_family.redistribute.route_map",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "address_family": {
+                    "{{ afi }}": {
+                        "afi": "{{ afi }}",
+                        "redistribute": [
+                            {
+                                "protocol": "{{ proto }}",
+                                "route_map": "{{ map }}"
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "name": "redistribute.table",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+redistribute
+                \s+table
+                \s+(?P<tab>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_redistribute_table,
+            "remval": _tmplt_bgp_af_delete_redistribute,
+            "compval": "address_family.redistribute.table",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "address_family": {
+                    "{{ afi }}": {
+                        "afi": "{{ afi }}",
+                        "redistribute": [
+                            {
+                                "table": "{{ tab }}"
+                            }
+                        ]
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": "protocols bgp {{ as_number }} neighbor {{ neighbors.neighbor_address }} address-family",
+            "compval": "neighbors",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.address_family",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": "protocols bgp {{ as_number }} neighbor {{ neighbors.neighbor_address }} address-family {{ neighbors.address_family.afi }}-unicast",
+            "compval": "neighbors",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.allowas_in",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+allowas-in
+                \s+number
+                \s+(?P<num>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.allowas_in",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "allowas_in": "{{ num }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.as_override",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+as-override
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.as_override",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "as_override": "{{ True }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.attribute_unchanged.as_path",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+attribute-unchanged
+                \s+(?P<val>as-path)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor_attribute,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.attribute_unchanged.as_path",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "attribute_unchanged": {
+                                    "as_path": "{{ True }}"
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.attribute_unchanged.med",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+attribute-unchanged
+                \s+(?P<val>med)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor_attribute,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.attribute_unchanged.med",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "attribute_unchanged": {
+                                    "med": "{{ True }}"
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.attribute_unchanged.next_hop",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+attribute-unchanged
+                \s+(?P<val>next-hop)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor_attribute,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.attribute_unchanged.next_hop",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "attribute_unchanged": {
+                                    "next_hop": "{{ True }}"
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.capability_dynamic",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+capability
+                \s+dynamic
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.capability.dynamic",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "capability": {
+                                    "dynamic": "{{ true }}"
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.capability_orf",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+capability
+                \s+prefix-list
+                \s+(?P<orf>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.capability.orf",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "capability": {
+                                    "orf": "{{ orf }}"
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.default_originate",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+default-originate
+                \s+route-map
+                \s+(?P<map>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.default_originate",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "default_originate": "{{ map }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.distribute_list",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+distribute-list
+                \s+(?P<action>export|import)
+                \s+(?P<list>\d+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor_distribute_list,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.distribute_list",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "distribute_list": [
+                                    {
+                                        "action": "{{ action }}",
+                                        "acl": "{{ list }}"
+                                    }
+                                ]
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.prefix_list",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+prefix-list
+                \s+(?P<action>export|import)
+                \s+(?P<list>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor_prefix_list,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.prefix_list",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "prefix_list": [
+                                    {
+                                        "action": "{{ action }}",
+                                        "prefix_list": "{{ list }}"
+                                    }
+                                ]
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.filter_list",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+filter-list
+                \s+(?P<action>export|import)
+                \s+(?P<list>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor_filter_list,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.filter_list",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "filter_list": [
+                                    {
+                                        "action": "{{ action }}",
+                                        "path_list": "{{ list }}"
+                                    }
+                                ]
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.maximum_prefix",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+maximum-prefix
+                \s+(?P<num>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.maximum_prefix",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "maximum_prefix": "{{ num }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.nexthop_local",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+nexthop-local
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.nexthop_local",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "nexthop_local": "{{ True }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.nexthop_self",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+nexthop-self
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.nexthop_self",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "nexthop_self": "{{ True }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.peer_group",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+peer-group
+                \s+(?P<name>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.peer_group",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "peer_group": "{{ name }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.remove_private_as",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+remove-private-as
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.remove_private_as",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "remove_private_as": "{{ True }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.route_map",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+route-map
+                \s+(?P<action>export|import)
+                \s+(?P<map>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor_route_map,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.route_map",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "route_map": [
+                                    {
+                                        "action": "{{ action }}",
+                                        "route_map": "{{ map }}"
+                                    }
+                                ]
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.route_reflector_client",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+route-reflector-client
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.route_reflector_client",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "route_reflector_client": "{{ True }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.route_server_client",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+route-server-client
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.route_server_client",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "route_server_client": "{{ True }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.soft_reconfiguration",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+soft-reconfiguration
+                \s+inbound
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.soft_reconfiguration",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "soft_reconfiguration": "{{ True }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.unsuppress_map",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+unsuppress-map
+                \s+(?P<map>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.unsuppress_map",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "unsuppress_map": "{{ map }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        {
+            "name": "neighbors.weight",
+            "getval": re.compile(
+                r"""
+                ^set
+                \s+protocols
+                \s+bgp
+                \s+(?P<as_num>\d+)
+                \s+neighbor
+                \s+(?P<address>\S+)
+                \s+address-family
+                \s+(?P<afi>\S+)-unicast
+                \s+weight
+                \s+(?P<num>\S+)
+                *$""",
+                re.VERBOSE,
+            ),
+            "setval": _tmplt_bgp_af_neighbor,
+            "remval": _tmplt_bgp_af_neighbor_delete,
+            "compval": "neighbors.address_family.weight",
+            "result": {
+                "as_number": "{{ as_num }}",
+                "neighbors": {
+                    "{{ address }}": {
+                        "neighbor_address": "{{ address }}",
+                        "address_family": {
+                            "{{ afi }}": {
+                                "afi": "{{ afi }}",
+                                "weight": "{{ num }}"
+                            }
+                        }
+                    }
+                }
+            }
+        },
+    ]
+    # fmt: on
diff --git a/plugins/modules/vyos_bgp_address_family.py b/plugins/modules/vyos_bgp_address_family.py
new file mode 100644
index 00000000..80c68077
--- /dev/null
+++ b/plugins/modules/vyos_bgp_address_family.py
@@ -0,0 +1,1192 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 2021 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#############################################
+#                WARNING                    #
+#############################################
+#
+# This file is auto generated by the resource
+#   module builder playbook.
+#
+# Do not edit this file manually.
+#
+# Changes to this file will be over written
+#   by the resource module builder.
+#
+# Changes should be made in the model used to
+#   generate this file or in the resource module
+#   builder template.
+#
+#############################################
+
+"""
+The module file for vyos_bgp_address_family
+"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = """
+module: vyos_bgp_address_family
+version_added: 2.1.0
+short_description: BGP Address Family Resource Module.
+description:
+- This module manages BGP address family configuration of interfaces on devices running VYOS.
+author: Gomathi Selvi Srinivasan (@GomathiselviS)
+options:
+  config:
+    description: A dict of BGP global configuration for interfaces.
+    type: dict
+    suboptions:
+      as_number:
+        description:
+        - AS number.
+        type: int
+      address_family:
+        description: BGP address-family parameters.
+        type: list
+        elements: dict
+        suboptions:
+          afi:
+            description: BGP address family settings.
+            type: str
+            choices: ['ipv4', 'ipv6']
+          aggregate_address:
+            description:
+              - BGP aggregate network.
+            type: list
+            elements: dict
+            suboptions:
+              prefix:
+                description: BGP aggregate network.
+                type: str
+              as_set:
+                description: Generate AS-set path information for this aggregate address.
+                type: bool
+              summary_only:
+                description: Announce the aggregate summary network only.
+                type: bool
+          networks:
+            description: BGP network
+            type: list
+            elements: dict
+            suboptions:
+              prefix:
+                description: BGP network address
+                type: str
+              path_limit:
+                description: AS path hop count limit
+                type: int
+              backdoor:
+                description: Network as a backdoor route.
+                type: bool
+              route_map:
+                description: Route-map to modify route attributes
+                type: str
+          redistribute:
+            description: Redistribute routes from other protocols into BGP
+            type: list
+            elements: dict
+            suboptions:
+              protocol:
+                description: types of routes to be redistributed.
+                type: str
+                choices: ['connected', 'kernel', 'ospf', 'ospfv3', 'rip', 'ripng', 'static']
+              table:
+                description: Redistribute non-main Kernel Routing Table.
+                type: str
+              route_map:
+                description: Route map to filter redistributed routes
+                type: str
+              metric:
+                description: Metric for redistributed routes.
+                type: int
+      neighbors:
+        description: BGP neighbor
+        type: list
+        elements: dict
+        suboptions:
+          neighbor_address:
+            description: BGP neighbor address (v4/v6).
+            type: str
+          address_family:
+            description: address family.
+            type: list
+            elements: dict
+            suboptions:
+              afi:
+                description: BGP neighbor parameters.
+                type: str
+                choices: ['ipv4', 'ipv6']
+              allowas_in:
+                description: Number of occurrences of AS number.
+                type: int
+              as_override:
+                description:  AS for routes sent to this neighbor to be the local AS.
+                type: bool
+              attribute_unchanged:
+                description: BGP attributes are sent unchanged.
+                type: dict
+                suboptions:
+                    as_path:
+                      description: as_path attribute
+                      type: bool
+                    med:
+                      description: med attribute
+                      type: bool
+                    next_hop:
+                      description: next_hop attribute
+                      type: bool
+              capability:
+                description: Advertise capabilities to this neighbor.
+                type: dict
+                suboptions:
+                  dynamic:
+                    description: Advertise dynamic capability to this neighbor.
+                    type: bool
+                  orf:
+                    description: Advertise ORF capability to this neighbor.
+                    type: str
+                    choices: ['send', 'receive']
+              default_originate:
+                description: Send default route to this neighbor
+                type: str
+              distribute_list:
+                description:  Access-list to filter route updates to/from this neighbor.
+                type: list
+                elements: dict
+                suboptions:
+                  action:
+                    description:  Access-list to filter outgoing/incoming route updates to this neighbor
+                    type: str
+                    choices: ['export', 'import']
+                  acl:
+                    description: Acess-list number.
+                    type: int
+              filter_list:
+                description: As-path-list to filter route updates to/from this neighbor.
+                type: list
+                elements: dict
+                suboptions:
+                  action:
+                    description: filter outgoing/incoming route updates
+                    type: str
+                    choices: ['export', 'import']
+                  path_list:
+                    description: As-path-list to filter
+                    type: str
+              maximum_prefix:
+                description:  Maximum number of prefixes to accept from this neighbor
+                   nexthop-self Nexthop for routes sent to this neighbor to be the local router.
+                type: int
+              nexthop_local:
+                description:  Nexthop attributes.
+                type: bool
+              nexthop_self:
+                description:  Nexthop for routes sent to this neighbor to be the local router.
+                type: bool
+              peer_group:
+                description:  IPv4 peer group for this peer
+                type: str
+              prefix_list:
+                description: Prefix-list to filter route updates to/from this neighbor.
+                type: list
+                elements: dict
+                suboptions:
+                  action:
+                    description: filter outgoing/incoming route updates
+                    type: str
+                    choices: ['export', 'import']
+                  prefix_list:
+                    description: Prefix-list to filter
+                    type: str
+              remove_private_as:
+                description: Remove private AS numbers from AS path in outbound route updates
+                type: bool
+              route_map:
+                description: Route-map to filter route updates to/from this neighbor.
+                type: list
+                elements: dict
+                suboptions:
+                  action:
+                    description: filter outgoing/incoming route updates
+                    type: str
+                    choices: ['export', 'import']
+                  route_map:
+                    description: route-map to filter
+                    type: str
+              route_reflector_client:
+                description: Neighbor as a route reflector client
+                type: bool
+              route_server_client:
+                description: Neighbor is route server client
+                type: bool
+              soft_reconfiguration:
+                description: Soft reconfiguration for neighbor
+                type: bool
+              unsupress_map:
+                description:  Route-map to selectively unsuppress suppressed routes
+                type: str
+              weight:
+                description: Default weight for routes from this neighbor
+                type: int
+  running_config:
+    type: str
+    description:
+    - This option is used only with state I(parsed).
+    - The value of this option should be the output received from the IOS device by
+      executing the command B(show configuration command | match bgp).
+    - The state I(parsed) reads the configuration from C(running_config) option and
+      transforms it into Ansible structured data as per the resource module's argspec
+      and the value is then returned in the I(parsed) key within the result.
+  state:
+    description:
+      - The state the configuration should be left in.
+    type: str
+    choices:
+    - merged
+    - replaced
+    - deleted
+    - gathered
+    - parsed
+    - rendered
+    - purged
+    - overridden
+    default: merged
+"""
+EXAMPLES = """
+# Using merged
+# Before state
+# vyos@vyos:~$ show configuration commands |  match "set protocols bgp"
+# vyos@vyos:~$
+
+  - name: Merge provided configuration with device configuration
+    vyos.vyos.vyos_bgp_address_family:
+      config:
+        as_number: "100"
+        address_family:
+          - afi: "ipv4"
+            redistribute:
+              - protocol: "static"
+                metric: 50
+        neighbors:
+          - neighbor_address: "20.33.1.1/24"
+            address_family:
+              - afi: "ipv4"
+                allowas_in: 4
+                as_override: True
+                attribute_unchanged:
+                  med: True
+              - afi: "ipv6"
+                default_originate: "map01"
+                distribute_list:
+                  - action: "export"
+                    acl: 10
+          - neighbor_address: "100.11.34.12"
+            address_family:
+              - afi: "ipv4"
+                maximum_prefix: 45
+                nexthop_self: True
+                route_map:
+                  - action: "export"
+                    route_map: "map01"
+                  - action: "import"
+                    route_map: "map01"
+                weight: 50
+
+# After State:
+# vyos@vyos:~$ show configuration commands | match "set protocols bgp"
+# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50'
+# vyos@vyos:~$
+#
+# Module Execution:
+#
+# "after": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv4",
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 100,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "maximum_prefix": 45,
+#                         "nexthop_self": true,
+#                         "route_map": [
+#                             {
+#                                 "action": "export",
+#                                 "route_map": "map01"
+#                             },
+#                             {
+#                                 "action": "import",
+#                                 "route_map": "map01"
+#                             }
+#                         ],
+#                         "weight": 50
+#                     }
+#                 ],
+#                 "neighbor_address": "100.11.34.12"
+#             },
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "allowas_in": 4,
+#                         "as_override": true,
+#                         "attribute_unchanged": {
+#                             "med": true
+#                         }
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "default_originate": "map01",
+#                         "distribute_list": [
+#                             {
+#                                 "acl": 10,
+#                                 "action": "export"
+#                             }
+#                         ]
+#                     }
+#                 ],
+#                 "neighbor_address": "20.33.1.1/24"
+#             }
+#         ]
+#     },
+#     "before": {},
+#     "changed": true,
+#     "commands": [
+#         "set protocols bgp 100 address-family ipv4-unicast redistribute static metric 50",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number 4",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast as-override",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map map01",
+#         "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export 10",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix 45",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self",
+#         "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export map01",
+#         "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import map01",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast weight 50"
+#     ],
+#
+
+# Using replaced:
+
+# Before state:
+
+# vyos@vyos:~$ show configuration commands | match "set protocols bgp"
+# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50'
+# vyos@vyos:~$
+
+  - name: Replace provided configuration with device configuration
+    vyos.vyos.vyos_bgp_address_family:
+      config:
+        as_number: "100"
+        neighbors:
+          - neighbor_address: "100.11.34.12"
+            address_family:
+              - afi: "ipv4"
+                allowas_in: 4
+                as_override: True
+                attribute_unchanged:
+                  med: True
+              - afi: "ipv6"
+                default_originate: "map01"
+                distribute_list:
+                  - action: "export"
+                    acl: 10
+          - neighbor_address: "20.33.1.1/24"
+            address_family:
+              - afi: "ipv6"
+                maximum_prefix: 45
+                nexthop_self: True
+
+      state: replaced
+
+# After State:
+
+# vyos@vyos:~$ show configuration commands | match "set protocols bgp"
+# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix '45'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number '4'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map 'map01'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export '10'
+# vyos@vyos:~$
+#
+#
+# # Module Execution:
+# "after": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv4",
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 100,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "allowas_in": 4,
+#                         "as_override": true,
+#                         "attribute_unchanged": {
+#                             "med": true
+#                         }
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "default_originate": "map01",
+#                         "distribute_list": [
+#                             {
+#                                 "acl": 10,
+#                                 "action": "export"
+#                             }
+#                         ]
+#                     }
+#                 ],
+#                 "neighbor_address": "100.11.34.12"
+#             },
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4"
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "maximum_prefix": 45,
+#                         "nexthop_self": true
+#                     }
+#                 ],
+#                 "neighbor_address": "20.33.1.1/24"
+#             }
+#         ]
+#     },
+#     "before": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv4",
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 100,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "maximum_prefix": 45,
+#                         "nexthop_self": true,
+#                         "route_map": [
+#                             {
+#                                 "action": "export",
+#                                 "route_map": "map01"
+#                             },
+#                             {
+#                                 "action": "import",
+#                                 "route_map": "map01"
+#                             }
+#                         ],
+#                         "weight": 50
+#                     }
+#                 ],
+#                 "neighbor_address": "100.11.34.12"
+#             },
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "allowas_in": 4,
+#                         "as_override": true,
+#                         "attribute_unchanged": {
+#                             "med": true
+#                         }
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "default_originate": "map01",
+#                         "distribute_list": [
+#                             {
+#                                 "acl": 10,
+#                                 "action": "export"
+#                             }
+#                         ]
+#                     }
+#                 ],
+#                 "neighbor_address": "20.33.1.1/24"
+#             }
+#         ]
+#     },
+#     "changed": true,
+#     "commands": [
+#         "delete protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list",
+#         "delete protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate",
+#         "delete protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged",
+#         "delete protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast as-override",
+#         "delete protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast weight",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast route-map",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number 4",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast as-override",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map map01",
+#         "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export 10",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix 45",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self"
+#     ],
+
+
+# Using overridden
+# vyos@vyos:~$ show configuration commands | match "set protocols bgp"
+# set protocols bgp 100 address-family ipv4-unicast network 35.1.1.0/24 backdoor
+# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50'
+# set protocols bgp 100 address-family ipv6-unicast aggregate-address 6601:1:1:1::/64 summary-only
+# set protocols bgp 100 address-family ipv6-unicast network 5001:1:1:1::/64 route-map 'map01'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast maximum-prefix '45'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast nexthop-self
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast allowas-in number '4'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast as-override
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged med
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast default-originate route-map 'map01'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast distribute-list export '10'
+# vyos@vyos:~$
+
+  - name: Override
+    vyos.vyos.vyos_bgp_address_family:
+      config:
+        as_number: "100"
+        neighbors:
+          - neighbor_address: "100.11.34.12"
+            address_family:
+              - afi: "ipv6"
+                maximum_prefix: 45
+                nexthop_self: True
+                route_map:
+                  - action: "import"
+                    route_map: "map01"
+        address_family:
+          - afi: "ipv4"
+            aggregate_address:
+              - prefix: "60.9.2.0/24"
+                summary_only: True
+          - afi: "ipv6"
+            redistribute:
+              - protocol: "static"
+                metric: 50
+      state: overridden
+
+# Aft=validate-moduleser State
+
+# vyos@vyos:~$ show configuration commands | match "set protocols bgp"
+# set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only
+# set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50'
+# set protocols bgp 100 neighbor 20.33.1.1/24
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix '45'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import 'map01'
+# vyos@vyos:~$
+
+
+# Module Execution:
+
+# "after": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv4",
+#                 "aggregate_address": [
+#                     {
+#                         "prefix": "60.9.2.0/24",
+#                         "summary_only": true
+#                     }
+#                 ]
+#             },
+#             {
+#                 "afi": "ipv6",
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 100,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4"
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "maximum_prefix": 45,
+#                         "nexthop_self": true,
+#                         "route_map": [
+#                             {
+#                                 "action": "import",
+#                                 "route_map": "map01"
+#                             }
+#                         ]
+#                     }
+#                 ],
+#                 "neighbor_address": "100.11.34.12"
+#             }
+#         ]
+#     },
+#     "before": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv4",
+#                 "networks": [
+#                     {
+#                         "backdoor": true,
+#                         "prefix": "35.1.1.0/24"
+#                     }
+#                 ],
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             },
+#             {
+#                 "afi": "ipv6",
+#                 "aggregate_address": [
+#                     {
+#                         "prefix": "6601:1:1:1::/64",
+#                         "summary_only": true
+#                     }
+#                 ],
+#                 "networks": [
+#                     {
+#                         "prefix": "5001:1:1:1::/64",
+#                         "route_map": "map01"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 100,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "allowas_in": 4,
+#                         "as_override": true,
+#                         "attribute_unchanged": {
+#                             "med": true
+#                         }
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "default_originate": "map01",
+#                         "distribute_list": [
+#                             {
+#                                 "acl": 10,
+#                                 "action": "export"
+#                             }
+#                         ]
+#                     }
+#                 ],
+#                 "neighbor_address": "100.11.34.12"
+#             },
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4"
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "maximum_prefix": 45,
+#                         "nexthop_self": true
+#                     }
+#                 ],
+#                 "neighbor_address": "20.33.1.1/24"
+#             }
+#         ]
+#     },
+#     "changed": true,
+#     "commands": [
+#         "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv6-unicast distribute-list",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv6-unicast default-originate",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast attribute-unchanged",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast as-override",
+#         "delete protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast allowas-in",
+#         "delete protocols bgp 100 address-family ipv6 aggregate-address",
+#         "delete protocols bgp 100 address-family ipv6 network",
+#         "delete protocols bgp 100 address-family ipv4 network",
+#         "delete protocols bgp 100 address-family ipv4 redistribute",
+#         "set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only",
+#         "set protocols bgp 100 address-family ipv6-unicast redistribute static metric 50",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix 45",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self",
+#         "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import map01"
+#     ],
+#
+
+# Using deleted:
+
+# Before State:
+
+# vyos@vyos:~$ show configuration commands | match "set protocols bgp"
+# set protocols bgp 100 address-family ipv4-unicast aggregate-address 60.9.2.0/24 summary-only
+# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50'
+# set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map 'map01'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export '10'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix '45'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export 'map01'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import 'map01'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast weight '50'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast maximum-prefix '45'
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast nexthop-self
+# set protocols bgp 100 neighbor 100.11.34.12 address-family ipv6-unicast route-map import 'map01'
+# vyos@vyos:~$
+
+  - name: Delete
+    vyos.vyos.vyos_bgp_address_family:
+      config:
+        as_number: "100"
+        neighbors:
+          - neighbor_address: "20.33.1.1/24"
+            address_family:
+              - afi: "ipv6"
+          - neighbor_address: "100.11.34.12"
+        address_family:
+          - afi: "ipv4"
+      state: deleted
+
+
+# After State:
+
+# vyos@vyos:~$ show configuration commands | match "set protocols bgp"
+# set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med
+# set protocols bgp 100 neighbor 100.11.34.12
+# vyos@vyos:~$
+#
+#
+# Module Execution:
+#
+# "after": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv6",
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 100,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "allowas_in": 4,
+#                         "as_override": true,
+#                         "attribute_unchanged": {
+#                             "med": true
+#                         }
+#                     }
+#                 ],
+#                 "neighbor_address": "20.33.1.1/24"
+#             }
+#         ]
+#     },
+#     "before": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv4",
+#                 "aggregate_address": [
+#                     {
+#                         "prefix": "60.9.2.0/24",
+#                         "summary_only": true
+#                     }
+#                 ],
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             },
+#             {
+#                 "afi": "ipv6",
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 100,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "maximum_prefix": 45,
+#                         "nexthop_self": true,
+#                         "route_map": [
+#                             {
+#                                 "action": "export",
+#                                 "route_map": "map01"
+#                             },
+#                             {
+#                                 "action": "import",
+#                                 "route_map": "map01"
+#                             }
+#                         ],
+#                         "weight": 50
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "maximum_prefix": 45,
+#                         "nexthop_self": true,
+#                         "route_map": [
+#                             {
+#                                 "action": "import",
+#                                 "route_map": "map01"
+#                             }
+#                         ]
+#                     }
+#                 ],
+#                 "neighbor_address": "100.11.34.12"
+#             },
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "allowas_in": 4,
+#                         "as_override": true,
+#                         "attribute_unchanged": {
+#                             "med": true
+#                         }
+#                     },
+#                     {
+#                         "afi": "ipv6",
+#                         "default_originate": "map01",
+#                         "distribute_list": [
+#                             {
+#                                 "acl": 10,
+#                                 "action": "export"
+#                             }
+#                         ]
+#                     }
+#                 ],
+#                 "neighbor_address": "20.33.1.1/24"
+#             }
+#         ]
+#     },
+#     "changed": true,
+#     "commands": [
+#         "delete protocols bgp 100 address-family ipv4-unicast",
+#         "delete protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast",
+#         "delete protocols bgp 100 neighbor 100.11.34.12 address-family"
+#     ],
+#
+
+# using parsed:
+
+# parsed.cfg
+# set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set
+# set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map 'map01'
+# set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor
+# set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20'
+# set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export 'map01'
+# set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound
+# set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop
+
+
+  - name: parse configs
+    vyos.vyos.vyos_bgp_address_family:
+      running_config: "{{ lookup('file', './parsed.cfg') }}"
+      state: parsed
+
+# Module Execution:
+# "parsed": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv4",
+#                 "aggregate_address": [
+#                     {
+#                         "as_set": true,
+#                         "prefix": "192.0.2.0/24"
+#                     }
+#                 ],
+#                 "networks": [
+#                     {
+#                         "prefix": "192.1.13.0/24",
+#                         "route_map": "map01"
+#                     },
+#                     {
+#                         "backdoor": true,
+#                         "prefix": "192.2.13.0/24"
+#                     }
+#                 ]
+#             },
+#             {
+#                 "afi": "ipv6",
+#                 "redistribute": [
+#                     {
+#                         "metric": 20,
+#                         "protocol": "ripng"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 65536,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "route_map": [
+#                             {
+#                                 "action": "export",
+#                                 "route_map": "map01"
+#                             }
+#                         ],
+#                         "soft_reconfiguration": true
+#                     }
+#                 ],
+#                 "neighbor_address": "192.0.2.25"
+#             },
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv6",
+#                         "attribute_unchanged": {
+#                             "next_hop": true
+#                         }
+#                     }
+#                 ],
+#                 "neighbor_address": "203.0.113.5"
+#             }
+#         ]
+#
+
+# Using gathered:
+
+# Native config:
+
+# vyos@vyos:~$ show configuration commands | match "set protocols bgp"
+# set protocols bgp 100 address-family ipv4-unicast network 35.1.1.0/24 backdoor
+# set protocols bgp 100 address-family ipv4-unicast redistribute static metric '50'
+# set protocols bgp 100 address-family ipv6-unicast aggregate-address 6601:1:1:1::/64 summary-only
+# set protocols bgp 100 address-family ipv6-unicast network 5001:1:1:1::/64 route-map 'map01'
+# set protocols bgp 100 address-family ipv6-unicast redistribute static metric '50'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number '4'
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast as-override
+# set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med
+# set protocols bgp 100 neighbor 100.11.34.12
+
+  - name: gather configs
+    vyos.vyos.vyos_bgp_address_family:
+      state: gathered
+
+# Module Execution:
+
+# "gathered": {
+#         "address_family": [
+#             {
+#                 "afi": "ipv4",
+#                 "networks": [
+#                     {
+#                         "backdoor": true,
+#                         "prefix": "35.1.1.0/24"
+#                     }
+#                 ],
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             },
+#             {
+#                 "afi": "ipv6",
+#                 "aggregate_address": [
+#                     {
+#                         "prefix": "6601:1:1:1::/64",
+#                         "summary_only": true
+#                     }
+#                 ],
+#                 "networks": [
+#                     {
+#                         "prefix": "5001:1:1:1::/64",
+#                         "route_map": "map01"
+#                     }
+#                 ],
+#                 "redistribute": [
+#                     {
+#                         "metric": 50,
+#                         "protocol": "static"
+#                     }
+#                 ]
+#             }
+#         ],
+#         "as_number": 100,
+#         "neighbors": [
+#             {
+#                 "address_family": [
+#                     {
+#                         "afi": "ipv4",
+#                         "allowas_in": 4,
+#                         "as_override": true,
+#                         "attribute_unchanged": {
+#                             "med": true
+#                         }
+#                     }
+#                 ],
+#                 "neighbor_address": "20.33.1.1/24"
+#             }
+#         ]
+
+# Using rendered:
+
+  - name: Render
+    vyos.vyos.vyos_bgp_address_family:
+      config:
+        as_number: "100"
+        address_family:
+          - afi: "ipv4"
+            redistribute:
+              - protocol: "static"
+                metric: 50
+        neighbors:
+          - neighbor_address: "20.33.1.1/24"
+            address_family:
+              - afi: "ipv4"
+                allowas_in: 4
+                as_override: True
+                attribute_unchanged:
+                  med: True
+              - afi: "ipv6"
+                default_originate: "map01"
+                distribute_list:
+                  - action: "export"
+                    acl: 10
+          - neighbor_address: "100.11.34.12"
+            address_family:
+              - afi: "ipv4"
+                maximum_prefix: 45
+                nexthop_self: True
+                route_map:
+                  - action: "export"
+                    route_map: "map01"
+                  - action: "import"
+                    route_map: "map01"
+                weight: 50
+      state: rendered
+
+# Module Execution:
+
+# "rendered": [
+#         "set protocols bgp 100 address-family ipv4-unicast redistribute static metric 50",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast allowas-in number 4",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast as-override",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv4-unicast attribute-unchanged med",
+#         "set protocols bgp 100  neighbor 20.33.1.1/24 address-family ipv6-unicast default-originate route-map map01",
+#         "set protocols bgp 100 neighbor 20.33.1.1/24 address-family ipv6-unicast distribute-list export 10",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast maximum-prefix 45",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast nexthop-self",
+#         "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map export map01",
+#         "set protocols bgp 100 neighbor 100.11.34.12 address-family ipv4-unicast route-map import map01",
+#         "set protocols bgp 100  neighbor 100.11.34.12 address-family ipv4-unicast weight 50"
+#     ]
+
+
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.bgp_address_family.bgp_address_family import (
+    Bgp_address_familyArgs,
+)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.bgp_address_family.bgp_address_family import (
+    Bgp_address_family,
+)
+
+
+def main():
+    """
+    Main entry point for module execution
+
+    :returns: the result form module invocation
+    """
+    module = AnsibleModule(
+        argument_spec=Bgp_address_familyArgs.argument_spec,
+        mutually_exclusive=[],
+        required_if=[],
+        supports_check_mode=False,
+    )
+
+    result = Bgp_address_family(module).execute_module()
+    module.exit_json(**result)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/integration/targets/vyos_bgp_address_family/defaults/main.yaml b/tests/integration/targets/vyos_bgp_address_family/defaults/main.yaml
new file mode 100644
index 00000000..852a6bee
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: '[^_].*'
+test_items: []
diff --git a/tests/integration/targets/vyos_bgp_address_family/meta/main.yaml b/tests/integration/targets/vyos_bgp_address_family/meta/main.yaml
new file mode 100644
index 00000000..7413320e
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/meta/main.yaml
@@ -0,0 +1,3 @@
+---
+dependencies:
+  - prepare_vyos_tests
diff --git a/tests/integration/targets/vyos_bgp_address_family/tasks/cli.yaml b/tests/integration/targets/vyos_bgp_address_family/tasks/cli.yaml
new file mode 100644
index 00000000..93eb2fe4
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tasks/cli.yaml
@@ -0,0 +1,19 @@
+---
+- name: Collect all cli test cases
+  find:
+    paths: '{{ role_path }}/tests/cli'
+    patterns: '{{ testcase }}.yaml'
+    use_regex: true
+  register: test_cases
+  delegate_to: localhost
+
+- name: Set test_items
+  set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: Run test case (connection=ansible.netcommon.network_cli)
+  include: '{{ test_case_to_run }}'
+  vars:
+    ansible_connection: ansible.netcommon.network_cli
+  with_items: '{{ test_items }}'
+  loop_control:
+    loop_var: test_case_to_run
diff --git a/tests/integration/targets/vyos_bgp_address_family/tasks/main.yaml b/tests/integration/targets/vyos_bgp_address_family/tasks/main.yaml
new file mode 100644
index 00000000..b957d2f4
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tasks/main.yaml
@@ -0,0 +1,4 @@
+---
+- include: cli.yaml
+  tags:
+    - network_cli
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_parsed_config.cfg
new file mode 100644
index 00000000..2aecc805
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_parsed_config.cfg
@@ -0,0 +1,9 @@
+set policy access-list 20 description 'acl20'
+set policy access-list 40 description 'acl40'
+set policy route-map map01 description 'map01'
+set protocols bgp 65536 address-family ipv6-unicast aggregate-address 5000:1:1::/64 summary-only
+set protocols bgp 65536 address-family ipv6-unicast network 21e0:1:1::/64 route-map 'map01'
+set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20'
+set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast route-map export 'map01'
+set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast soft-reconfiguration inbound
+set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/_populate.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_populate.yaml
new file mode 100644
index 00000000..3aca9161
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_populate.yaml
@@ -0,0 +1,17 @@
+---
+- name: setup
+  vyos.vyos.vyos_config:
+    lines:
+      - set policy access-list 20 description 'acl20'
+      - set policy access-list 40 description 'acl40'
+      - set policy access-list6 10 description 'acl10'
+      - set policy route-map map01 description 'map01'
+      - set protocols bgp 65536 address-family ipv6-unicast aggregate-address 5000:1:1::/64 summary-only
+      - set protocols bgp 65536 address-family ipv6-unicast network 21e0:1:1::/64 route-map 'map01'
+      - set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20'
+      - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast route-map export 'map01'
+      - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast soft-reconfiguration inbound
+      - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop
+  ignore_errors: true
+  vars:
+    ansible_connection: ansible.netcommon.network_cli
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/_preconfig.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_preconfig.yaml
new file mode 100644
index 00000000..5024a70f
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_preconfig.yaml
@@ -0,0 +1,11 @@
+---
+- name: setup
+  vyos.vyos.vyos_config:
+    lines:
+      - set policy access-list 20 description 'acl20'
+      - set policy access-list 40 description 'acl40'
+      - set policy access-list6 10 description 'acl10'
+      - set policy route-map map01 description 'map01'
+  ignore_errors: true
+  vars:
+    ansible_connection: ansible.netcommon.network_cli
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_remove_config.yaml
new file mode 100644
index 00000000..39d8bd02
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/_remove_config.yaml
@@ -0,0 +1,12 @@
+---
+- name: Remove pre-existing bgp processes
+  vyos.vyos.vyos_config:
+    lines:
+      - delete protocols bgp 65536
+      - delete policy access-list 20
+      - delete policy access-list 40
+      - delete policy access-list6 10
+      - delete policy route-map map01
+  ignore_errors: true
+  vars:
+    ansible_connection: ansible.netcommon.network_cli
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/deleted.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/deleted.yaml
new file mode 100644
index 00000000..7fb8e109
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/deleted.yaml
@@ -0,0 +1,49 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family deleted integration tests on connection={{
+      ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+    - name: Delete the provided configuration
+      register: result
+      vyos.vyos.vyos_bgp_address_family: &id001
+        config:
+          as_number: "65536"
+          address_family:
+            - afi: "ipv6"
+          neighbors:
+            - neighbor_address: "203.0.113.5"
+            - neighbor_address: "192.0.2.25"
+              address_family:
+                - afi: "ipv6"
+        state: deleted
+
+    - become: true
+      vyos.vyos.vyos_facts:
+        gather_network_resources: bgp_address_family
+
+    - assert:
+        that:
+          - result.commands|length == 3
+          - result.changed == true
+          - result.commands|symmetric_difference(deleted.commands) == []
+          - result.after == ansible_facts['network_resources']['bgp_address_family']
+
+    - name: Delete the existing configuration with the provided running configuration
+        (IDEMPOTENT)
+      register: result
+      vyos.vyos.vyos_bgp_address_family: *id001
+
+    - name: Assert that the previous task was idempotent
+      assert:
+        that:
+          - result['changed'] == false
+
+  always:
+
+    - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/empty_config.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/empty_config.yaml
new file mode 100644
index 00000000..3a12be92
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/empty_config.yaml
@@ -0,0 +1,60 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family empty_config integration tests on connection={{
+      ansible_connection }}
+
+- name: Merged with empty config should give appropriate error message
+  register: result
+  ignore_errors: true
+  vyos.vyos.vyos_bgp_address_family:
+    config:
+    state: merged
+
+- assert:
+    that:
+      - result.msg == 'value of config parameter must not be empty for state merged'
+
+- name: Replaced with empty config should give appropriate error message
+  register: result
+  ignore_errors: true
+  vyos.vyos.vyos_bgp_address_family:
+    config:
+    state: replaced
+
+- assert:
+    that:
+      - result.msg == 'value of config parameter must not be empty for state replaced'
+
+- name: OVerridden with empty config should give appropriate error message
+  register: result
+  ignore_errors: true
+  vyos.vyos.vyos_bgp_address_family:
+    config:
+    state: overridden
+
+- assert:
+    that:
+      - result.msg == 'value of config parameter must not be empty for state overridden'
+
+- name: Parsed with empty running_config should give appropriate error message
+  register: result
+  ignore_errors: true
+  vyos.vyos.vyos_bgp_address_family:
+    running_config:
+    state: parsed
+
+- assert:
+    that:
+      - result.msg == 'value of running_config parameter must not be empty for state
+        parsed'
+
+- name: Rendered with empty config should give appropriate error message
+  register: result
+  ignore_errors: true
+  vyos.vyos.vyos_bgp_address_family:
+    config:
+    state: rendered
+
+- assert:
+    that:
+      - result.msg == 'value of config parameter must not be empty for state rendered'
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/gathered.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/gathered.yaml
new file mode 100644
index 00000000..7176a488
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/gathered.yaml
@@ -0,0 +1,24 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family gathered integration tests on connection={{
+      ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+    - name: Gather config from the device in structured format.
+      register: result
+      vyos.vyos.vyos_bgp_address_family:
+        state: gathered
+
+    - become: true
+      vyos.vyos.vyos_facts:
+        gather_network_resources: bgp_address_family
+
+    - assert:
+        that:
+          - result.changed == false
+          - result.gathered == ansible_facts['network_resources']['bgp_address_family']
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/merged.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/merged.yaml
new file mode 100644
index 00000000..45870c6b
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/merged.yaml
@@ -0,0 +1,70 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family merged integration tests on connection={{
+      ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _preconfig.yaml
+
+- block:
+
+    - name: Merge the provided configuration with the exisiting running configuration
+      register: result
+      vyos.vyos.vyos_bgp_address_family: &id001
+        config:
+          as_number: "65536"
+          address_family:
+            - afi: "ipv6"
+              redistribute:
+                - protocol: "static"
+                  metric: 50
+          neighbors:
+            - neighbor_address: "203.0.113.5"
+              address_family:
+                - afi: "ipv6"
+                  allowas_in: 4
+                  attribute_unchanged:
+                    med: true
+                  default_originate: "map01"
+                  distribute_list:
+                    - action: "export"
+                      acl: 10
+            - neighbor_address: "192.0.2.25"
+              address_family:
+                - afi: "ipv6"
+                  maximum_prefix: 45
+                  nexthop_self: true
+                  route_map:
+                    - action: "export"
+                      route_map: "map01"
+                    - action: "import"
+                      route_map: "map01"
+        state: merged
+
+    - become: true
+      vyos.vyos.vyos_facts:
+        gather_network_resources: bgp_address_family
+
+    - assert:
+        that:
+          - result.commands|length == 9
+          - result.changed == true
+          - result.commands|symmetric_difference(merged.commands) == []
+          - result.after == ansible_facts['network_resources']['bgp_address_family']
+          - result.before == {}
+          - result.after == merged.after
+
+    - name: Merge the provided configuration with the existing running configuration
+        (IDEMPOTENT)
+      register: result
+      vyos.vyos.vyos_bgp_address_family: *id001
+
+    - name: Assert that the previous task was idempotent
+      assert:
+        that:
+          - result['changed'] == false
+
+  always:
+
+    - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/overridden.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/overridden.yaml
new file mode 100644
index 00000000..77ba5b49
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/overridden.yaml
@@ -0,0 +1,58 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family overridden integration tests on connection={{
+      ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+    - name: Replace the existing configuration with the provided running configuration
+      register: result
+      vyos.vyos.vyos_bgp_address_family: &id001
+        config:
+          as_number: "65536"
+          address_family:
+            - afi: "ipv6"
+              redistribute:
+                - protocol: "static"
+                  metric: 50
+          neighbors:
+            - neighbor_address: "203.0.113.5"
+              address_family:
+                - afi: "ipv6"
+                  allowas_in: 4
+                  attribute_unchanged:
+                    med: true
+                  default_originate: "map01"
+                  distribute_list:
+                    - action: "export"
+                      acl: 10
+        state: overridden
+
+    - become: true
+      vyos.vyos.vyos_facts:
+        gather_network_resources: bgp_address_family
+
+    - assert:
+        that:
+          - result.commands|length == 10
+          - result.changed == true
+          - result.commands|symmetric_difference(overridden.commands) == []
+          - result.after == ansible_facts['network_resources']['bgp_address_family']
+
+    - name: Replace the existing configuration with the provided running configuration
+        (IDEMPOTENT)
+      register: result
+      vyos.vyos.vyos_bgp_address_family: *id001
+
+    - name: Assert that the previous task was idempotent
+      assert:
+        that:
+          - result['changed'] == false
+
+  always:
+
+    - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/parsed.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/parsed.yaml
new file mode 100644
index 00000000..d20684f0
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/parsed.yaml
@@ -0,0 +1,16 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family parsed integration tests on connection={{ ansible_connection
+      }}
+
+- name: Provide the running configuration for parsing (config to be parsed)
+  become: true
+  register: result
+  vyos.vyos.vyos_bgp_address_family:
+    running_config: "{{ lookup('file', '_parsed_config.cfg') }}"
+    state: parsed
+
+- assert:
+    that:
+      - result.changed == false
+      - result.parsed == populate.config
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/rendered.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/rendered.yaml
new file mode 100644
index 00000000..1071657c
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/rendered.yaml
@@ -0,0 +1,44 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family rendered integration tests on connection={{
+      ansible_connection }}
+
+- block:
+
+    - name: Render the given config in the form of native config.
+      register: result
+      vyos.vyos.vyos_bgp_address_family: &id001
+        config:
+          as_number: "65536"
+          address_family:
+            - afi: "ipv6"
+              redistribute:
+                - protocol: "static"
+                  metric: 50
+          neighbors:
+            - neighbor_address: "203.0.113.5"
+              address_family:
+                - afi: "ipv6"
+                  allowas_in: 4
+                  attribute_unchanged:
+                    med: true
+                  default_originate: "map01"
+                  distribute_list:
+                    - action: "export"
+                      acl: 10
+            - neighbor_address: "192.0.2.25"
+              address_family:
+                - afi: "ipv6"
+                  maximum_prefix: 45
+                  nexthop_self: true
+                  route_map:
+                    - action: "export"
+                      route_map: "map01"
+                    - action: "import"
+                      route_map: "map01"
+        state: rendered
+
+    - assert:
+        that:
+          - result.changed == false
+          - result.rendered|symmetric_difference(merged.commands) == []
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/replaced.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/replaced.yaml
new file mode 100644
index 00000000..001c9971
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/replaced.yaml
@@ -0,0 +1,58 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family replaced integration tests on connection={{
+      ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+
+    - name: Replace the existing configuration with the provided running configuration
+      register: result
+      vyos.vyos.vyos_bgp_address_family: &id001
+        config:
+          as_number: "65536"
+          address_family:
+            - afi: "ipv6"
+              redistribute:
+                - protocol: "static"
+                  metric: 50
+          neighbors:
+            - neighbor_address: "203.0.113.5"
+              address_family:
+                - afi: "ipv6"
+                  allowas_in: 4
+                  attribute_unchanged:
+                    med: true
+                  default_originate: "map01"
+                  distribute_list:
+                    - action: "export"
+                      acl: 10
+        state: replaced
+
+    - become: true
+      vyos.vyos.vyos_facts:
+        gather_network_resources: bgp_address_family
+
+    - assert:
+        that:
+          - result.commands|length == 9
+          - result.changed == true
+          - result.commands|symmetric_difference(replaced.commands) == []
+          - result.after == ansible_facts['network_resources']['bgp_address_family']
+
+    - name: Replace the existing configuration with the provided running configuration
+        (IDEMPOTENT)
+      register: result
+      vyos.vyos.vyos_bgp_address_family: *id001
+
+    - name: Assert that the previous task was idempotent
+      assert:
+        that:
+          - result['changed'] == false
+
+  always:
+
+    - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_bgp_address_family/tests/cli/rtt.yaml b/tests/integration/targets/vyos_bgp_address_family/tests/cli/rtt.yaml
new file mode 100644
index 00000000..3ef93c40
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/tests/cli/rtt.yaml
@@ -0,0 +1,90 @@
+---
+- debug:
+    msg: START vyos_bgp_address_family rtt integration tests on connection={{
+      ansible_connection }}
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _preconfig.yaml
+
+- block:
+
+    - name: Merge the provided configuration with the exisiting running configuration
+      register: baseconfig
+      vyos.vyos.vyos_bgp_address_family:
+        config:
+          as_number: "65536"
+          address_family:
+            - afi: "ipv6"
+              redistribute:
+                - protocol: "static"
+                  metric: 50
+          neighbors:
+            - neighbor_address: "203.0.113.5"
+              address_family:
+                - afi: "ipv6"
+                  allowas_in: 4
+                  attribute_unchanged:
+                    med: true
+                  default_originate: "map01"
+                  distribute_list:
+                    - action: "export"
+                      acl: 10
+            - neighbor_address: "192.0.2.25"
+              address_family:
+                - afi: "ipv6"
+                  maximum_prefix: 45
+                  nexthop_self: true
+                  route_map:
+                    - action: "export"
+                      route_map: "map01"
+                    - action: "import"
+                      route_map: "map01"
+        state: merged
+
+    - become: true
+      vyos.vyos.vyos_facts:
+        gather_network_resources: bgp_address_family
+
+    - assert:
+        that:
+          - baseconfig.commands|length == 9
+          - baseconfig.changed == true
+          - baseconfig.commands|symmetric_difference(merged.commands) == []
+          - baseconfig.after == ansible_facts['network_resources']['bgp_address_family']
+
+    - name: Apply the provided configuration (config to be reverted)
+      become: true
+      register: result
+      vyos.vyos.vyos_bgp_address_family:
+        config:
+          as_number: "65536"
+          address_family:
+            - afi: "ipv6"
+              aggregate_address:
+                - summary_only: true
+                  prefix: "21e0:1:1::/64"
+              networks:
+                - prefix: "21e0:1:1::/64"
+                  route_map: "map01"
+          neighbors:
+            - address_family:
+                - afi: "ipv6"
+                  remove_private_as: true
+              neighbor_address: "203.0.113.5"
+
+    - name: Revert back to base config using facts round trip
+      become: true
+      register: revert
+      vyos.vyos.vyos_bgp_address_family:
+        config: "{{ ansible_facts['network_resources']['bgp_address_family'] }}"
+        state: overridden
+
+    - name: Assert that config was reverted
+      assert:
+        that: baseconfig.after == revert.after
+
+
+  always:
+
+    - include_tasks: _remove_config.yaml
diff --git a/tests/integration/targets/vyos_bgp_address_family/vars/main.yaml b/tests/integration/targets/vyos_bgp_address_family/vars/main.yaml
new file mode 100644
index 00000000..aed6de63
--- /dev/null
+++ b/tests/integration/targets/vyos_bgp_address_family/vars/main.yaml
@@ -0,0 +1,99 @@
+---
+merged:
+  commands:
+    - set protocols bgp 65536 address-family ipv6-unicast redistribute static metric 50
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast allowas-in number 4
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged med
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast default-originate route-map map01
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast distribute-list export 10
+    - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast maximum-prefix 45
+    - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast nexthop-self
+    - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast route-map export map01
+    - set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast route-map import map01
+  after:
+    address_family:
+      - afi: "ipv6"
+        redistribute:
+          - metric: 50
+            protocol: "static"
+    as_number: 65536
+    neighbors:
+      - address_family:
+          - afi: "ipv6"
+            maximum_prefix: 45
+            nexthop_self: true
+            route_map:
+              - action: "export"
+                route_map: "map01"
+              - action: "import"
+                route_map: "map01"
+        neighbor_address: "192.0.2.25"
+      - address_family:
+          - afi: "ipv6"
+            allowas_in: 4
+            attribute_unchanged:
+              med: true
+            default_originate: "map01"
+            distribute_list:
+              - acl: 10
+                action: "export"
+        neighbor_address: "203.0.113.5"
+
+replaced:
+  commands:
+    - delete protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged
+    - delete protocols bgp 65536 address-family ipv6 aggregate-address
+    - delete protocols bgp 65536 address-family ipv6 network
+    - delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng
+    - set protocols bgp 65536 address-family ipv6-unicast redistribute static metric 50
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast allowas-in number 4
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged med
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast default-originate route-map map01
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast distribute-list export 10
+
+overridden:
+  commands:
+    - delete protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged
+    - delete protocols bgp 65536 neighbor 192.0.2.25 address-family
+    - delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng
+    - delete protocols bgp 65536 address-family ipv6 aggregate-address
+    - delete protocols bgp 65536 address-family ipv6 network
+    - set protocols bgp 65536 address-family ipv6-unicast redistribute static metric 50
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast allowas-in number 4
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged med
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast default-originate route-map map01
+    - set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast distribute-list export 10
+
+deleted:
+  commands:
+    - delete protocols bgp 65536 address-family ipv6-unicast
+    - delete protocols bgp 65536 neighbor 203.0.113.5 address-family
+    - delete protocols bgp 65536 neighbor 192.0.2.25 address-family ipv6-unicast
+
+populate:
+  config:
+    address_family:
+      - afi: "ipv6"
+        aggregate_address:
+          - summary_only: true
+            prefix: "5000:1:1::/64"
+        networks:
+          - prefix: "21e0:1:1::/64"
+            route_map: "map01"
+        redistribute:
+          - metric: 20
+            protocol: "ripng"
+    as_number: 65536
+    neighbors:
+      - address_family:
+          - afi: "ipv6"
+            route_map:
+              - action: "export"
+                route_map: "map01"
+            soft_reconfiguration: true
+        neighbor_address: "192.0.2.25"
+      - address_family:
+          - afi: "ipv6"
+            attribute_unchanged:
+              next_hop: true
+        neighbor_address: "203.0.113.5"
diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_bgp_address_family_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_bgp_address_family_config.cfg
new file mode 100644
index 00000000..a59878f3
--- /dev/null
+++ b/tests/unit/modules/network/vyos/fixtures/vyos_bgp_address_family_config.cfg
@@ -0,0 +1,7 @@
+set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set
+set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map 'map01'
+set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor
+set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric '20'
+set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export 'map01'
+set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound
+set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop
diff --git a/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py b/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py
new file mode 100644
index 00000000..5fc7e358
--- /dev/null
+++ b/tests/unit/modules/network/vyos/test_vyos_bgp_address_family.py
@@ -0,0 +1,684 @@
+# (c) 2016 Red Hat Inc.
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch
+from ansible_collections.vyos.vyos.plugins.modules import (
+    vyos_bgp_address_family,
+)
+from ansible_collections.vyos.vyos.tests.unit.modules.utils import (
+    set_module_args,
+)
+from .vyos_module import TestVyosModule, load_fixture
+
+
+class TestVyosBgpafModule(TestVyosModule):
+
+    module = vyos_bgp_address_family
+
+    def setUp(self):
+        super(TestVyosBgpafModule, self).setUp()
+        self.mock_get_resource_connection_config = patch(
+            "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module_base.get_resource_connection"
+        )
+        self.get_resource_connection_config = (
+            self.mock_get_resource_connection_config.start()
+        )
+
+        self.mock_execute_show_command = patch(
+            "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts."
+            + "bgp_address_family.bgp_address_family.Bgp_address_familyFacts.get_device_data"
+        )
+        self.execute_show_command = self.mock_execute_show_command.start()
+
+    def tearDown(self):
+        super(TestVyosBgpafModule, self).tearDown()
+        self.mock_get_resource_connection_config.stop()
+        self.mock_execute_show_command.stop()
+
+    def load_fixtures(self, commands=None, transport="cli", filename=None):
+        if filename is None:
+            filename = "vyos_bgp_address_family_config.cfg"
+
+        def load_from_file(*args, **kwargs):
+            output = load_fixture(filename)
+            return output
+
+        self.execute_show_command.side_effect = load_from_file
+
+    def test_vyos_bgp_address_family_merged_idempotent(self):
+        set_module_args(
+            dict(
+                config=dict(
+                    as_number=65536,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                            aggregate_address=[
+                                dict(prefix="192.0.2.0/24", as_set=True)
+                            ],
+                            networks=[
+                                dict(
+                                    prefix="192.1.13.0/24", route_map="map01"
+                                ),
+                                dict(prefix="192.2.13.0/24", backdoor=True),
+                            ],
+                        ),
+                        dict(
+                            afi="ipv6",
+                            redistribute=[dict(protocol="ripng", metric=20)],
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.0.2.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    route_map=[
+                                        dict(
+                                            action="export", route_map="map01"
+                                        )
+                                    ],
+                                    soft_reconfiguration=True,
+                                ),
+                            ],
+                        ),
+                        dict(
+                            neighbor_address="203.0.113.5",
+                            address_family=[
+                                dict(
+                                    afi="ipv6",
+                                    attribute_unchanged=dict(next_hop=True),
+                                )
+                            ],
+                        ),
+                    ],
+                )
+            )
+        )
+        self.execute_module(changed=False, commands=[])
+
+    def test_vyos_bgp_address_family_merged(self):
+        set_module_args(
+            dict(
+                config=dict(
+                    as_number=65536,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                            aggregate_address=[
+                                dict(prefix="192.0.2.0/24", summary_only=True)
+                            ],
+                            networks=[
+                                dict(
+                                    prefix="192.1.13.0/24", route_map="map01"
+                                ),
+                            ],
+                        ),
+                        dict(
+                            afi="ipv6",
+                            redistribute=[dict(protocol="ospfv3", metric=20)],
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.10.21.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv6",
+                                    distribute_list=[
+                                        dict(action="export", acl=10)
+                                    ],
+                                    route_server_client=True,
+                                ),
+                            ],
+                        ),
+                        dict(
+                            neighbor_address="203.0.113.5",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    filter_list=[
+                                        dict(
+                                            action="export", path_list="list01"
+                                        ),
+                                    ],
+                                    capability=dict(orf="send"),
+                                )
+                            ],
+                        ),
+                    ],
+                )
+            )
+        )
+        commands = [
+            "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-setipv4-unicast aggregate-address 192.0.2.0/24 summary-only",
+            "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20",
+            "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast filter-list export list01",
+            "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast capability  prefix-list send",
+            "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10",
+            "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client",
+        ]
+        self.execute_module(changed=True, commands=commands)
+
+    def test_vyos_bgp_address_family_replaced_idempotent(self):
+        set_module_args(
+            dict(
+                state="replaced",
+                config=dict(
+                    as_number=65536,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                            aggregate_address=[
+                                dict(prefix="192.0.2.0/24", as_set=True)
+                            ],
+                            networks=[
+                                dict(
+                                    prefix="192.1.13.0/24", route_map="map01"
+                                ),
+                                dict(prefix="192.2.13.0/24", backdoor=True),
+                            ],
+                        ),
+                        dict(
+                            afi="ipv6",
+                            redistribute=[dict(protocol="ripng", metric=20)],
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.0.2.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    route_map=[
+                                        dict(
+                                            action="export", route_map="map01"
+                                        )
+                                    ],
+                                    soft_reconfiguration=True,
+                                ),
+                            ],
+                        ),
+                        dict(
+                            neighbor_address="203.0.113.5",
+                            address_family=[
+                                dict(
+                                    afi="ipv6",
+                                    attribute_unchanged=dict(next_hop=True),
+                                )
+                            ],
+                        ),
+                    ],
+                ),
+            )
+        )
+        self.execute_module(changed=False, commands=[])
+
+    def test_vyos_bgp_address_family_replaced(self):
+        set_module_args(
+            dict(
+                state="replaced",
+                config=dict(
+                    as_number=65536,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                            aggregate_address=[
+                                dict(prefix="192.0.2.0/24", summary_only=True)
+                            ],
+                            networks=[
+                                dict(
+                                    prefix="192.1.13.0/24", route_map="map01"
+                                ),
+                            ],
+                        ),
+                        dict(
+                            afi="ipv6",
+                            redistribute=[dict(protocol="ospfv3", metric=20)],
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.10.21.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    route_map=[
+                                        dict(
+                                            action="import", route_map="map01"
+                                        )
+                                    ],
+                                ),
+                                dict(
+                                    afi="ipv6",
+                                    distribute_list=[
+                                        dict(action="export", acl=10)
+                                    ],
+                                    route_server_client=True,
+                                ),
+                            ],
+                        ),
+                        dict(
+                            neighbor_address="192.0.2.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    route_map=[
+                                        dict(
+                                            action="export", route_map="map01"
+                                        )
+                                    ],
+                                ),
+                            ],
+                        ),
+                        dict(
+                            neighbor_address="203.0.113.5",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    filter_list=[
+                                        dict(
+                                            action="export", path_list="list01"
+                                        ),
+                                    ],
+                                    capability=dict(orf="send"),
+                                )
+                            ],
+                        ),
+                    ],
+                ),
+            )
+        )
+        commands = [
+            "delete protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged",
+            "delete protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration",
+            "delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng",
+            "delete protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24",
+            "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 summary-only",
+            "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20",
+            "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv4-unicast route-map import map01",
+            "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10",
+            "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client",
+            "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast filter-list export list01",
+            "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv4-unicast capability  prefix-list send",
+        ]
+        self.execute_module(changed=True, commands=commands)
+
+    def test_vyos_bgp_address_family_overridden_idempotent(self):
+        set_module_args(
+            dict(
+                state="overridden",
+                config=dict(
+                    as_number=65536,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                            aggregate_address=[
+                                dict(prefix="192.0.2.0/24", as_set=True)
+                            ],
+                            networks=[
+                                dict(
+                                    prefix="192.1.13.0/24", route_map="map01"
+                                ),
+                                dict(prefix="192.2.13.0/24", backdoor=True),
+                            ],
+                        ),
+                        dict(
+                            afi="ipv6",
+                            redistribute=[dict(protocol="ripng", metric=20)],
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.0.2.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    route_map=[
+                                        dict(
+                                            action="export", route_map="map01"
+                                        )
+                                    ],
+                                    soft_reconfiguration=True,
+                                ),
+                            ],
+                        ),
+                        dict(
+                            neighbor_address="203.0.113.5",
+                            address_family=[
+                                dict(
+                                    afi="ipv6",
+                                    attribute_unchanged=dict(next_hop=True),
+                                )
+                            ],
+                        ),
+                    ],
+                ),
+            )
+        )
+        self.execute_module(changed=False, commands=[])
+
+    def test_vyos_bgp_address_family_overridden(self):
+        set_module_args(
+            dict(
+                state="overridden",
+                config=dict(
+                    as_number=65536,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                            networks=[
+                                dict(
+                                    prefix="192.1.13.0/24", route_map="map01"
+                                ),
+                            ],
+                        ),
+                        dict(
+                            afi="ipv6",
+                            redistribute=[dict(protocol="ospfv3", metric=20)],
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.10.21.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    route_map=[
+                                        dict(
+                                            action="import", route_map="map01"
+                                        )
+                                    ],
+                                ),
+                                dict(
+                                    afi="ipv6",
+                                    distribute_list=[
+                                        dict(action="export", acl=10)
+                                    ],
+                                    route_server_client=True,
+                                ),
+                            ],
+                        ),
+                    ],
+                ),
+            )
+        )
+        commands = [
+            "delete protocols bgp 65536 neighbor 203.0.113.5 address-family",
+            "delete protocols bgp 65536 neighbor 192.0.2.25 address-family",
+            "delete protocols bgp 65536 address-family ipv6-unicast redistribute ripng",
+            "delete protocols bgp 65536 address-family ipv4 aggregate-address",
+            "delete protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24",
+            "set protocols bgp 65536 address-family ipv6-unicast redistribute ospfv3 metric 20",
+            "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv4-unicast route-map import map01",
+            "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast distribute-list export 10",
+            "set protocols bgp 65536 neighbor 192.10.21.25 address-family ipv6-unicast route-server-client",
+        ]
+        self.execute_module(changed=True, commands=commands)
+
+    def test_vyos_bgp_address_family_deleted(self):
+        set_module_args(
+            dict(
+                state="deleted",
+                config=dict(
+                    as_number=65536,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.0.2.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                ),
+                            ],
+                        ),
+                        dict(
+                            neighbor_address="203.0.113.5",
+                        ),
+                    ],
+                ),
+            )
+        )
+        commands = [
+            "delete protocols bgp 65536 address-family ipv4-unicast",
+            "delete protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast",
+            "delete protocols bgp 65536 neighbor 203.0.113.5 address-family",
+        ]
+
+        self.execute_module(changed=True, commands=commands)
+
+    def test_vyos_bgp_address_family_incorrect_instance(self):
+        set_module_args(
+            dict(
+                state="overridden",
+                config=dict(
+                    as_number=100,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                            networks=[
+                                dict(
+                                    prefix="192.1.13.0/24", route_map="map01"
+                                ),
+                            ],
+                        ),
+                        dict(
+                            afi="ipv6",
+                            redistribute=[dict(protocol="ospfv3", metric=20)],
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.10.21.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    route_map=[
+                                        dict(
+                                            action="import", route_map="map01"
+                                        )
+                                    ],
+                                ),
+                                dict(
+                                    afi="ipv6",
+                                    distribute_list=[
+                                        dict(action="export", acl=10)
+                                    ],
+                                    route_server_client=True,
+                                ),
+                            ],
+                        ),
+                    ],
+                ),
+            )
+        )
+        result = self.execute_module(failed=True)
+        self.assertIn(
+            "Only one bgp instance is allowed per device", result["msg"]
+        )
+
+    def test_vyos_bgp_address_family_rendered(self):
+        set_module_args(
+            dict(
+                state="rendered",
+                config=dict(
+                    as_number=65536,
+                    address_family=[
+                        dict(
+                            afi="ipv4",
+                            aggregate_address=[
+                                dict(prefix="192.0.2.0/24", as_set=True)
+                            ],
+                            networks=[
+                                dict(
+                                    prefix="192.1.13.0/24", route_map="map01"
+                                ),
+                                dict(prefix="192.2.13.0/24", backdoor=True),
+                            ],
+                        ),
+                        dict(
+                            afi="ipv6",
+                            redistribute=[dict(protocol="ripng", metric=20)],
+                        ),
+                    ],
+                    neighbors=[
+                        dict(
+                            neighbor_address="192.0.2.25",
+                            address_family=[
+                                dict(
+                                    afi="ipv4",
+                                    route_map=[
+                                        dict(
+                                            action="export", route_map="map01"
+                                        )
+                                    ],
+                                    soft_reconfiguration=True,
+                                ),
+                            ],
+                        ),
+                        dict(
+                            neighbor_address="203.0.113.5",
+                            address_family=[
+                                dict(
+                                    afi="ipv6",
+                                    attribute_unchanged=dict(next_hop=True),
+                                )
+                            ],
+                        ),
+                    ],
+                ),
+            )
+        )
+
+        rendered_cmds = [
+            "set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map map01",
+            "set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor",
+            "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set",
+            "set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric 20",
+            "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export map01",
+            "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound",
+            "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop",
+        ]
+        result = self.execute_module(changed=False)
+        self.assertEqual(
+            sorted(result["rendered"]),
+            sorted(rendered_cmds),
+            result["rendered"],
+        )
+
+    def test_vyos_bgp_address_family_parsed(self):
+        commands = [
+            "set protocols bgp 65536 address-family ipv4-unicast network 192.1.13.0/24 route-map map01",
+            "set protocols bgp 65536 address-family ipv4-unicast network 192.2.13.0/24 backdoor",
+            "set protocols bgp 65536 address-family ipv4-unicast aggregate-address 192.0.2.0/24 as-set",
+            "set protocols bgp 65536 address-family ipv6-unicast redistribute ripng metric 20",
+            "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast route-map export map01",
+            "set protocols bgp 65536 neighbor 192.0.2.25 address-family ipv4-unicast soft-reconfiguration inbound",
+            "set protocols bgp 65536 neighbor 203.0.113.5 address-family ipv6-unicast attribute-unchanged next-hop",
+        ]
+
+        parsed_str = "\n".join(commands)
+        set_module_args(dict(running_config=parsed_str, state="parsed"))
+        result = self.execute_module(changed=False)
+        parsed_list = {
+            "as_number": 65536,
+            "address_family": [
+                {
+                    "afi": "ipv4",
+                    "networks": [
+                        {"prefix": "192.1.13.0/24", "route_map": "map01"},
+                        {"prefix": "192.2.13.0/24", "backdoor": True},
+                    ],
+                    "aggregate_address": [
+                        {"prefix": "192.0.2.0/24", "as_set": True}
+                    ],
+                },
+                {
+                    "afi": "ipv6",
+                    "redistribute": [{"protocol": "ripng", "metric": 20}],
+                },
+            ],
+            "neighbors": [
+                {
+                    "neighbor_address": "192.0.2.25",
+                    "address_family": [
+                        {"afi": "ipv4", "soft_reconfiguration": True},
+                    ],
+                },
+                {
+                    "neighbor_address": "203.0.113.5",
+                    "address_family": [
+                        {
+                            "afi": "ipv6",
+                            "attribute_unchanged": {"next_hop": True},
+                        }
+                    ],
+                },
+            ],
+        }
+        self.assertEqual(sorted(parsed_list), sorted(result["parsed"]))
+
+    def test_vyos_bgp_address_family_gathered(self):
+        set_module_args(dict(state="gathered"))
+        result = self.execute_module(changed=False)
+        gather_list = {
+            "as_number": 65536,
+            "address_family": [
+                {
+                    "afi": "ipv4",
+                    "networks": [
+                        {"prefix": "192.1.13.0/24", "route_map": "map01"},
+                        {"prefix": "192.2.13.0/24", "backdoor": True},
+                    ],
+                    "aggregate_address": [
+                        {"prefix": "192.0.2.0/24", "as_set": True}
+                    ],
+                },
+                {
+                    "afi": "ipv6",
+                    "redistribute": [{"protocol": "ripng", "metric": 20}],
+                },
+            ],
+            "neighbors": [
+                {
+                    "neighbor_address": "192.0.2.25",
+                    "address_family": [
+                        {"afi": "ipv4", "soft_reconfiguration": True},
+                    ],
+                },
+                {
+                    "neighbor_address": "203.0.113.5",
+                    "address_family": [
+                        {
+                            "afi": "ipv6",
+                            "attribute_unchanged": {"next_hop": True},
+                        }
+                    ],
+                },
+            ],
+        }
+        self.assertEqual(sorted(gather_list), sorted(result["gathered"]))
diff --git a/tox.ini b/tox.ini
index 3da0118f..49e9b1d6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -26,7 +26,7 @@ commands = {posargs}
 # E123, E125 skipped as they are invalid PEP-8.
 
 show-source = True
-ignore = E123,E125,E402,W503
+ignore = E123,E125,E402,W503,W504
 max-line-length = 160
 builtins = _
 exclude = .git,.tox,tests/unit/compat/
-- 
cgit v1.2.3