Red Hat Ansible Engine v2.9 introduced the first set of Resource Modules that make network automation easier and more consistent, especially in multi-vendor environments. These network resource specific and opinionated Ansible modules help us avoid creating overly complex Jinja2 templates to render and push network configurations, thereby easing the adoption of network automation both in green and brownfield environments. The resource modules, along with the tools provided in ansible.utils, are highly focused on allowing the end user to manipulate network configuration as “structured data” and not have to worry about network platform specific details.
In the past, we have gone through resource modules that facilitate managing BGP, OSPFv2, ACLs and VLANS configurations on network devices. In this blog post, we’ll cover the newly added route maps resource modules using cisco.nxos.nxos_route_maps
as an example.
Route maps are used to define which routes from a source routing protocol are to be distributed to a target routing protocol. It also allows filtering routes that are sent or received between BGP peers. Every route map can have multiple entries, with each entry having a sequence number and an action (the “permit” or “deny” clause) associated with it. Each of these contain an ordered set of match criterias (the “match” clause) that are successively evaluated. Route maps are similar to access lists (ACLs), but are more powerful and flexible than them and can be used to match routes based on conditions that ACLs cannot verify. In fact, route maps have the ability to leverage an existing ACL as a match criteria within it. They can also modify information associated with a route while redistributing it into the target protocol using the “set” clause.
Route maps are an extremely powerful feature in the networking landscape, and with all its bells and whistles, it can be difficult to manually manage them, especially in a production environment where the margin of error is very small. This is where the route maps resource modules can step in! Let us walk through several examples on how to leverage and make the best use of them in real world scenarios.
The Certified Ansible Content Collection
The route maps resource module is available for the following Ansible-maintained platforms on both Automation Hub (supported) and Ansible Galaxy (community):
Platform | Full Collection Path | Automation Hub Link | Ansible Galaxy Link |
Arista EOS |
| ||
Cisco IOS |
| ||
Cisco NX-OS |
| ||
Juniper JunOS |
| ||
VyOS |
|
Note: For Cisco IOS-XR, it is suggested to use the cisco.iosxr.iosxr_config module and the platform’s Route Policy Language (RPL) to configure route maps.
This blog uses the nxos_route_maps
module from the cisco.nxos
Collection and a Cisco Nexus 9000v running NX-OS 9.3.6 as reference for all the configuration management operations in the examples.
For more information on Ansible Content Collections, please refer to the following documentation: https://docs.ansible.com/ansible/latest/user_guide/collections_using.html.
Getting started - Managing Route Maps configuration with Ansible
The route maps resource module provides the same level of functionality that a user can achieve when configuring manually on a device running Cisco NX-OS. But combined with the power of Ansible’s fact gathering capabilities and resource module approach, this is more closely aligned with how network professionals work day to day. Like all supported resource modules, the route_maps
module also supports the following seven states:
- merged
- replaced
- overridden
- deleted
- gathered
- rendered
- parsed
Let us take a look at them one by one.
Below is the existing state of route map configurations on our target device.
nxos9k# show running-config | section “^route-map”
route-map rmap1 permit 10
description rmap1-10-permit
match as-number 65564
match as-path Allow40
match ip address acl_1
route-map rmap1 deny 20
description rmap1-20-deny
match ip address prefix-list AllowPrefix1 AllowPrefix2
match community BGPCommunity1 BGPCommunity2
set dampening 30 1500 10000 120
route-map rmap2 permit 10
Using state gathered - Building an Ansible inventory
Resource modules have the ability to convert the network platform specific configuration into ''structured data'' as per the module’s argument specifications by using state: gathered
. This is equivalent to gathering Ansible facts for any network resource. In the following example, we will use state: gathered
in a task to read the route-maps configuration as structured data and write it to host_vars
. If you are new to the concept of Ansible inventory and want to learn more about group_vars
and host_vars
, please refer to the Ansible User Guide: Inventory.
Ansible Playbook Example
---
- name: Gather route-maps configuration as structured data
hosts: nxos
gather_facts: no
vars:
destination: "{{ inventory_dir }}/host_vars/{{ inventory_hostname }}"
tasks:
- name: Use Route Maps resource module to gather existing configuration
cisco.nxos.nxos_route_maps:
state: gathered
register: route_maps
- name: Create inventory directory
ansible.builtin.file:
path: "{{ destination }}"
state: directory
- name: Write the Route Maps configuration to a file
ansible.builtin.copy:
content: "{{ {'route_maps': route_maps['gathered']} | to_nice_yaml }}"
dest: "{{ destination }}/route_maps.yaml"
(Gist source available here)
Execute the above playbook with the following command:
$ ansible-playbook example.yaml
Now let’s take a look at the contents of the file that was created. We should see the existing device configuration as structured data in it. Getting started with this module in a brownfield environment is as simple as this!
$ cat ./ansible_inventory/host_vars/nxos-9k/route_maps.yaml
route_maps:
- entries:
- action: permit
description: rmap1-10-permit
match:
as_number:
asn:
- '65564'
as_path:
- Allow40
ip:
address:
access_list: acl_1
sequence: 10
- action: deny
description: rmap1-20-deny
match:
community:
community_list:
- BGPCommunity1
- BGPCommunity2
ip:
address:
prefix_lists:
- AllowPrefix1
- AllowPrefix2
sequence: 20
set:
dampening:
half_life: 30
max_suppress_time: 120
start_reuse_route: 1500
start_suppress_route: 10000
route_map: rmap1
- entries:
- action: permit
sequence: 10
route_map: rmap2
(Gist source available here)
Using state merged - Pushing configuration changes
The state: merged
is used to merge the task input with the existing on-box configuration. This does not affect configurations that are not specified in the task. Let’s take a look at how we can use this.
Modify stored route-maps data
In the previous example, we saw how we can gather existing information from the target device as structured data and store it as a flat file. Now, let us update that data and push it back to the device with state: merged
. Notice how we can immediately start using the gathered facts as a source of truth for a network resource.
We make the following updates:
- Append “BGPCommunity3” to the community-list in match->community->community_list in sequence 20 of route-map rmap1.
- Update sequence 10 of route-map rmap2 with “match” and “set” clauses.
- Add new sequence 40 with action “deny” and “match” clauses.
The updated flat-file should now look like this:
route_maps:
- entries:
- action: permit
description: rmap1-10-permit
match:
as_number:
asn:
- '65564'
as_path:
- Allow40
ip:
address:
access_list: acl_1
sequence: 10
- action: deny
description: rmap1-20-deny
match:
community:
community_list:
- BGPCommunity1
- BGPCommunity2
- BGPCommunity3
ip:
address:
prefix_lists:
- AllowPrefix1
- AllowPrefix2
sequence: 20
set:
dampening:
half_life: 30
max_suppress_time: 120
start_reuse_route: 1500
start_suppress_route: 10000
route_map: rmap1
- entries:
- action: permit
sequence: 10
continue_sequence: 40
match:
ipv6:
address:
prefix_lists:
- AllowIPv6Prefix
interfaces: Ethernet1/1
set:
as_path:
prepend:
as_number:
- “65563”
- “65568”
- “65569”
extcomm_list: BGPExtCommunity
- sequence: 40
action: deny
description: rmap2-40-deny
match:
route_types:
- level-1
- level-2
tags:
- 2
ip:
multicast:
rp:
prefix: 192.0.2.0/24
rp_type: ASM
source: 203.0.113.0/24
group_range:
first: 239.0.0.1
last: 239.255.255.255
route_map: rmap2
(Gist source available here)
We are ready to push this data back to the device with the following task:
- name: Merge with the existing on-box running configuration
cisco.nxos.nxos_route_maps:
config: "{{ route_maps }}"
state: merged
But hold on; wouldn’t it be even better if we could verify that our route-map’s source of truth is indeed in sync with the actual device configuration after we send out the updates? We can do this by re-running the same merged task and asserting that the second one did not make any changes.
The final merged playbook should look like this:
- name: Merge with the existing on-box running configuration
cisco.nxos.nxos_route_maps:
config: "{{ route_maps }}"
state: merged
- name: Merge with the existing on-box configuration (IDEMPOTENT)
cisco.nxos.nxos_route_maps:
config: "{{ route_maps }}"
state: merged
register: merged
- name: Asset that the source of truth is in sync with device config
assert:
that:
- merged.changed == False
- merged.commands == []
Once the playbook run is complete, we can verify the updated configuration on the network device. As evident, all the intended updates have been correctly pushed.
nxos9k# show running-config | section ^route-map
route-map rmap1 permit 10
description rmap1-10-permit
match ip address acl_1
match as-path Allow40
match as-number 65564
route-map rmap1 deny 20
description rmap1-20-deny
match ip address prefix-list AllowPrefix1 AllowPrefix2
match community BGPCommunity1 BGPCommunity2 BGPCommunity3
set dampening 30 1500 10000 120
route-map rmap2 permit 10
match ipv6 address prefix-list AllowIPv6Prefix
match interface Ethernet1/1
continue 40
set as-path prepend 65563 65568 65569
set extcomm-list BGPExtCommunity delete
route-map rmap2 deny 40
description rmap2-40-deny
match tag 2
match route-type level-1 level-2
match ip multicast source 203.0.113.0/24 group-range 239.0.0.1 to 239.255.255.255 rp 192.0.2.0/24 rp-type ASM
Note: Cisco NX-OS allows executing route-map command without the action clause and a sequence number. For example, “route-map rmap1” by itself is a valid command. Once executed, this shows up in running-config as “route-map rmap1 permit 10”. As such, the nxos_route_maps
resource module also allows the user to do the same. However, such a task will NOT be idempotent or function properly.
Using state replaced - Pushing configuration changes
The state: replaced
replaces the on-box configuration subsection with the provided configuration subsection in the task. This is how a user can expect this state to behave for the route_maps
modules:
- For listed route maps:
- all entries (identified by the sequence number) that are in running-config but not in the task, will be negated
- superfluous attributes within existing entries will also be negated
- The following items will be “merged” with the existing on-box configuration:
- new route-maps
- new entries within an existing route map
- new attributes in existing entries
- existing route maps that are not specified in the task will remain unaffected
Alright, now it’s time to see this in action. For this scenario, let’s make the following changes to the source of truth:
- Remove the entry with sequence number 10 from route-map rmap1.
- Remove “BGPCommunity1” and ”BGPCommunity2” from match->community->community_list in sequence 20 of route-map rmap1.
- Add “acl_1” to ip->address->access_list in sequence 20 of route-map rmap1.
- Update match->ip->multicast to use a single multicast group prefix “239.0.0.0/24” instead of a range, in sequence 40 of rmap2.
- Add a new entry with sequence 30 in rmap1.
- Add a new route-map rmap3.
The updated flat file should look like this:
route_maps:
- entries:
- action: deny
description: rmap1-20-deny
match:
community:
community_list:
- BGPCommunity2
ip:
address:
prefix_lists:
- AllowPrefix1
- AllowPrefix2
access_list: acl_1
sequence: 20
set:
dampening:
half_life: 30
max_suppress_time: 120
start_reuse_route: 1500
start_suppress_route: 10000
route_map: rmap1
- entries:
- action: permit
sequence: 10
continue_sequence: 40
match:
ipv6:
address:
prefix_lists:
- AllowIPv6Prefix
interfaces:
- Ethernet1/1
set:
as_path:
prepend:
as_number:
- "65563"
- "65568"
- "65569"
extcomm_list: BGPExtCommunity
- sequence: 30
action: permit
description: rmap2-30-permit
match:
ipv6:
address:
prefix_lists:
- AllowIPv6Prefix2
- sequence: 40
action: deny
description: rmap2-40-deny
match:
route_types:
- level-1
- level-2
tags:
- 2
ip:
multicast:
rp:
prefix: 192.0.2.0/24
rp_type: ASM
source: 203.0.113.0/24
group:
prefix: 239.0.0.0/24
route_map: rmap2
- entries:
- action: deny
description: rmap3-20-deny
sequence: 20
route_map: rmap3
(Gist source available here)
We now push this structured configuration data to the target device and verify its correctness with the following set of tasks. Notice that these are identical to the previous merged
tasks, except the value state
parameter, which is now replaced
.
- name: Replace provided route-maps with given configuration
cisco.nxos.nxos_route_maps:
config: "{{ route_maps }}"
state: replaced
- name: Replace provided route-maps with given configuration
(IDEMPOTENT)
cisco.nxos.nxos_route_maps:
config: "{{ route_maps }}"
state: replaced
register: replaced
- name: Asset that the source of truth is in sync with device config
assert:
that:
- replaced.changed == False
- replaced.commands == []
Inspecting the updated configuration on the device after the playbook was run, we can see that all extraneous configurations have been removed and updates have been applied.
nxos9k# show running-config | section “^route-map”
route-map rmap1 deny 20
description rmap1-20-deny
match ip address acl_1
match ip address prefix-list AllowPrefix1 AllowPrefix2
match community BGPCommunity2
set dampening 30 1500 10000 120
route-map rmap2 permit 10
match ipv6 address prefix-list AllowIPv6Prefix
match interface Ethernet1/1
continue 40
set as-path prepend 65563 65568 65569
set extcomm-list BGPExtCommunity delete
route-map rmap2 permit 30
description rmap2-30-permit
match ipv6 address prefix-list AllowIPv6Prefix2
route-map rmap2 deny 40
description rmap2-40-deny
match tag 2
match route-type level-1 level-2
match ip multicast source 203.0.113.0/24 group 239.0.0.0/24 rp 192.0.2.0/24 rp-type ASM
route-map rmap3 deny 20
description rmap3-20-deny
Using state overridden - Pushing configuration changes
With state: overridden
, Ansible overrides all the on-device configuration of a particular resource with the provided configuration in the task.
Let’s assume some unwanted route map configuration got pushed to our target device. As a result, the device started behaving erroneously. Now instead of manually inspecting and reverting these changes, we can push the existing structured data in our flat-file with state: overridden
, thereby enforcing the source-of-truth and removing all unwanted configuration lines.
This state behaves similar to state: replaced
, except that overridden affects all the route maps on the device, which means, route maps that are in running-config but not in the task input will be removed.
For this example, let us consider the following to be the route map configuration on the target device after the unwanted changes were pushed:
nxos9k# show running-config | section “^route-map”
route-map rmap1 deny 20
description rmap1-20-deny
match ip address acl_1
match ip address prefix-list AllowPrefix1 AllowPrefix2
match community BGPCommunity1
set dampening 30 1500 10000 120
route-map rmap2 permit 10
match ipv6 address prefix-list AllowIPv6Prefix
match interface Ethernet1/1
continue 40
set as-path prepend 65563 65568 65569
set extcomm-list BGPExtCommunity delete
route-map rmap2 permit 30
description rmap2-30-permit
match ipv6 address prefix-list AllowIPv6Prefix1
route-map rmap2 deny 40
description rmap2-40-deny
match tag 2
match route-type level-1 level-2
match ip multicast source 198.51.100.0/24 group 239.0.0.0/24 rp 192.0.2.0/24 rp-type ASM
route-map rmap3 deny 20
description rmap3-20-deny
route-map rmap4 deny 10
description rmap4-10-deny
match route-type level-2
Before we remediate the changes and align the on-box route-map configuration with its source of truth, let us find out how the current (running) config has deviated from the intended config. To do that, we will leverage the very useful fact_diff
and to_paths
plugins from the ansible.utils
Collection.
- name: Gather current route-map configuration from the device
cisco.nxos.nxos_route_maps:
state: gathered
register: result
- name: Find out diff between intended and current configuration
ansible.utils.fact_diff:
before: "{{ route_maps|ansible.utils.to_paths }}"
after: "{{ result['gathered']|ansible.utils.to_paths }}"
(Gist source available here)
In the above tasks, we first gather the existing route-map configuration from the target device as structured data and then compare it with the source of truth in the flat-file. Running this playbook renders the deviation in a dot delimited format which can also be saved in a file for auditing purposes in the future.
(Gist source available here)
Okay, so now that we know what changed, let’s restore the route map configuration to it’s correct state with the following:
- name: Override all route-maps config with given configuration
cisco.nxos.nxos_route_maps:
config: "{{ route_maps }}"
state: overridden
- name: Override all route-maps config with given configuration
(IDEMPOTENT)
cisco.nxos.nxos_route_maps:
config: "{{ route_maps }}"
state: overridden
register: overridden
- name: Asset that the source of truth is in sync with device config
assert:
that:
- overridden.changed == False
- overridden.commands == []
Once this playbook completes the execution, we can verify by inspecting the device that the route map configurations have been reverted to their correct state:
nxos9k# show running-config | section “^route-map”
route-map rmap1 deny 20
description rmap1-20-deny
match ip address acl_1
match ip address prefix-list AllowPrefix1 AllowPrefix2
match community BGPCommunity2
set dampening 30 1500 10000 120
route-map rmap2 permit 10
match ipv6 address prefix-list AllowIPv6Prefix
match interface Ethernet1/1
continue 40
set as-path prepend 65563 65568 65569
set extcomm-list BGPExtCommunity delete
route-map rmap2 permit 30
description rmap2-30-permit
match ipv6 address prefix-list AllowIPv6Prefix2
route-map rmap2 deny 40
description rmap2-40-deny
match tag 2
match route-type level-1 level-2
match ip multicast source 203.0.113.0/24 group 239.0.0.0/24 rp 192.0.2.0/24 rp-type ASM
route-map rmap3 deny 20
description rmap3-20-deny
Using state deleted - Deleting configuration changes
Now that we have seen how to configure and modify route maps, let’s take a look at how to delete them with state: deleted
.
Deleting specified route map(s)
We have the ability to delete individual route maps from the device with state: deleted
. For example, the following task will remove route map rmap2 (with both its entries) from the target device.
- name: Delete rmap2 from target device
cisco.nxos.nxos_route_maps:
config:
- route_map: rmap2
state: deleted
However, running such a task would mean that we have to manually update the flat-file (a.k.a, source of truth) so that it is in sync with the device configuration. For deleting hand picked route maps or sequences, it is recommended to first delete them from the source of truth and then use state: overridden
on it.
Deleting all route maps from the device
When no route maps are specified in the task, state: deleted
would remove all the route maps from running-config.
- name: Delete all route-maps from target device
cisco.nxos.nxos_route_maps:
state: deleted
Running the above on our target device would delete all three route maps - rmap1, rmap2 and rmap3.
nxos9k# show running-config | section “^route-map”
nxos9k#
Using state rendered - Development and working offline
The state: rendered
transforms the task input into platform specific CLI commands without requiring a connection to the end device. When run with this state, the module does not make any changes to the target and always assumes that there are no existing configurations to compare against.
Let’s reuse the route maps configuration from the state: merged
example and pass it to the nxos_route_maps
module setting state: rendered
.
- name: Render platform specific configuration lines (offline)
cisco.nxos.nxos_route_maps:
config: "{{ route_maps }}"
state: rendered
Running this task gives us the following output:
"rendered": [
"route-map rmap1 permit 10",
"match as-number 65564",
"match as-path Allow40",
"match ip address acl_1",
"description rmap1-10-permit",
"route-map rmap1 deny 20",
"match community BGPCommunity1 BGPCommunity2 BGPCommunity3",
"match ip address prefix-list AllowPrefix1 AllowPrefix2",
"description rmap1-20-deny",
"set dampening 30 1500 10000 120",
"route-map rmap2 permit 10",
"match interface Ethernet1/1",
"match ipv6 address prefix-list AllowIPv6Prefix",
"set as-path prepend “65563” “65568” “65569”",
"continue 40",
"set extcomm-list BGPExtCommunity delete",
"route-map rmap2 deny 40",
"match ip multicast source 203.0.113.0/24 group-range 239.0.0.1 to 239.255.255.255 rp 192.0.2.0/24 rp-type ASM",
"match route-type level-1 level-2",
"match tag 2",
"description rmap2-40-deny"
]
Using state parsed - Development and working offline
This state reads the configuration from the running_config
option and transforms it into structured data as per the module’s argspec. Like state: rendered
, this state also doesn’t require a connection to the target device. This is very helpful for experimenting, troubleshooting or creating offline sources of truth for network resources.
- name: Parse externally provided route-maps configuration
cisco.nxos.nxos_route_maps:
running_config: |
route-map rmap1 permit 10
match as-number 65564
match as-path Allow40
match ip address acl_1
description rmap1-10-permit
route-map rmap1 deny 20
match community BGPCommunity1 BGPCommunity2
match ip address prefix-list AllowPrefix1 AllowPrefix2
description rmap1-20-deny
set dampening 30 1500 10000 120
state: parsed
(Gist source available here)
Running this task renders the specified platform specific route map configuration lines as malleable structured data:
"parsed": [
{
"entries": [
{
"action": "permit",
"description": "rmap1-10-permit",
"match": {
"as_number": {
"asn": [
"65564"
]
},
"as_path": [
"Allow40"
],
"ip": {
"address": {
"access_list": "acl_1"
}
}
},
"sequence": 10
},
{
"action": "deny",
"description": "rmap1-20-deny",
"match": {
"community": {
"community_list": [
"BGPCommunity1",
"BGPCommunity2"
]
},
"ip": {
"address": {
"prefix_lists": [
"AllowPrefix1",
"AllowPrefix2"
]
}
}
},
"sequence": 20,
"set": {
"dampening": {
"half_life": 30,
"max_suppress_time": 120,
"start_reuse_route": 1500,
"start_suppress_route": 10000
}
}
}
],
"route_map": "rmap1"
}
]
(Gist source available here)
Takeaways & Next Steps
As shown above, with the help of the resource modules for route maps, automation can be simplified. Users don't need to create complicated Jinja2 templates or bother much about implementation details for each platform. Engineers can just enter the important data into a simple data model broken out into each resource module. 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 (structured data) 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:
執筆者紹介
チャンネル別に見る
自動化
テクノロジー、チームおよび環境に関する IT 自動化の最新情報
AI (人工知能)
お客様が AI ワークロードをどこでも自由に実行することを可能にするプラットフォームについてのアップデート
オープン・ハイブリッドクラウド
ハイブリッドクラウドで柔軟に未来を築く方法をご確認ください。
セキュリティ
環境やテクノロジー全体に及ぶリスクを軽減する方法に関する最新情報
エッジコンピューティング
エッジでの運用を単純化するプラットフォームのアップデート
インフラストラクチャ
世界有数のエンタープライズ向け Linux プラットフォームの最新情報
アプリケーション
アプリケーションの最も困難な課題に対する Red Hat ソリューションの詳細
オリジナル番組
エンタープライズ向けテクノロジーのメーカーやリーダーによるストーリー
製品
ツール
試用、購入、販売
コミュニケーション
Red Hat について
エンタープライズ・オープンソース・ソリューションのプロバイダーとして世界をリードする Red Hat は、Linux、クラウド、コンテナ、Kubernetes などのテクノロジーを提供しています。Red Hat は強化されたソリューションを提供し、コアデータセンターからネットワークエッジまで、企業が複数のプラットフォームおよび環境間で容易に運用できるようにしています。
言語を選択してください
Red Hat legal and privacy links
- Red Hat について
- 採用情報
- イベント
- 各国のオフィス
- Red Hat へのお問い合わせ
- Red Hat ブログ
- ダイバーシティ、エクイティ、およびインクルージョン
- Cool Stuff Store
- Red Hat Summit