From cb206abea36427ca4292b5a14986d6316224d7bc Mon Sep 17 00:00:00 2001 From: Samer Deeb Date: Thu, 11 Jan 2018 09:40:39 -0800 Subject: [PATCH] Support advertised networks in bgp protocol (#34728) Signed-off-by: Samer Deeb --- .../module_utils/network/mlnxos/mlnxos.py | 2 +- .../modules/network/mlnxos/mlnxos_bgp.py | 99 +++++++++++++++---- .../mlnxos/fixtures/mlnxos_bgp_show.cfg | 29 ++++-- .../modules/network/mlnxos/test_mlnxos_bgp.py | 23 ++++- 4 files changed, 123 insertions(+), 30 deletions(-) diff --git a/lib/ansible/module_utils/network/mlnxos/mlnxos.py b/lib/ansible/module_utils/network/mlnxos/mlnxos.py index b389d35fbf8..9f4d95939f9 100644 --- a/lib/ansible/module_utils/network/mlnxos/mlnxos.py +++ b/lib/ansible/module_utils/network/mlnxos/mlnxos.py @@ -141,7 +141,7 @@ def get_interfaces_config(module, interface_type, flags=None, json_fmt=True): def get_bgp_summary(module): - cmd = "show ip bgp summary" + cmd = "show running-config protocol bgp" return show_cmd(module, cmd, json_fmt=False, fail_on_error=False) diff --git a/lib/ansible/modules/network/mlnxos/mlnxos_bgp.py b/lib/ansible/modules/network/mlnxos/mlnxos_bgp.py index 4cb43103c6a..4d6fe9e2dc8 100644 --- a/lib/ansible/modules/network/mlnxos/mlnxos_bgp.py +++ b/lib/ansible/modules/network/mlnxos/mlnxos_bgp.py @@ -41,6 +41,9 @@ options: description: - Neighbor IP address. required: true + networks: + description: + - List of advertised networks. state: description: - BGP state. @@ -57,6 +60,8 @@ EXAMPLES = """ - remote_as: 321 neighbor: 10.3.3.4 state: present + networks: + - 172.16.1.0/24 """ RETURN = """ @@ -69,6 +74,7 @@ commands: - exit - router bgp 172 router-id 2.3.4.5 force - router bgp 172 neighbor 2.3.4.6 remote-as 173 + - router bgp 172 network 172.16.1.0 /24 """ import re @@ -80,10 +86,13 @@ from ansible.module_utils.network.mlnxos.mlnxos import BaseMlnxosModule class MlnxosBgpModule(BaseMlnxosModule): - LOCAL_AS_REGEX = \ - r"BGP router identifier ([0-9\.]+), local AS number (\d+)" - NEIGHBOR_REGEX = \ - r"^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+\d+\s+(\d+)" + LOCAL_AS_REGEX = re.compile(r'^\s+router bgp\s+(\d+).*') + ROUTER_ID_REGEX = re.compile( + r'^\s+router bgp\s+(\d+).*router-id\s+(\S+)\s+.*') + NEIGHBOR_REGEX = re.compile( + r'^\s+router bgp\s+(\d+).*neighbor\s+(\S+)\s+remote\-as\s+(\S+).*') + NETWORK_REGEX = re.compile( + r'^\s+router bgp\s+(\d+).*network\s+(\S+)\s+(\S+).*') def init_module(self): """ initialize module @@ -97,6 +106,7 @@ class MlnxosBgpModule(BaseMlnxosModule): router_id=dict(), neighbors=dict(type='list', elements='dict', options=neighbor_spec), + networks=dict(type='list', elements='str'), state=dict(choices=['present', 'absent'], default='present'), ) argument_spec = dict() @@ -108,28 +118,47 @@ class MlnxosBgpModule(BaseMlnxosModule): def get_required_config(self): module_params = self._module.params - bgp_params = { - 'as_number': module_params['as_number'], - 'router_id': module_params['router_id'], - 'state': module_params['state'], - } - neighbors = module_params['neighbors'] or [] - req_neighbors = bgp_params['neighbors'] = [] + req_neighbors = list() + self._required_config = dict( + as_number=module_params['as_number'], + router_id=module_params['router_id'], + state=module_params['state'], + neighbors=req_neighbors, + networks=module_params['networks']) + neighbors = module_params['neighbors'] or list() for neighbor_data in neighbors: req_neighbors.append( (neighbor_data['neighbor'], neighbor_data['remote_as'])) - self.validate_param_values(bgp_params) - self._required_config = bgp_params + self.validate_param_values(self._required_config) def _set_bgp_config(self, bgp_config): - match = re.search(self.LOCAL_AS_REGEX, bgp_config, re.M) - if match: - self._current_config['router_id'] = match.group(1) - self._current_config['as_number'] = int(match.group(2)) - matches = re.findall(self.NEIGHBOR_REGEX, bgp_config, re.M) or [] + lines = bgp_config.split('\n') + self._current_config['router_id'] = None + self._current_config['as_number'] = None neighbors = self._current_config['neighbors'] = [] - for match in matches: - neighbors.append((match[0], int(match[1]))) + networks = self._current_config['networks'] = [] + for line in lines: + if line.startswith('#'): + continue + if not self._current_config['as_number']: + match = self.LOCAL_AS_REGEX.match(line) + if match: + self._current_config['as_number'] = int(match.group(1)) + continue + if not self._current_config['router_id']: + match = self.ROUTER_ID_REGEX.match(line) + if match: + self._current_config['router_id'] = match.group(2) + continue + match = self.NEIGHBOR_REGEX.match(line) + if match: + neighbors.append((match.group(2), int(match.group(3)))) + continue + match = self.NETWORK_REGEX.match(line) + if match: + network = match.group(2) + match.group(3) + networks.append(network) + continue def _get_bgp_summary(self): return get_bgp_summary(self._module) @@ -162,7 +191,10 @@ class MlnxosBgpModule(BaseMlnxosModule): if req_router_id and req_router_id != curr_route_id or bgp_removed: self._commands.append('router bgp %d router-id %s force' % (as_number, req_router_id)) + self._generate_neighbors_cmds(as_number, bgp_removed) + self._generate_networks_cmds(as_number, bgp_removed) + def _generate_neighbors_cmds(self, as_number, bgp_removed): req_neighbors = self._required_config['neighbors'] curr_neighbors = self._current_config.get('neighbors', []) if not bgp_removed: @@ -180,6 +212,33 @@ class MlnxosBgpModule(BaseMlnxosModule): 'router bgp %s neighbor %s remote-as %s' % (as_number, neighbor, remote_as)) + def _generate_networks_cmds(self, as_number, bgp_removed): + req_networks = self._required_config['networks'] or [] + curr_networks = self._current_config.get('networks', []) + if not bgp_removed: + for network in curr_networks: + if network not in req_networks: + net_attrs = network.split('/') + if len(net_attrs) != 2: + self._module.fail_json( + msg='Invalid network %s' % network) + + net_address, netmask = net_attrs + cmd = 'router bgp %s no network %s /%s' % ( + as_number, net_address, netmask) + self._commands.append(cmd) + + for network in req_networks: + if bgp_removed or network not in curr_networks: + net_attrs = network.split('/') + if len(net_attrs) != 2: + self._module.fail_json( + msg='Invalid network %s' % network) + net_address, netmask = net_attrs + cmd = 'router bgp %s network %s /%s' % ( + as_number, net_address, netmask) + self._commands.append(cmd) + def _generate_no_bgp_cmds(self): as_number = self._required_config['as_number'] curr_as_num = self._current_config.get('as_number') diff --git a/test/units/modules/network/mlnxos/fixtures/mlnxos_bgp_show.cfg b/test/units/modules/network/mlnxos/fixtures/mlnxos_bgp_show.cfg index ee5c738c87c..d0849a9900a 100644 --- a/test/units/modules/network/mlnxos/fixtures/mlnxos_bgp_show.cfg +++ b/test/units/modules/network/mlnxos/fixtures/mlnxos_bgp_show.cfg @@ -1,6 +1,23 @@ -BGP router identifier 1.2.3.4, local AS number 172 -BGP table version is 1, main routing table version 1 --------- - -- ------- ------- ------ --- ---- ------- ------------ -Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd --------- - -- ------- ------- ------ --- ---- ------- ------------ -10.2.3.4 4 173 0 0 1 0 0 Never ACTIVE +## +## Running database "initial" +## Generated at 2018/01/10 23:13:17 +0000 +## Hostname: r-neo-sw12 +## + +## +## Running-config temporary prefix mode setting +## +no cli default prefix-modes enable + +## +## BGP configuration +## + protocol bgp + router bgp 172 vrf default + router bgp 172 vrf default router-id 1.2.3.4 force + router bgp 172 vrf default neighbor 10.2.3.4 remote-as 173 + router bgp 172 vrf default network 172.16.1.0 /24 +## +## Persistent prefix mode setting +## +cli default prefix-modes enable diff --git a/test/units/modules/network/mlnxos/test_mlnxos_bgp.py b/test/units/modules/network/mlnxos/test_mlnxos_bgp.py index 5a33d1f7715..1f1a3cd9d70 100644 --- a/test/units/modules/network/mlnxos/test_mlnxos_bgp.py +++ b/test/units/modules/network/mlnxos/test_mlnxos_bgp.py @@ -39,7 +39,8 @@ class TestMlnxosBgpModule(TestMlnxosModule): def test_bgp_no_change(self): neighbor = dict(remote_as=173, neighbor='10.2.3.4') set_module_args(dict(as_number=172, router_id='1.2.3.4', - neighbors=[neighbor])) + neighbors=[neighbor], + networks=['172.16.1.0/24'])) self.execute_module(changed=False) def test_bgp_remove(self): @@ -60,11 +61,27 @@ class TestMlnxosBgpModule(TestMlnxosModule): neighbors = [dict(remote_as=173, neighbor='10.2.3.4'), dict(remote_as=175, neighbor='10.2.3.5')] set_module_args(dict(as_number=172, router_id='1.2.3.4', - neighbors=neighbors)) + neighbors=neighbors, + networks=['172.16.1.0/24'])) commands = ['router bgp 172 neighbor 10.2.3.5 remote-as 175'] self.execute_module(changed=True, commands=commands) def test_bgp_del_neighbor(self): - set_module_args(dict(as_number=172)) + set_module_args(dict(as_number=172, + networks=['172.16.1.0/24'])) commands = ['router bgp 172 no neighbor 10.2.3.4 remote-as 173'] self.execute_module(changed=True, commands=commands) + + def test_bgp_add_network(self): + neighbors = [dict(remote_as=173, neighbor='10.2.3.4')] + set_module_args(dict(as_number=172, router_id='1.2.3.4', + neighbors=neighbors, + networks=['172.16.1.0/24', '172.16.2.0/24'])) + commands = ['router bgp 172 network 172.16.2.0 /24'] + self.execute_module(changed=True, commands=commands) + + def test_bgp_del_network(self): + neighbors = [dict(remote_as=173, neighbor='10.2.3.4')] + set_module_args(dict(as_number=172, neighbors=neighbors)) + commands = ['router bgp 172 no network 172.16.1.0 /24'] + self.execute_module(changed=True, commands=commands)