Getting Started With BGP Global Resource Modules

April 6, 2021 by Rohit Thakur

With the increasing size and complexity of modern enterprise networks, the demand on simplifying the networks management becomes more intense. The introduction of resources modules with Ansible Engine 2.9 provide a path to users to ease the network management, especially across multiple different product vendors.

In the past, we’ve already covered resource modules for OSPF management and for ACLs. However, simplifying network management is not limited to rather local network setups or intra domain routing only. “Border Gateway Protocol (BGP) is a standardized exterior gateway protocol designed to exchange routing and reachability information between autonomous systems (AS) on the internet. The protocol is often classified as a path vector protocol but is sometimes also classed as a distance-vector routing protocol.” It is used in larger network setups, as the NetworkWorld so aptly observes:

BGP has been called the glue of the Internet and the postal service of the internet. One comparison likens BGP to GPS applications on mobile phones.

Managing BGP manually for a network device can be a very difficult and tedious task, and more often this needs to be performed carefully, as the manual process is more prone to human error.

This blog post goes through the BGP resource module for the Juniper Networks Junos  Network Management Platform. We will walk through several examples and describe the use cases for each state parameter and how we envision these being used in real world scenarios.

 

BGP Global resource modules example: Junos

The goal of BGP resource modules is to make sure configurations are consistently applied across the infrastructure with less effort. It simplifies management and makes it faster and easier to scale without worrying about the actual implementation details of the network platforms working under the hood.

Ansible content refers to Ansible Playbooks, modules, module utilities and plugins. Basically all of the Ansible tools that users utilize to create their Ansible automation. BGP resource module is part of Ansible Content Collections. To learn more about Ansible Content Collections, you can check out two of our blogs: Getting Started with Ansible Content Collections and The Future of Ansible Content Delivery.

Let’s have a closer look at how the BGP resource modules work. As an example, we pick the junos_bgp_global resource module. In this blog, we’ll be using a Junos device model vSRX with version 18.4R1-S3.1 for all the configuration management specific operations. Also, to better showcase the effect of the modules, we will start with some BGP global config specific attributes already configured. 

 

Accessing and using the Junos Collection

To download the Junos Collection, refer to Automation Hub (fully supported, requires a Red Hat Ansible Automation Platform subscription) or Ansible Galaxy (upstream community supported):

To learn more about how to configure downloading via the ansible.cfg file or requirements.yml file, please refer to the blog, Hands On With Ansible Collections.

Before we get started, let’s quickly explain the rationale behind naming the network resource modules. Notice for resource modules that configure BGP routes, the newly added modules will be named based on their behaviour or impact. To manage global BGP config we have separate resource module junos_bgp_global and for address family configuration we have separate resource module junos_bgp_address_family. This was done so that those using existing network modules would not have their Ansible Playbooks stop working and have sufficient time to migrate to the new network automation modules.

A module to configure global BGP attributes  is also available for the following supported platforms:

The BGP resource module provides the same level of functionality that a user can achieve when configuring manually on the JuniperJunos device with all advantages of Ansible automation, plus with an added edge of Ansible fact gathering and resource module approach, which is more closely aligned with network professionals day to day working.

In Ansible core 2.10 and newer, short names are supported with the meta/runtime.yml file for any Ansible Collection.  For more information on redirects read the documentation here.

Long name: junipernetworks.junos.junos_bgp_global
Short Name: junipernetworks.junos.bgp_global

However we will maintain backwards compatibility to make sure your automation does not lose functionality.

There is also an online webinar that goes into more details on webinar naming and fully qualified collection names (FQCN): Webinar: Migrating to Ansible Collections.

Use Case: BGP configuration changes

Using state gathered - Building an Ansible inventory

Resource modules allow the user to read in existing network configuration and convert that into a structured data model. The state: gathered is the equivalent for gathering Ansible facts for this specific resource. This example will read in the existing network configuration and store it as a flat-file.

Here is an Ansible Playbook example of using state: gathered and storing the result as YAML into host_vars. If you are new to Ansible inventory and want to learn about group_vars and host_vars, please refer to the documentation here.

Some Ansible Network platforms support multiple connection types, privilege escalation or other options. To learn more about different platform options, please refer to platform  options documentation.

