diff --git a/lib/ansible/module_utils/network/eos/providers/__init__.py b/lib/ansible/module_utils/network/eos/providers/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/lib/ansible/module_utils/network/eos/providers/cli/__init__.py b/lib/ansible/module_utils/network/eos/providers/cli/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/lib/ansible/module_utils/network/eos/providers/cli/config/__init__.py b/lib/ansible/module_utils/network/eos/providers/cli/config/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/__init__.py b/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/address_family.py b/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/address_family.py
new file mode 100644
index 00000000000..d8e15c1e7ff
--- /dev/null
+++ b/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/address_family.py
@@ -0,0 +1,130 @@
+#
+# (c) 2019, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+import re
+
+from ansible.module_utils.six import iteritems
+from ansible.module_utils.network.common.utils import to_list
+from ansible.module_utils.network.eos.providers.providers import CliProvider
+from ansible.module_utils.network.eos.providers.cli.config.bgp.neighbors import AFNeighbors
+from ansible.module_utils.common.network import to_netmask
+
+
+class AddressFamily(CliProvider):
+
+    def render(self, config=None):
+        commands = list()
+        safe_list = list()
+
+        router_context = 'router bgp %s' % self.get_value('config.bgp_as')
+        context_config = None
+
+        for item in self.get_value('config.address_family'):
+            context = 'address-family %s' % item['afi']
+            context_commands = list()
+
+            if config:
+                context_path = [router_context, context]
+                context_config = self.get_config_context(config, context_path, indent=2)
+
+            for key, value in iteritems(item):
+                if value is not None:
+                    meth = getattr(self, '_render_%s' % key, None)
+                    if meth:
+                        resp = meth(item, context_config)
+                        if resp:
+                            context_commands.extend(to_list(resp))
+
+            if context_commands:
+                commands.append(context)
+                commands.extend(context_commands)
+                commands.append('exit')
+
+            safe_list.append(context)
+
+        if self.params['operation'] == 'replace':
+            if config:
+                resp = self._negate_config(config, safe_list)
+                commands.extend(resp)
+
+        return commands
+
+    def _negate_config(self, config, safe_list=None):
+        commands = list()
+        matches = re.findall(r'(address-family .+)$', config, re.M)
+        for item in set(matches).difference(safe_list):
+            commands.append('no %s' % item)
+        return commands
+
+    def _render_auto_summary(self, item, config=None):
+        cmd = 'auto-summary'
+        if item['auto_summary'] is False:
+            cmd = 'no %s' % cmd
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_synchronization(self, item, config=None):
+        cmd = 'synchronization'
+        if item['synchronization'] is False:
+            cmd = 'no %s' % cmd
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_networks(self, item, config=None):
+        commands = list()
+        safe_list = list()
+
+        for entry in item['networks']:
+            network = entry['prefix']
+            if entry['masklen']:
+                network = '%s/%s' % (entry['prefix'], entry['masklen'])
+            safe_list.append(network)
+
+            cmd = 'network %s' % network
+
+            if entry['route_map']:
+                cmd += ' route-map %s' % entry['route_map']
+
+            if not config or cmd not in config:
+                commands.append(cmd)
+
+        if self.params['operation'] == 'replace':
+            if config:
+                matches = re.findall(r'network (\S+)', config, re.M)
+                for entry in set(matches).difference(safe_list):
+                    commands.append('no network %s' % entry)
+
+        return commands
+
+    def _render_redistribute(self, item, config=None):
+        commands = list()
+        safe_list = list()
+
+        for entry in item['redistribute']:
+            option = entry['protocol']
+
+            cmd = 'redistribute %s' % entry['protocol']
+
+            if entry['route_map']:
+                cmd += ' route-map %s' % entry['route_map']
+
+            if not config or cmd not in config:
+                commands.append(cmd)
+
+            safe_list.append(option)
+
+        if self.params['operation'] == 'replace':
+            if config:
+                matches = re.findall(r'redistribute (\S+)(?:\s*)(\d*)', config, re.M)
+                for i in range(0, len(matches)):
+                    matches[i] = ' '.join(matches[i]).strip()
+                for entry in set(matches).difference(safe_list):
+                    commands.append('no redistribute %s' % entry)
+
+        return commands
+
+    def _render_neighbors(self, item, config):
+        """ generate bgp neighbor configuration
+        """
+        return AFNeighbors(self.params).render(config, nbr_list=item['neighbors'])
diff --git a/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/neighbors.py b/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/neighbors.py
new file mode 100644
index 00000000000..329916b69f6
--- /dev/null
+++ b/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/neighbors.py
@@ -0,0 +1,173 @@
+#
+# (c) 2019, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+import re
+
+from ansible.module_utils.six import iteritems
+from ansible.module_utils.network.common.utils import to_list
+from ansible.module_utils.network.eos.providers.providers import CliProvider
+
+
+class Neighbors(CliProvider):
+
+    def render(self, config=None, nbr_list=None):
+        commands = list()
+        safe_list = list()
+        if not nbr_list:
+            nbr_list = self.get_value('config.neighbors')
+
+        for item in nbr_list:
+            neighbor_commands = list()
+            context = 'neighbor %s' % item['neighbor']
+            cmd = '%s remote-as %s' % (context, item['remote_as'])
+
+            if not config or cmd not in config:
+                neighbor_commands.append(cmd)
+
+            for key, value in iteritems(item):
+                if value is not None:
+                    meth = getattr(self, '_render_%s' % key, None)
+                    if meth:
+                        resp = meth(item, config)
+                        if resp:
+                            neighbor_commands.extend(to_list(resp))
+
+            commands.extend(neighbor_commands)
+            safe_list.append(context)
+
+        if self.params['operation'] == 'replace':
+            if config and safe_list:
+                commands.extend(self._negate_config(config, safe_list))
+
+        return commands
+
+    def _negate_config(self, config, safe_list=None):
+        commands = list()
+        matches = re.findall(r'(neighbor \S+)', config, re.M)
+        for item in set(matches).difference(safe_list):
+            commands.append('no %s' % item)
+        return commands
+
+    def _render_description(self, item, config=None):
+        cmd = 'neighbor %s description %s' % (item['neighbor'], item['description'])
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_enabled(self, item, config=None):
+        cmd = 'neighbor %s shutdown' % item['neighbor']
+        if item['enabled'] is True:
+            if not config or cmd in config:
+                cmd = 'no %s' % cmd
+                return cmd
+        elif not config or cmd not in config:
+            return cmd
+
+    def _render_update_source(self, item, config=None):
+        cmd = 'neighbor %s update-source %s' % (item['neighbor'], item['update_source'])
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_password(self, item, config=None):
+        cmd = 'neighbor %s password %s' % (item['neighbor'], item['password'])
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_ebgp_multihop(self, item, config=None):
+        cmd = 'neighbor %s ebgp-multihop %s' % (item['neighbor'], item['ebgp_multihop'])
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_peer_group(self, item, config=None):
+        cmd = 'neighbor %s peer-group %s' % (item['neighbor'], item['peer_group'])
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_route_reflector_client(self, item, config=None):
+        cmd = 'neighbor %s route-reflector-client' % item['neighbor']
+        if item['route_reflector_client'] is False:
+            if not config or cmd in config:
+                cmd = 'no %s' % cmd
+                return cmd
+        elif not config or cmd not in config:
+            return cmd
+
+    def _render_maximum_prefix(self, item, config=None):
+        cmd = 'neighbor %s maximum-routes %s' % (item['neighbor'], item['maximum_prefix'])
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_remove_private_as(self, item, config=None):
+        cmd = 'neighbor %s remove-private-AS' % item['neighbor']
+        if item['remove_private_as'] is False:
+            if not config or cmd in config:
+                cmd = 'no %s' % cmd
+                return cmd
+        elif not config or cmd not in config:
+            return cmd
+
+    def _render_timers(self, item, config):
+        """generate bgp timer related configuration
+        """
+        keepalive = item['timers']['keepalive']
+        holdtime = item['timers']['holdtime']
+        neighbor = item['neighbor']
+
+        if keepalive and holdtime:
+            cmd = 'neighbor %s timers %s %s' % (neighbor, keepalive, holdtime)
+            if not config or cmd not in config:
+                return cmd
+
+
+class AFNeighbors(CliProvider):
+
+    def render(self, config=None, nbr_list=None):
+        commands = list()
+        if not nbr_list:
+            return
+
+        for item in nbr_list:
+            neighbor_commands = list()
+            for key, value in iteritems(item):
+                if value is not None:
+                    meth = getattr(self, '_render_%s' % key, None)
+                    if meth:
+                        resp = meth(item, config)
+                        if resp:
+                            neighbor_commands.extend(to_list(resp))
+
+            commands.extend(neighbor_commands)
+
+        return commands
+
+    def _render_activate(self, item, config=None):
+        cmd = 'neighbor %s activate' % item['neighbor']
+        if item['activate'] is False:
+            if not config or cmd in config:
+                cmd = 'no %s' % cmd
+                return cmd
+        elif not config or cmd not in config:
+            return cmd
+
+    def _render_default_originate(self, item, config=None):
+        cmd = 'neighbor %s default-originate' % item['neighbor']
+        if item['activate'] is False:
+            if not config or cmd in config:
+                cmd = 'no %s' % cmd
+                return cmd
+        elif not config or cmd not in config:
+            return cmd
+
+    def _render_graceful_restart(self, item, config=None):
+        cmd = 'neighbor %s graceful-restart' % item['neighbor']
+        if item['activate'] is False:
+            if not config or cmd in config:
+                cmd = 'no %s' % cmd
+                return cmd
+        elif not config or cmd not in config:
+            return cmd
+
+    def _render_weight(self, item, config=None):
+        cmd = 'neighbor %s weight %s' % (item['neighbor'], item['weight'])
+        if not config or cmd not in config:
+            return cmd
diff --git a/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/process.py b/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/process.py
new file mode 100644
index 00000000000..a5302b2c371
--- /dev/null
+++ b/lib/ansible/module_utils/network/eos/providers/cli/config/bgp/process.py
@@ -0,0 +1,162 @@
+#
+# (c) 2019, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+import re
+
+from ansible.module_utils.six import iteritems
+from ansible.module_utils.network.common.utils import to_list
+from ansible.module_utils.network.eos.providers.providers import register_provider
+from ansible.module_utils.network.eos.providers.providers import CliProvider
+from ansible.module_utils.network.eos.providers.cli.config.bgp.neighbors import Neighbors
+from ansible.module_utils.network.eos.providers.cli.config.bgp.address_family import AddressFamily
+
+REDISTRIBUTE_PROTOCOLS = frozenset(['ospf', 'ospf3', 'rip', 'isis', 'static', 'connected'])
+
+
+@register_provider('eos', 'eos_bgp')
+class Provider(CliProvider):
+
+    def render(self, config=None):
+        commands = list()
+
+        existing_as = None
+        if config:
+            match = re.search(r'router bgp (\d+)', config, re.M)
+            existing_as = match.group(1)
+
+        operation = self.params['operation']
+
+        context = None
+        if self.params['config']:
+            context = 'router bgp %s' % self.get_value('config.bgp_as')
+
+        if operation == 'delete':
+            if existing_as:
+                commands.append('no router bgp %s' % existing_as)
+            elif context:
+                commands.append('no %s' % context)
+
+        else:
+            self._validate_input(config)
+            if operation == 'replace':
+                if existing_as and int(existing_as) != self.get_value('config.bgp_as'):
+                    commands.append('no router bgp %s' % existing_as)
+                    config = None
+
+            elif operation == 'override':
+                if existing_as:
+                    commands.append('no router bgp %s' % existing_as)
+                config = None
+
+            context_commands = list()
+
+            for key, value in iteritems(self.get_value('config')):
+                if value is not None:
+                    meth = getattr(self, '_render_%s' % key, None)
+                    if meth:
+                        resp = meth(config)
+                        if resp:
+                            context_commands.extend(to_list(resp))
+
+            if context and context_commands:
+                commands.append(context)
+                commands.extend(context_commands)
+                commands.append('exit')
+        return commands
+
+    def _render_router_id(self, config=None):
+        cmd = 'router-id %s' % self.get_value('config.router_id')
+        if not config or cmd not in config:
+            return cmd
+
+    def _render_log_neighbor_changes(self, config=None):
+        cmd = 'bgp log-neighbor-changes'
+        log_neighbor_changes = self.get_value('config.log_neighbor_changes')
+        if log_neighbor_changes is True:
+            if not config or cmd not in config:
+                return cmd
+        elif log_neighbor_changes is False:
+            if config and cmd in config:
+                return 'no %s' % cmd
+
+    def _render_networks(self, config=None):
+        commands = list()
+        safe_list = list()
+
+        for entry in self.get_value('config.networks'):
+            network = entry['prefix']
+            if entry['masklen']:
+                network = '%s/%s' % (entry['prefix'], entry['masklen'])
+            safe_list.append(network)
+
+            cmd = 'network %s' % network
+
+            if entry['route_map']:
+                cmd += ' route-map %s' % entry['route_map']
+
+            if not config or cmd not in config:
+                commands.append(cmd)
+
+        if self.params['operation'] == 'replace':
+            if config:
+                matches = re.findall(r'network (\S+)', config, re.M)
+                for entry in set(matches).difference(safe_list):
+                    commands.append('no network %s' % entry)
+
+        return commands
+
+    def _render_redistribute(self, config=None):
+        commands = list()
+        safe_list = list()
+
+        for entry in self.get_value('config.redistribute'):
+            option = entry['protocol']
+
+            cmd = 'redistribute %s' % entry['protocol']
+
+            if entry['route_map']:
+                cmd += ' route-map %s' % entry['route_map']
+
+            if not config or cmd not in config:
+                commands.append(cmd)
+
+            safe_list.append(option)
+
+        if self.params['operation'] == 'replace':
+            if config:
+                matches = re.findall(r'redistribute (\S+)(?:\s*)(\d*)', config, re.M)
+                for i in range(0, len(matches)):
+                    matches[i] = ' '.join(matches[i]).strip()
+                for entry in set(matches).difference(safe_list):
+                    commands.append('no redistribute %s' % entry)
+
+        return commands
+
+    def _render_neighbors(self, config):
+        """ generate bgp neighbor configuration
+        """
+        return Neighbors(self.params).render(config)
+
+    def _render_address_family(self, config):
+        """ generate address-family configuration
+        """
+        return AddressFamily(self.params).render(config)
+
+    def _validate_input(self, config):
+        def device_has_AF(config):
+            return re.search(r'address-family (?:.*)', config)
+
+        address_family = self.get_value('config.address_family')
+        root_networks = self.get_value('config.networks')
+        operation = self.params['operation']
+
+        if operation == 'replace' and root_networks:
+            if address_family:
+                for item in address_family:
+                    if item['networks']:
+                        raise ValueError('operation is replace but provided both root level networks and networks under %s address family'
+                                         % item['afi'])
+
+            if config and device_has_AF(config):
+                raise ValueError('operation is replace and device has one or more address family activated but root level network(s) provided')
diff --git a/lib/ansible/module_utils/network/eos/providers/module.py b/lib/ansible/module_utils/network/eos/providers/module.py
new file mode 100644
index 00000000000..9f259ebb460
--- /dev/null
+++ b/lib/ansible/module_utils/network/eos/providers/module.py
@@ -0,0 +1,62 @@
+#
+# (c) 2019, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.connection import Connection
+from ansible.module_utils.network.eos.providers import providers
+from ansible.module_utils._text import to_text
+
+
+class NetworkModule(AnsibleModule):
+
+    fail_on_missing_provider = True
+
+    def __init__(self, connection=None, *args, **kwargs):
+        super(NetworkModule, self).__init__(*args, **kwargs)
+
+        if connection is None:
+            connection = Connection(self._socket_path)
+
+        self.connection = connection
+
+    @property
+    def provider(self):
+        if not hasattr(self, '_provider'):
+            capabilities = self.from_json(self.connection.get_capabilities())
+
+            network_os = capabilities['device_info']['network_os']
+            network_api = capabilities['network_api']
+
+            if network_api == 'cliconf':
+                connection_type = 'network_cli'
+
+            cls = providers.get(network_os, self._name, connection_type)
+
+            if not cls:
+                msg = 'unable to find suitable provider for network os %s' % network_os
+                if self.fail_on_missing_provider:
+                    self.fail_json(msg=msg)
+                else:
+                    self.warn(msg)
+
+            obj = cls(self.params, self.connection, self.check_mode)
+
+            setattr(self, '_provider', obj)
+
+        return getattr(self, '_provider')
+
+    def get_facts(self, subset=None):
+        try:
+            self.provider.get_facts(subset)
+        except Exception as exc:
+            self.fail_json(msg=to_text(exc))
+
+    def edit_config(self, config_filter=None):
+        current_config = self.connection.get_config(flags=config_filter)
+        try:
+            commands = self.provider.edit_config(current_config)
+            changed = bool(commands)
+            return {'commands': commands, 'changed': changed}
+        except Exception as exc:
+            self.fail_json(msg=to_text(exc))
diff --git a/lib/ansible/module_utils/network/eos/providers/providers.py b/lib/ansible/module_utils/network/eos/providers/providers.py
new file mode 100644
index 00000000000..be429dc0f8d
--- /dev/null
+++ b/lib/ansible/module_utils/network/eos/providers/providers.py
@@ -0,0 +1,120 @@
+#
+# (c) 2019, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+import json
+
+from threading import RLock
+
+from ansible.module_utils.six import itervalues
+from ansible.module_utils.network.common.utils import to_list
+from ansible.module_utils.network.common.config import NetworkConfig
+
+
+_registered_providers = {}
+_provider_lock = RLock()
+
+
+def register_provider(network_os, module_name):
+    def wrapper(cls):
+        _provider_lock.acquire()
+        try:
+            if network_os not in _registered_providers:
+                _registered_providers[network_os] = {}
+            for ct in cls.supported_connections:
+                if ct not in _registered_providers[network_os]:
+                    _registered_providers[network_os][ct] = {}
+            for item in to_list(module_name):
+                for entry in itervalues(_registered_providers[network_os]):
+                    entry[item] = cls
+        finally:
+            _provider_lock.release()
+        return cls
+    return wrapper
+
+
+def get(network_os, module_name, connection_type):
+    network_os_providers = _registered_providers.get(network_os)
+    if network_os_providers is None:
+        raise ValueError('unable to find a suitable provider for this module')
+    if connection_type not in network_os_providers:
+        raise ValueError('provider does not support this connection type')
+    elif module_name not in network_os_providers[connection_type]:
+        raise ValueError('could not find a suitable provider for this module')
+    return network_os_providers[connection_type][module_name]
+
+
+class ProviderBase(object):
+
+    supported_connections = ()
+
+    def __init__(self, params, connection=None, check_mode=False):
+        self.params = params
+        self.connection = connection
+        self.check_mode = check_mode
+
+    @property
+    def capabilities(self):
+        if not hasattr(self, '_capabilities'):
+            resp = self.from_json(self.connection.get_capabilities())
+            setattr(self, '_capabilities', resp)
+        return getattr(self, '_capabilities')
+
+    def get_value(self, path):
+        params = self.params.copy()
+        for key in path.split('.'):
+            params = params[key]
+        return params
+
+    def get_facts(self, subset=None):
+        raise NotImplementedError(self.__class__.__name__)
+
+    def edit_config(self):
+        raise NotImplementedError(self.__class__.__name__)
+
+
+class CliProvider(ProviderBase):
+
+    supported_connections = ('network_cli',)
+
+    @property
+    def capabilities(self):
+        if not hasattr(self, '_capabilities'):
+            resp = self.from_json(self.connection.get_capabilities())
+            setattr(self, '_capabilities', resp)
+        return getattr(self, '_capabilities')
+
+    def get_config_context(self, config, path, indent=2):
+        if config is not None:
+            netcfg = NetworkConfig(indent=indent, contents=config)
+            try:
+                config = netcfg.get_block_config(to_list(path))
+            except ValueError:
+                config = None
+            return config
+
+    def render(self, config=None):
+        raise NotImplementedError(self.__class__.__name__)
+
+    def cli(self, command):
+        try:
+            if not hasattr(self, '_command_output'):
+                setattr(self, '_command_output', {})
+            return self._command_output[command]
+        except KeyError:
+            out = self.connection.get(command)
+            try:
+                out = json.loads(out)
+            except ValueError:
+                pass
+            self._command_output[command] = out
+            return out
+
+    def get_facts(self, subset=None):
+        return self.populate()
+
+    def edit_config(self, config=None):
+        commands = self.render(config)
+        if commands and self.check_mode is False:
+            self.connection.edit_config(commands)
+        return commands
diff --git a/lib/ansible/modules/network/eos/eos_bgp.py b/lib/ansible/modules/network/eos/eos_bgp.py
new file mode 100644
index 00000000000..b60e42a15f9
--- /dev/null
+++ b/lib/ansible/modules/network/eos/eos_bgp.py
@@ -0,0 +1,394 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# (c) 2019, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+                    'status': ['preview'],
+                    'supported_by': 'network'}
+
+
+DOCUMENTATION = """
+---
+module: eos_bgp
+version_added: "2.8"
+author: "Nilashish Chakraborty (@nilashishc)"
+short_description: Configure global BGP protocol settings on Arista EOS.
+description:
+  - This module provides configuration management of global BGP parameters
+    on Arista EOS devices.
+notes:
+  - Tested against Arista vEOS Version 4.15.9M.
+options:
+  config:
+    description:
+      - Specifies the BGP related configuration.
+    suboptions:
+      bgp_as:
+        description:
+          - Specifies the BGP Autonomous System (AS) number to configure on the device.
+        type: int
+        required: true
+      router_id:
+        description:
+          - Configures the BGP routing process router-id value.
+        default: null
+      log_neighbor_changes:
+        description:
+          - Enable/disable logging neighbor up/down and reset reason.
+        type: bool
+      neighbors:
+        description:
+          - Specifies BGP neighbor related configurations.
+        suboptions:
+          neighbor:
+            description:
+              - Neighbor router address.
+            required: True
+          remote_as:
+            description:
+              - Remote AS of the BGP neighbor to configure.
+            type: int
+            required: True
+          update_source:
+            description:
+              - Source of the routing updates.
+          password:
+            description:
+              - Password to authenticate the BGP peer connection.
+          description:
+            description:
+              - Neighbor specific description.
+          ebgp_multihop:
+            description:
+              - Specifies the maximum hop count for EBGP neighbors not on directly connected networks.
+              - The range is from 1 to 255.
+            type: int
+          peer_group:
+            description:
+              - Name of the peer group that the neighbor is a member of.
+          timers:
+            description:
+              - Specifies BGP neighbor timer related configurations.
+            suboptions:
+              keepalive:
+                description:
+                  - Frequency (in seconds) with which the device sends keepalive messages to its peer.
+                  - The range is from 0 to 3600.
+                type: int
+                required: True
+              holdtime:
+                description:
+                  - Interval (in seconds) after not receiving a keepalive message that device declares a peer dead.
+                  - The range is from 3 to 7200.
+                  - Setting this value to 0 will not send keep-alives (hold forever).
+                type: int
+                required: True
+          route_reflector_client:
+            description:
+              - Specify a neighbor as a route reflector client.
+            type: bool
+          remove_private_as:
+            description:
+              - Remove the private AS number from outbound updates.
+            type: bool
+          enabled:
+            description:
+              - Administratively shutdown or enable a neighbor.
+          maximum_prefix:
+            description:
+              - Maximum number of prefixes to accept from this peer.
+              - The range is from 0 to 4294967294.
+            type: int
+      redistribute:
+        description:
+          - Specifies the redistribute information from another routing protocol.
+        suboptions:
+          protocol:
+            description:
+              - Specifies the protocol for configuring redistribute information.
+            required: True
+          route_map:
+            description:
+              - Specifies the route map reference.
+      networks:
+        description:
+          - Specify Networks to announce via BGP.
+          - For operation replace, this option is mutually exclusive with networks option under address_family.
+          - For operation replace, if the device already has an address family activated, this option is not allowed.
+        suboptions:
+          prefix:
+            description:
+              - Network ID to announce via BGP.
+            required: True
+          masklen:
+            description:
+              - Subnet mask length for the Network to announce(e.g, 8, 16, 24, etc.).
+          route_map:
+            description:
+              - Route map to modify the attributes.
+      address_family:
+        description:
+          - Specifies BGP address family related configurations.
+        suboptions:
+          afi:
+            description:
+              - Type of address family to configure.
+            choices:
+              - ipv4
+              - ipv6
+            required: True
+          redistribute:
+            description:
+              - Specifies the redistribute information from another routing protocol.
+            suboptions:
+              protocol:
+                description:
+                  - Specifies the protocol for configuring redistribute information.
+                required: True
+              route_map:
+                description:
+                  - Specifies the route map reference.
+          networks:
+            description:
+              - Specify Networks to announce via BGP.
+              - For operation replace, this option is mutually exclusive with root level networks option.
+            suboptions:
+              prefix:
+                description:
+                  - Network ID to announce via BGP.
+                required: True
+              masklen:
+                description:
+                  - Subnet mask length for the Network to announce(e.g, 8, 16, 24, etc.).
+              route_map:
+                description:
+                  - Route map to modify the attributes.
+          neighbors:
+            description:
+              - Specifies BGP neighbor related configurations in Address Family configuration mode.
+            suboptions:
+              neighbor:
+                description:
+                  - Neighbor router address.
+                required: True
+              activate:
+                description:
+                  - Enable the Address Family for this Neighbor.
+                type: bool
+              default_originate:
+                description:
+                  - Originate default route to this neighbor.
+                type: bool
+              graceful_restart:
+                description:
+                  - Enable/disable graceful restart mode for this neighbor.
+                type: bool
+              weight:
+                description:
+                  - Assign weight for routes learnt from this neighbor.
+                  - The range is from 0 to 65535
+                type: int
+  operation:
+    description:
+      - Specifies the operation to be performed on the BGP process configured on the device.
+      - In case of merge, the input configuration will be merged with the existing BGP configuration on the device.
+      - In case of replace, if there is a diff between the existing configuration and the input configuration, the
+        existing configuration will be replaced by the input configuration for every option that has the diff.
+      - In case of override, all the existing BGP configuration will be removed from the device and replaced with
+        the input configuration.
+      - In case of delete the existing BGP configuration will be removed from the device.
+    default: merge
+    choices: ['merge', 'replace', 'override', 'delete']
+"""
+
+EXAMPLES = """
+- name: configure global bgp as 64496
+  eos_bgp:
+    config:
+      bgp_as: 64496
+      router_id: 192.0.2.1
+      log_neighbor_changes: True
+      neighbors:
+        - neighbor: 203.0.113.5
+          remote_as: 64511
+          timers:
+            keepalive: 300
+            holdtime: 360
+        - neighbor: 198.51.100.2
+          remote_as: 64498
+      networks:
+        - prefix: 198.51.100.0
+          route_map: RMAP_1
+        - prefix: 192.0.2.0
+          masklen: 23
+      address_family:
+        - afi: ipv4
+          safi: unicast
+          redistribute:
+            - protocol: isis
+              route_map: RMAP_1
+    operation: merge
+
+- name: Configure BGP neighbors
+  eos_bgp:
+    config:
+      bgp_as: 64496
+      neighbors:
+        - neighbor: 192.0.2.10
+          remote_as: 64496
+          description: IBGP_NBR_1
+          ebgp_multihop: 100
+          timers:
+            keepalive: 300
+            holdtime: 360
+
+        - neighbor: 192.0.2.15
+          remote_as: 64496
+          description: IBGP_NBR_2
+          ebgp_multihop: 150
+    operation: merge
+
+- name: Configure root-level networks for BGP
+  eos_bgp:
+    config:
+      bgp_as: 64496
+      networks:
+        - prefix: 203.0.113.0
+          masklen: 27
+          route_map: RMAP_1
+
+        - prefix: 203.0.113.32
+          masklen: 27
+          route_map: RMAP_2
+    operation: merge
+
+- name: Configure BGP neighbors under address family mode
+  eos_bgp:
+    config:
+      bgp_as: 64496
+      address_family:
+        - afi: ipv4
+          neighbors:
+            - neighbor: 203.0.113.10
+              activate: yes
+              default_originate: True
+
+            - neighbor: 192.0.2.15
+              activate: yes
+              graceful_restart: True
+    operation: merge
+
+- name: remove bgp as 64496 from config
+  eos_bgp:
+    config:
+      bgp_as: 64496
+    operation: delete
+"""
+
+RETURN = """
+commands:
+  description: The list of configuration mode commands to send to the device
+  returned: always
+  type: list
+  sample:
+    - router bgp 64496
+    - bgp router-id 192.0.2.1
+    - bgp log-neighbor-changes
+    - neighbor 203.0.113.5 remote-as 64511
+    - neighbor 203.0.113.5 timers 300 360
+    - neighbor 198.51.100.2 remote-as 64498
+    - network 198.51.100.0 route-map RMAP_1
+    - network 192.0.2.0 mask 255.255.254.0
+    - address-family ipv4
+    - redistribute isis route-map RMAP_1
+    - exit-address-family
+"""
+from ansible.module_utils._text import to_text
+from ansible.module_utils.network.eos.providers.module import NetworkModule
+from ansible.module_utils.network.eos.providers.cli.config.bgp.process import REDISTRIBUTE_PROTOCOLS
+
+
+def main():
+    """ main entry point for module execution
+    """
+    network_spec = {
+        'prefix': dict(required=True),
+        'masklen': dict(type='int'),
+        'route_map': dict(),
+    }
+
+    redistribute_spec = {
+        'protocol': dict(choices=REDISTRIBUTE_PROTOCOLS, required=True),
+        'route_map': dict(),
+    }
+
+    timer_spec = {
+        'keepalive': dict(type='int', required=True),
+        'holdtime': dict(type='int', required=True),
+    }
+
+    neighbor_spec = {
+        'neighbor': dict(required=True),
+        'remote_as': dict(type='int', required=True),
+        'update_source': dict(),
+        'password': dict(no_log=True),
+        'enabled': dict(type='bool'),
+        'description': dict(),
+        'ebgp_multihop': dict(type='int'),
+        'timers': dict(type='dict', options=timer_spec),
+        'peer_group': dict(),
+        'maximum_prefix': dict(type='int'),
+        'route_reflector_client': dict(type='int'),
+        'remove_private_as': dict(type='bool')
+    }
+
+    af_neighbor_spec = {
+        'neighbor': dict(required=True),
+        'activate': dict(type='bool'),
+        'default_originate': dict(type='bool'),
+        'graceful_restart': dict(type='bool'),
+        'weight': dict(type='int'),
+    }
+
+    address_family_spec = {
+        'afi': dict(choices=['ipv4', 'ipv6'], required=True),
+        'networks': dict(type='list', elements='dict', options=network_spec),
+        'redistribute': dict(type='list', elements='dict', options=redistribute_spec),
+        'neighbors': dict(type='list', elements='dict', options=af_neighbor_spec),
+    }
+
+    config_spec = {
+        'bgp_as': dict(type='int', required=True),
+        'router_id': dict(),
+        'log_neighbor_changes': dict(type='bool'),
+        'neighbors': dict(type='list', elements='dict', options=neighbor_spec),
+        'address_family': dict(type='list', elements='dict', options=address_family_spec),
+        'redistribute': dict(type='list', elements='dict', options=redistribute_spec),
+        'networks': dict(type='list', elements='dict', options=network_spec)
+    }
+
+    argument_spec = {
+        'config': dict(type='dict', options=config_spec),
+        'operation': dict(default='merge', choices=['merge', 'replace', 'override', 'delete'])
+    }
+
+    module = NetworkModule(argument_spec=argument_spec,
+                           supports_check_mode=True)
+
+    try:
+        result = module.edit_config(config_filter='| section bgp')
+    except Exception as exc:
+        module.fail_json(msg=to_text(exc))
+
+    module.exit_json(**result)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/test/integration/targets/eos_bgp/defaults/main.yaml b/test/integration/targets/eos_bgp/defaults/main.yaml
new file mode 100644
index 00000000000..9ef5ba51651
--- /dev/null
+++ b/test/integration/targets/eos_bgp/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: "*"
+test_items: []
diff --git a/test/integration/targets/eos_bgp/meta/main.yaml b/test/integration/targets/eos_bgp/meta/main.yaml
new file mode 100644
index 00000000000..e5c8cd02f04
--- /dev/null
+++ b/test/integration/targets/eos_bgp/meta/main.yaml
@@ -0,0 +1,2 @@
+dependencies:
+  - prepare_eos_tests
diff --git a/test/integration/targets/eos_bgp/tasks/cli.yaml b/test/integration/targets/eos_bgp/tasks/cli.yaml
new file mode 100644
index 00000000000..87a42971bbc
--- /dev/null
+++ b/test/integration/targets/eos_bgp/tasks/cli.yaml
@@ -0,0 +1,16 @@
+---
+- name: collect all cli test cases
+  find:
+    paths: "{{ role_path }}/tests/cli"
+    patterns: "{{ testcase }}.yaml"
+  register: test_cases
+  delegate_to: localhost
+
+- name: set test_items
+  set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: run test cases (connection=network_cli)
+  include: "{{ test_case_to_run }} ansible_connection=network_cli"
+  with_items: "{{ test_items }}"
+  loop_control:
+    loop_var: test_case_to_run
diff --git a/test/integration/targets/eos_bgp/tasks/main.yaml b/test/integration/targets/eos_bgp/tasks/main.yaml
new file mode 100644
index 00000000000..415c99d8b12
--- /dev/null
+++ b/test/integration/targets/eos_bgp/tasks/main.yaml
@@ -0,0 +1,2 @@
+---
+- { include: cli.yaml, tags: ['cli'] }
diff --git a/test/integration/targets/eos_bgp/tests/cli/basic.yaml b/test/integration/targets/eos_bgp/tests/cli/basic.yaml
new file mode 100644
index 00000000000..0864152d61b
--- /dev/null
+++ b/test/integration/targets/eos_bgp/tests/cli/basic.yaml
@@ -0,0 +1,372 @@
+- debug: msg="START eos cli/eos_bgp.yaml on connection={{ ansible_connection }}"
+
+- name: Clear existing BGP config
+  eos_bgp:
+    operation: delete
+  ignore_errors: yes
+
+- name: Configure BGP with AS 64496 and a router-id
+  eos_bgp: &config
+    operation: merge
+    config:
+      bgp_as: 64496
+      router_id: 192.0.2.2
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'router bgp 64496' in result.commands"
+      - "'router-id 192.0.2.2' in result.commands"
+
+- name: Configure BGP with AS 64496 and a router-id (idempotent)
+  eos_bgp: *config
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure BGP neighbors
+  eos_bgp: &nbr
+    operation: merge
+    config:
+      bgp_as: 64496
+      neighbors:
+        - neighbor: 192.0.2.10
+          remote_as: 64496
+          description: IBGP_NBR_1
+          ebgp_multihop: 100
+          timers:
+            keepalive: 300
+            holdtime: 360
+
+        - neighbor: 192.0.2.15
+          remote_as: 64496
+          description: IBGP_NBR_2
+          ebgp_multihop: 150
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'router bgp 64496' in result.commands"
+      - "'neighbor 192.0.2.10 remote-as 64496' in result.commands"
+      - "'neighbor 192.0.2.10 description IBGP_NBR_1' in result.commands"
+      - "'neighbor 192.0.2.10 ebgp-multihop 100' in result.commands"
+      - "'neighbor 192.0.2.10 timers 300 360' in result.commands"
+      - "'neighbor 192.0.2.15 remote-as 64496' in result.commands"
+      - "'neighbor 192.0.2.15 description IBGP_NBR_2' in result.commands"
+      - "'neighbor 192.0.2.15 ebgp-multihop 150' in result.commands"
+
+- name: Configure BGP neighbors (idempotent)
+  eos_bgp: *nbr
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure BGP neighbors with operation replace
+  eos_bgp: &nbr_rplc
+    operation: replace
+    config:
+      bgp_as: 64496
+      neighbors:
+        - neighbor: 192.0.2.15
+          remote_as: 64496
+          description: IBGP_NBR_2
+          ebgp_multihop: 150
+
+        - neighbor: 203.0.113.10
+          remote_as: 64511
+          description: EBGP_NBR_1
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'neighbor 203.0.113.10 remote-as 64511' in result.commands"
+      - "'neighbor 203.0.113.10 description EBGP_NBR_1' in result.commands"
+      - "'no neighbor 192.0.2.10' in result.commands"
+
+- name: Configure BGP neighbors with operation replace (idempotent)
+  eos_bgp: *nbr_rplc
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure root-level networks for BGP
+  eos_bgp: &net
+    operation: merge
+    config:
+      bgp_as: 64496
+      networks:
+        - prefix: 203.0.113.0
+          masklen: 27
+          route_map: RMAP_1
+
+        - prefix: 203.0.113.32
+          masklen: 27
+          route_map: RMAP_2
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == True'
+      - "'router bgp 64496' in result.commands"
+      - "'network 203.0.113.0/27 route-map RMAP_1' in result.commands"
+      - "'network 203.0.113.32/27 route-map RMAP_2' in result.commands"
+
+- name: Configure root-level networks for BGP (idempotent)
+  eos_bgp: *net
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure root-level networks for BGP with operation replace
+  eos_bgp: &net_rplc
+    operation: replace
+    config:
+      bgp_as: 64496
+      networks:
+        - prefix: 203.0.113.0
+          masklen: 27
+          route_map: RMAP_1
+
+        - prefix: 198.51.100.16
+          masklen: 28
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == True'
+      - "'router bgp 64496' in result.commands"
+      - "'network 198.51.100.16/28' in result.commands"
+      - "'no network 203.0.113.32/27' in result.commands"
+
+- name: Configure root-level networks for BGP with operation replace (idempotent)
+  eos_bgp: *net_rplc
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure BGP route redistribute information
+  eos_bgp: &rdr
+    operation: merge
+    config:
+      bgp_as: 64496
+      redistribute:
+        - protocol: ospf
+          route_map: RMAP_1
+
+        - protocol: rip
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'router bgp 64496' in result.commands"
+      - "'redistribute ospf route-map RMAP_1' in result.commands"
+      - "'redistribute rip' in result.commands"
+
+- name: Configure BGP route redistribute information (idempotent)
+  eos_bgp: *rdr
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure BGP route redistribute information with operation replace
+  eos_bgp: &rdr_rplc
+    operation: replace
+    config:
+      bgp_as: 64496
+      redistribute:
+        - protocol: ospf
+          route_map: RMAP_1
+
+        - protocol: static
+          route_map: RMAP_2
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'redistribute static route-map RMAP_2' in result.commands"
+      - "'no redistribute rip' in result.commands"
+
+- name: Configure BGP route redistribute information with operation replace (idempotent)
+  eos_bgp: *rdr_rplc
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure BGP neighbors under address family mode
+  eos_bgp: &af_nbr
+    operation: merge
+    config:
+      bgp_as: 64496
+      address_family:
+        - afi: ipv4
+          neighbors:
+            - neighbor: 203.0.113.10
+              activate: yes
+              default_originate: True
+
+            - neighbor: 192.0.2.15
+              activate: yes
+              graceful_restart: True
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'router bgp 64496' in result.commands"
+      - "'address-family ipv4' in result.commands"
+      - "'neighbor 203.0.113.10 activate' in result.commands"
+      - "'neighbor 203.0.113.10 default-originate' in result.commands"
+      - "'neighbor 192.0.2.15 activate' in result.commands"
+      - "'neighbor 192.0.2.15 graceful-restart' in result.commands"
+
+- name: Configure BGP neighbors under address family mode (idempotent)
+  eos_bgp: *af_nbr
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure networks under address family
+  eos_bgp: &af_net
+    operation: merge
+    config:
+      bgp_as: 64496
+      address_family:
+        - afi: ipv4
+          networks:
+            - prefix: 198.51.100.48
+              masklen: 28
+              route_map: RMAP_1
+
+            - prefix: 192.0.2.64
+              masklen: 27
+
+            - prefix: 203.0.113.160
+              masklen: 27
+              route_map: RMAP_2
+
+        - afi: ipv6
+          networks:
+            - prefix: "2001:db8::"
+              masklen: 33
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'router bgp 64496' in result.commands"
+      - "'address-family ipv4' in result.commands"
+      - "'network 198.51.100.48/28 route-map RMAP_1' in result.commands"
+      - "'network 192.0.2.64/27' in result.commands"
+      - "'network 203.0.113.160/27 route-map RMAP_2' in result.commands"
+      - "'address-family ipv6' in result.commands"
+      - "'network 2001:db8::/33' in result.commands"
+
+- name: Configure networks under address family (idempotent)
+  eos_bgp: *af_net
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Configure networks under address family with operation replace
+  eos_bgp: &af_net_rplc
+    operation: replace
+    config:
+      bgp_as: 64496
+      address_family:
+        - afi: ipv4
+          networks:
+            - prefix: 198.51.100.80
+              masklen: 28
+
+            - prefix: 192.0.2.64
+              masklen: 27
+
+            - prefix: 203.0.113.192
+              masklen: 27
+
+        - afi: ipv6
+          networks:
+            - prefix: "2001:db8:1000::"
+              masklen: 37
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - '"router bgp 64496" in result.commands'
+      - '"address-family ipv4" in result.commands'
+      - '"network 198.51.100.80/28" in result.commands'
+      - '"network 203.0.113.192/27" in result.commands'
+      - '"no network 198.51.100.48/28" in result.commands'
+      - '"no network 203.0.113.160/27" in result.commands'
+      - '"address-family ipv6" in result.commands'
+      - '"network 2001:db8:1000::/37" in result.commands'
+      - '"no network 2001:db8::/33" in result.commands'
+
+- name: Configure networks under address family with operation replace (idempotent)
+  eos_bgp: *af_net_rplc
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- name: Override all the exisiting BGP config
+  eos_bgp:
+    operation: override
+    config:
+      bgp_as: 64497
+      router_id: 192.0.2.10
+      log_neighbor_changes: True
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'no router bgp 64496' in result.commands"
+      - "'router bgp 64497' in result.commands"
+      - "'router-id 192.0.2.10' in result.commands"
+      - "'bgp log-neighbor-changes' in result.commands"
+
+- name: Teardown
+  eos_bgp: &rm
+    operation: delete
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - "'no router bgp 64497' in result.commands"
+
+- name: Teardown again (idempotent)
+  eos_bgp: *rm
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == false'
+
+- debug: msg="END eos cli/eos_bgp.yaml on connection={{ ansible_connection }}"
diff --git a/test/units/modules/network/eos/fixtures/eos_bgp_config.cfg b/test/units/modules/network/eos/fixtures/eos_bgp_config.cfg
new file mode 100644
index 00000000000..3df62417d1f
--- /dev/null
+++ b/test/units/modules/network/eos/fixtures/eos_bgp_config.cfg
@@ -0,0 +1,24 @@
+router bgp 64496
+   bgp router-id 192.0.2.1
+   bgp log-neighbor-changes
+   neighbor 198.51.100.102 remote-as 64498
+   neighbor 198.51.100.102 timers 300 360
+   neighbor 192.0.2.111 remote-as 64496
+   neighbor 192.0.2.111 update-source Ethernet1
+   neighbor 203.0.113.5 remote-as 64511
+   neighbor 203.0.113.5 maximum-routes 500
+   redistribute ospf route-map RMAP_1
+   address-family ipv4
+      neighbor 198.51.100.102 activate
+      neighbor 198.51.100.102 graceful-restart
+      neighbor 198.51.100.102 default-originate
+      neighbor 198.51.100.102 weight 25
+      neighbor 192.0.2.111 activate
+      neighbor 192.0.2.111 default-originate
+      network 192.0.2.0/27 route-map RMAP_1
+      network 198.51.100.0/24 route-map RMAP_2
+   !
+   address-family ipv6
+      network 2001:db8:8000::/34
+      network 2001:db8:c000::/34
+
diff --git a/test/units/modules/network/eos/test_eos_bgp.py b/test/units/modules/network/eos/test_eos_bgp.py
new file mode 100644
index 00000000000..d39f80feebe
--- /dev/null
+++ b/test/units/modules/network/eos/test_eos_bgp.py
@@ -0,0 +1,197 @@
+#
+# (c) 2019, Ansible by Red Hat, inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+#
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible.module_utils.network.eos.providers.cli.config.bgp.process import Provider
+from ansible.modules.network.eos import eos_bgp
+from .eos_module import TestEosModule, load_fixture
+
+
+class TestFrrBgpModule(TestEosModule):
+    module = eos_bgp
+
+    def setUp(self):
+        super(TestFrrBgpModule, self).setUp()
+        self._bgp_config = load_fixture('eos_bgp_config.cfg')
+
+    def test_eos_bgp(self):
+        obj = Provider(params=dict(config=dict(bgp_as=64496, router_id='192.0.2.2', networks=None,
+                                               address_family=None), operation='merge'))
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, ['router bgp 64496', 'router-id 192.0.2.2', 'exit'])
+
+    def test_eos_bgp_idempotent(self):
+        obj = Provider(params=dict(config=dict(bgp_as=64496, router_id='192.0.2.1',
+                                               networks=None, address_family=None), operation='merge'))
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, [])
+
+    def test_eos_bgp_remove(self):
+        obj = Provider(params=dict(config=dict(bgp_as=64496, networks=None, address_family=None), operation='delete'))
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, ['no router bgp 64496'])
+
+    def test_eos_bgp_neighbor(self):
+        obj = Provider(params=dict(config=dict(bgp_as=64496, neighbors=[dict(neighbor='198.51.100.12', remote_as=64498)],
+                                               networks=None, address_family=None),
+                                   operation='merge'))
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, ['router bgp 64496', 'neighbor 198.51.100.12 remote-as 64498', 'exit'])
+
+    def test_eos_bgp_neighbor_idempotent(self):
+        neighbors = [dict(neighbor='198.51.100.102', remote_as=64498, timers=dict(keepalive=300, holdtime=360)),
+                     dict(neighbor='203.0.113.5', remote_as=64511, maximum_prefix=500)]
+        obj = Provider(params=dict(config=dict(bgp_as=64496, neighbors=neighbors, networks=None, address_family=None),
+                                   operation='merge'))
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, [])
+
+    def test_eos_bgp_network(self):
+        obj = Provider(
+            params=dict(config=dict(bgp_as=64496, networks=[dict(prefix='203.0.113.0', masklen=24, route_map='RMAP_1')],
+                                    address_family=None),
+                        operation='merge'))
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(sorted(commands), sorted(['router bgp 64496', 'network 203.0.113.0/24 route-map RMAP_1', 'exit']))
+
+    def test_eos_bgp_network_idempotent(self):
+        obj = Provider(
+            params=dict(config=dict(bgp_as=64496, networks=[dict(prefix='192.0.2.0', masklen=27, route_map='RMAP_1'),
+                                                            dict(prefix='198.51.100.0', masklen=24, route_map='RMAP_2')],
+                                    address_family=None),
+                        operation='merge'))
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, [])
+
+    def test_eos_bgp_redistribute(self):
+        rd_1 = dict(protocol='rip', route_map='RMAP_1')
+
+        config = dict(bgp_as=64496, redistribute=[rd_1], networks=None, address_family=None)
+
+        obj = Provider(params=dict(config=config, operation='merge'))
+
+        commands = obj.render(self._bgp_config)
+        cmd = ['router bgp 64496', 'redistribute rip route-map RMAP_1', 'exit']
+        self.assertEqual(sorted(commands), sorted(cmd))
+
+    def test_eos_bgp_redistribute_idempotent(self):
+        rd_1 = dict(protocol='ospf', route_map='RMAP_1')
+        config = dict(bgp_as=64496, redistribute=[rd_1], networks=None, address_family=None)
+
+        obj = Provider(params=dict(config=config, operation='merge'))
+
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, [])
+
+    def test_eos_bgp_address_family_neighbors(self):
+        af_nbr_1 = dict(neighbor='198.51.100.104', default_originate=True, activate=True)
+        af_nbr_2 = dict(neighbor='198.51.100.105', activate=True, weight=30, graceful_restart=True)
+
+        config = dict(bgp_as=64496, address_family=[dict(afi='ipv4', neighbors=[af_nbr_1, af_nbr_2])],
+                      networks=None)
+
+        obj = Provider(params=dict(config=config, operation='merge'))
+
+        commands = obj.render(self._bgp_config)
+        cmd = ['router bgp 64496', 'address-family ipv4', 'neighbor 198.51.100.104 activate',
+               'neighbor 198.51.100.104 default-originate', 'neighbor 198.51.100.105 weight 30',
+               'neighbor 198.51.100.105 activate', 'neighbor 198.51.100.105 graceful-restart', 'exit', 'exit']
+        self.assertEqual(sorted(commands), sorted(cmd))
+
+    def test_eos_bgp_address_family_neighbors_idempotent(self):
+        af_nbr_1 = dict(neighbor='198.51.100.102', activate=True, graceful_restart=True, default_originate=True, weight=25)
+        af_nbr_2 = dict(neighbor='192.0.2.111', activate=True, default_originate=True)
+        config = dict(bgp_as=64496, address_family=[dict(afi='ipv4', neighbors=[af_nbr_1, af_nbr_2])],
+                      networks=None)
+
+        obj = Provider(params=dict(config=config, operation='merge'))
+
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, [])
+
+    def test_eos_bgp_address_family_networks(self):
+        net = dict(prefix='203.0.113.128', masklen=26, route_map='RMAP_1')
+        net2 = dict(prefix='203.0.113.192', masklen=26, route_map='RMAP_2')
+
+        config = dict(bgp_as=64496, address_family=[dict(afi='ipv4', networks=[net, net2])],
+                      networks=None)
+
+        obj = Provider(params=dict(config=config, operation='merge'))
+
+        commands = obj.render(self._bgp_config)
+        cmd = ['router bgp 64496', 'address-family ipv4', 'network 203.0.113.128/26 route-map RMAP_1',
+               'network 203.0.113.192/26 route-map RMAP_2', 'exit', 'exit']
+        self.assertEqual(sorted(commands), sorted(cmd))
+
+    def test_eos_bgp_address_family_networks_idempotent(self):
+        net = dict(prefix='2001:db8:8000::', masklen=34, route_map=None)
+        net2 = dict(prefix='2001:db8:c000::', masklen=34, route_map=None)
+
+        config = dict(bgp_as=64496, address_family=[dict(afi='ipv6', networks=[net, net2])],
+                      networks=None)
+
+        obj = Provider(params=dict(config=config, operation='merge'))
+
+        commands = obj.render(self._bgp_config)
+        self.assertEqual(commands, [])
+
+    def test_eos_bgp_operation_override(self):
+        net_1 = dict(prefix='2001:0db8:0800::', masklen=38, route_map='RMAP_1')
+        net_2 = dict(prefix='2001:0db8:1c00::', masklen=38, route_map='RMAP_2')
+        nbr_1 = dict(neighbor='203.0.113.111', remote_as=64511, update_source='Ethernet2')
+        nbr_2 = dict(neighbor='203.0.113.120', remote_as=64511, timers=dict(keepalive=300, holdtime=360))
+        af_nbr_1 = dict(neighbor='203.0.113.111', activate=True)
+        af_nbr_2 = dict(neighbor='203.0.113.120', activate=True, default_originate=True)
+
+        af_1 = dict(afi='ipv4', neighbors=[af_nbr_1, af_nbr_2])
+        af_2 = dict(afi='ipv6', networks=[net_1, net_2])
+        config = dict(bgp_as=64496, neighbors=[nbr_1, nbr_2], address_family=[af_1, af_2],
+                      networks=None)
+
+        obj = Provider(params=dict(config=config, operation='override'))
+        commands = obj.render(self._bgp_config)
+
+        cmd = ['no router bgp 64496', 'router bgp 64496', 'neighbor 203.0.113.111 remote-as 64511',
+               'neighbor 203.0.113.111 update-source Ethernet2', 'neighbor 203.0.113.120 remote-as 64511',
+               'neighbor 203.0.113.120 timers 300 360', 'address-family ipv4',
+               'neighbor 203.0.113.111 activate', 'neighbor 203.0.113.120 default-originate', 'neighbor 203.0.113.120 activate',
+               'exit', 'address-family ipv6', 'network 2001:0db8:0800::/38 route-map RMAP_1',
+               'network 2001:0db8:1c00::/38 route-map RMAP_2',
+               'exit', 'exit']
+
+        self.assertEqual(sorted(commands), sorted(cmd))
+
+    def test_eos_bgp_operation_replace(self):
+        net = dict(prefix='203.0.113.0', masklen=27, route_map='RMAP_1')
+        net2 = dict(prefix='192.0.2.32', masklen=29, route_map='RMAP_2')
+        net_3 = dict(prefix='2001:db8:8000::', masklen=34, route_map=None)
+        net_4 = dict(prefix='2001:db8:c000::', masklen=34, route_map=None)
+
+        af_1 = dict(afi='ipv4', networks=[net, net2])
+        af_2 = dict(afi='ipv6', networks=[net_3, net_4])
+
+        config = dict(bgp_as=64496, address_family=[af_1, af_2], networks=None)
+        obj = Provider(params=dict(config=config, operation='replace'))
+        commands = obj.render(self._bgp_config)
+
+        cmd = ['router bgp 64496', 'address-family ipv4', 'network 203.0.113.0/27 route-map RMAP_1',
+               'network 192.0.2.32/29 route-map RMAP_2', 'no network 192.0.2.0/27', 'no network 198.51.100.0/24',
+               'exit', 'exit']
+
+        self.assertEqual(sorted(commands), sorted(cmd))
+
+    def test_eos_bgp_operation_replace_with_new_as(self):
+        nbr = dict(neighbor='203.0.113.124', remote_as=64496, update_source='Ethernet3')
+
+        config = dict(bgp_as=64497, neighbors=[nbr], networks=None, address_family=None)
+        obj = Provider(params=dict(config=config, operation='replace'))
+        commands = obj.render(self._bgp_config)
+
+        cmd = ['no router bgp 64496', 'router bgp 64497', 'neighbor 203.0.113.124 remote-as 64496',
+               'neighbor 203.0.113.124 update-source Ethernet3', 'exit']
+
+        self.assertEqual(sorted(commands), sorted(cmd))