---
- name: convert configured BGP resource to structured data
  hosts: junos
  vars:
    inventory_dir: "lab_inventory"
    inventory_hostname: "junos"
  gather_facts: false
  tasks:

  - name: Use the bgp_global resource module to gather the current config
    junipernetworks.junos.junos_bgp_global:
      state: gathered
    register: bgp_global

  - name: Create inventory directory
    file:
      path: "{{ inventory_dir }}/host_vars/{{ inventory_hostname }}"
      state: directory

  - name: Write the BGP GLOBAL configuration to a file
    copy:
      content: "{{ {'bgp_global': bgp_global['gathered']} | to_nice_yaml }}"
      dest: "{{ inventory_dir }}/host_vars/{{ inventory_hostname }}/bgp_global.yaml"

Gist source available here

Execute the Ansible Playbook with the ansible-playbook command:

$ ansible-playbook example.yml

Here is the data structure that was created from reading/gathered operation in a brown-field configuration:

$ cat lab_inventory/host_vars/junos/bgp_global.yaml 
bgp_global:
  accept_remote_nexthop: true
  add_path_display_ipv4_address: true
  advertise_from_main_vpn_tables: true
  advertise_inactive: true
  as_number: '65534'
  asdot_notation: true
  authentication_algorithm: md5
  bgp_error_tolerance:
     malformed_route_limit: 20000000
  bmp:
     monitor: true
  damping: true
  description: This is configured with Junos_bgp resource module
  egress_te_sid_stats: true
  groups:
  - name: internal
    out_delay: 12
  hold_time: 5

You can check out the full detailed listing of the output of this example in the state: gathered reference gist.

 

Using state merged - Pushing configuration changes

The state merged will take your Ansible configuration data (i.e. Ansible variables) and merges them into the network device’s running configuration. This will not affect existing configuration not specified in your Ansible configuration data. Let’s walk through an example.

We will modify the flat-file created in the first example with a configuration to be merged. Here are the most important pieces:

groups:
 - name: internal
   out_delay: 22
 - name: external
   out_delay: 20
hold_time: 15
holddown_all_stale_labels: true
include_mp_next_hop: true
loops: 5
no_advertise_peer_as: true
no_aggregator_id: true

Now let’s create an Ansible Playbook to merge this new configuration into the network device’s running configuration:

---
- name: Merged state play
  hosts: junos
  gather_facts: false
  tasks:
    - name: Merge BGP config with device existing BGP global config
      junipernetworks.junos.junos_bgp_global:
        state: merged
        config: "{{ bgp_global }}"

Execute the Ansible Playbook with the ansible-playbook command:

$ ansible-playbook merged.yml

And, once we run the respective merge play, all of the provided parameters will be configured on the Junos appliance with Ansible changed=True.

Note the network configuration after the merge operation:

vagrant@vsrx# show protocols bgp
include-mp-next-hop;
group internal {
    out-delay 22;
}
group external {
    out-delay 20;
}

Note that this listing only shows a few highlights; the full listing is available in the merged gist.

Let’s take a look at what has changed through this operation: if we go through the device output, there are a few observations:

  • Attribute group with name ‘external’ was added to the BGP group list. 
  • Mtu-discovery, keep and no-precision-timers got configured.
  • If there was an already configured BGP group with out_delay and the user wanted to update any parameter for that particular group, then the user can also use the merged state to update the group under BGP.

With the second run, the respective merge play runs again and Ansible charm of idempotency comes to picture. If nothing’s changed, play run results into changed=False, which confirms to the user that all of the provided configurations in the play are already configured on the Junos appliance.

 

Using state replaced - Replacing configuration 

If the user wants to re-configure the Junos appliance entirely existing BGP configuration with the provided BGP configuration, then the resource module replaced state comes into picture.

The scope of the replaced operation is up to BGP global attributes accept address_family or bgp_group address family. In the case of Juniper Junos vSRX only a single BGP instance is supported. As a result, the replaced state acts similar to the overridden state. For that reason a dedicated overridden state is not required with the Junos modules. Other network platforms that support multiple BGP instances do have the overridden state operation.

Using the replaced state, a user can override all BGP resource global attributes with user-provided BGP configuration. Since this resource module state overrides all pre-existing attributes of the resource module, the replaced state should be used cautiously; if all the configurations are mistakenly replaced with the play input configuration, it might create unnecessary issues for the network administrators. 

In this scenario, BGP with ‘n’ number GROUPs are already configured on the Junos appliance, and now the user wants to update the GROUP list with a new set of GROUPs and discard all the already configured BGP GROUPs. Here, the resource module replaced state will be an ideal choice and, as the name suggests, the replaced state will replace BGP existing GROUP list with a new set of GROUPSs given as input by the user.

If a user tries to configure any new BGP GROUP/attribute that’s not already pre-configured on the device, it’ll act as a merged state and the junos_bgp_global module will try to configure the BGP GROUPs given as input by the user inside the replace play.

We will modify the flat-file created in the first example:

groups:
 - name: inter
   out_delay: 22
   description: "This is internal group"
   keep: all
 - name: external
   out_delay: 20
 - name: extbound
     accept_remote_nexthop: true
     add_path_display_ipv4_address: true
     out_delay: 10
     description: "External bound"
     keep: all

Check out the full input config structure if you want to learn more details.

Again, we create an Ansible Playbook to replace this new configuration into the network device’s running configuration:

---
- name: Replaced state play
  hosts: junos
  gather_facts: false
  tasks:
    - name: Replace running BGP global config with provided config
      junipernetworks.junos.junos_bgp_global:
        state: replaced
        config: "{{ bgp_global }}"

Once we run the respective replaced play, all of the provided parameters will override all the existing BGP resource specific config on the Junos appliance with Ansible changed=True.

The Juniper network device configuration after the replaced operation:

vagrant@vsrx# show protocols bgp
group extbound {
    description "External bound";
    accept-remote-nexthop;
    keep all;
    out-delay 10;
    add-path-display-ipv4-address;
}

Check out the corresponding gist for full Juniper device configuration.

If we dig into the above output, we note the following changes:

  • Replaced negates all of the pre-existing BGP resource-specific attributes (except address family in case that exists).
  • For any non-existing BGP specific attribute, the replaced state will configure the BGP in the same manner as the merged state. In the above example, a new BGP group configured for BGP groups list.

With the second run of the above play, there are no changes reported which satisfies the Ansible idempotency.

 

Using state purged - Purge configuration 

we can purge the pre-configured BGP instance attribute with the purge operational state for the user.

Purging ALL BGP(GLOBAL and ADDRESS-FAMILY) config in one go leads to deleting all the pre-configured BGP specific attributes from the Junos appliance. But that said, this is a very critical delete operation and if not used judiciously, it has the power to delete all pre-configured BGP (GLOBAL and ADDRESS-FAMILY) and can result in the production environment with the router having no pre-configured  BGP attributes.

Let’s create an Ansible Playbook to purge running BGP configuration.

Note: Running BGP configuration includes bgp address family resources.

vagrant@vsrx# show routing-options autonomous-system    
65534 loops 3 asdot-notation;

vagrant@vsrx# show protocols bgp 
damping;
bgp-error-tolerance {
    malformed-route-limit 20000000;
}
family inet {
    unicast {
        local-ipv4-address 9.9.9.9;
        extended-nexthop;
        extended-nexthop-color;
    }

Check out the full running-config including address-family resource.

---
- name: Purged state play
  hosts: Junos
  gather_facts: false
  tasks:
    - name: Purge Complete BGP config
      junipernetworks.junos.junos_bgp_global:
        state: purged

After we execute the playbook, the network device configuration changed:

vagrant@vsrx# show protocols bgp  
vagrant@vsrx#
vagrant@vsrx# show routing-options autonomous-system
vagrant@vsrx# 

Make sure to look at the full listing of the changed values. If we dig into the above output briefly, we can see that all the BGP resource-specific config(bgp-global, bgp-address-family, bgp-groups, bgp-groups-address-family) has been removed from the network configuration.

 

Using state deleted - Delete configuration 

Now that we’ve talked about how we can purge BGP instances attributes on the Junos appliance by using junos_bgp_global resource module purged state, I’d like to note that purged operation is only something that needs to be performed in extreme use cases. You might want to use delete operation more often than purge, as delete will only affect global configuration and won’t affect
address-family resource configuration (which also includes BGP groups AF or neighbors AF).

Deleting BGP global configuration leads to deleting all the pre-configured BGP GLOBAL resource specific attributes from the Junos appliance and won’t delete address-family resource attributes.

Note: Running BGP configuration includes BGP address family resources.

vagrant@vsrx# show routing-options autonomous-system    
65534 loops 3 asdot-notation;

vagrant@vsrx# show protocols bgp 
bgp-error-tolerance {
    malformed-route-limit 20000000;
}
family inet {
    unicast {
        local-ipv4-address 9.9.9.9;

Check out the full running-config including address-family resource.

Let’s create an Ansible Playbook to delete running BGP global resource configuration.

---
- name: Deleted state play
  hosts: Junos
  gather_facts: false
  tasks:
    - name: Delete BGP global resource config
      junipernetworks.junos.junos_bgp_global:
        state: deleted

After we execute the playbook, the network device configuration changed:

$vagrant@vsrx# show protocols bgp  
vagrant@vsrx#
vagrant@vsrx# show routing-options autonomous-system
vagrant@vsrx# vagrant@vsrx# show protocols bgp 
family inet {
    unicast {
        local-ipv4-address 9.9.9.9;
        extended-nexthop;
        extended-nexthop-color;
    }

Make sure to look at the full listing of the changed values. If we dig into the above output briefly, we can observe following:

  •  all the BGP global resource-specific config has been removed from the network configuration.
  • Unlike purged state operation, deleted state operation doesn’t remove address-family resource config, which is still part of Junos network device running config. This means purged impacts the whole BGP resource while delete impacts only the global level attributes and doesn’t affect BGP address-family resources. This is what differentiates purged and deleted state operations from each other.

If you want to see more differences between purged and deleted operations functionality, you can observe the commands/XML tree generated in the above listed changed values for both operations.

 

Using state rendered - Development and working offline

Ansible renders the provided configuration in the task in the device-native format (for example, Junos XML). Ansible returns this rendered configuration in the rendered key in the result. Note this state does not communicate with the network device and can be used offline.

To have a config to be rendered, modify the YAML file created in the first scenario. For example, if this is the junos_bgp_global module, you can just add a few more attributes to show we change the data model yet again.

groups:
 - name: inter
   out_delay: 22
   description: "This is internal group"
   keep: "all"

See the full listing in the corresponding rendered gist.

We create a playbook to execute this:

---
- name: Rendered state play
  hosts: junos
  gather_facts: false
  tasks:
    - name: Render the provided configuration
      junipernetworks.junos.junos_bgp_global:
        config: ""
        state: rendered

This produces the following output:

"rendered": [
       <nc:protocols xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">
          <nc:bgp>
            <nc:accept-remote-nexthop/>
            <nc:add-path-display-ipv4-address/>

Check out the corresponding gist for more details.

If we dig into the above output, we can see that nothing has changed at all; rendered doesn’t even require the connection establishment with an actual network device.

 

Using state parsed - Development and working offline

Ansible parses the configuration from the running_configuration option into Ansible structured data in the parsed key in the result. Note this does not gather the configuration from the network device, so this state can be used offline.

As the config to be parsed we take XML format configuration as device support netconf:

<?xml version="1.0" encoding="UTF-8"?>
<rpc-reply message-id="urn:uuid:0cadb4e8-5bba-47f4-986e-72906227007f">
<configuration changed-seconds="1590139550" changed-localtime="2020-05-22 09:25:50 UTC">
<version>18.4R1-S2.4</version>
<protocols>
<bgp>

See the full listing in the corresponding parsed gist.

The playbook to apply this configuration is:

---
- name: Parsed state play
  hosts: junos
  gather_facts: false
  tasks:
    - name: Parse the provided BGP global configuration
      junipernetworks.junos.junos_bgp_global:
        running_config: "{{lookup('file', './bgp_global.cfg')}}"
        state: parsed

Execute the playbook generates the structured output:

"parsed": {
         "accept_remote_nexthop": true,
        "add_path_display_ipv4_address": true,
        "advertise_bgp_static": {
            "policy": "static-to-bgp"
        },
        "advertise_from_main_vpn_tables": true,
        "advertise_inactive": true,

See the full listing in the corresponding  gist.

If we dig into the above output, we can see that nothing has changed at all; parsed operation doesn’t even require the connection establishment with an actual network device.
Note: parsed input to be provided as value to running_config key.

 

Takeaways & Next Steps

As shown above, with the help of the resource modules management of BGP, resource-specific configurations can be greatly simplified. Users don't need to bother much about BGP implementation details for each platform, they can just enter the actual data. By using the merged, replaced and overridden parameters, we allow much more flexibility for network engineers to adopt automation in incremental steps. The other operations like gathered, rendered and parsed allow a better, user friendly handling of the facts and the data managed within these tasks.

If you want to learn more about the Red Hat Ansible Automation Platform and network automation, you can check out these resources:

Share:

 

Rohit Thakur

Rohit Thakur is a Senior Software Engineer for Red Hat Ansible Automation, where he brings over 6 years in the computer software industry.


rss-icon  RSS Feed

RH-ansible-automation-platform_trial-banner
AnsibleFest-2020-banner-A