I’ve spent the last couple of years at Red Hat helping customers automate their networks with Ansible. If there is one thing that I’ve learned during that time, it is that network automation is not as easy as many would have you believe. That is not to say that tools like Ansible are not good tools for automation or that anyone is trying to sell you snake oil, but I believe that there is a fundamental impedance mismatch in translating the success Ansible has had with automating systems to automate networks.
Part of this disconnect stems from a fundamental mis-understanding of the capabilities that Ansible provides. According to Red Hat, Ansible is a “common language to describe your infrastructure.” In practice, however, Ansible is more of a framework that brings an inventory of things together with a set of modules, plugins, and Jinja2 capabilities that perform operations on those things. The language, rendered in YAML or JSON, just passes key/value pairs between the modules, plugins, and Jinja2 capabilities. (Yes, that’s a simple description of a complex tool, but one that is accurate to illustrate the point of this and subsequent blogs.)
That is not to say that Ansible is not a powerful framework, but it has no native linguistic ability to describe a network. When I say an “inventory of things,” it is because Ansible really does not care what that thing is. Because of its agentless approach, it can talk to many things: systems, network devices, clouds, lightbulbs, etc. This is a great capability and part of why Ansible is so popular, but Ansible truly does not know one thing from another. It has no innate prowess for automating networks. It is simply a tool for automating what an operator does task by task. You cannot “describe” what you want OSPF to look like on your network. You simply provide a bunch of key/value pairs that get passed to the devices on your network through modules in hopes of yielding the OSPF configuration that you want.
To illustrate this, let’s look at configuring two simple settings on an IOS device: hostname and NTP servers. Using Ansible parlance, we’ll describe the desired end state of the hostname of a particular device. Hostname is a great use case because it is a scalar (i.e. a single value). To change the hostname, the Ansible ios_config module does a simple textual compare of the configuration. If ‘hostname newname’ is not present, it sends that line to the device. Since hostname is a scaler, the old hostname gets replaced by the desired hostname.
A list of NTP servers, however, is more difficult. Say you’ve set the NTP server to 1.1.1.1 with:
- ios_config:
lines:
- ntp server 1.1.1.1
Now you want to change your NTP server to 2.2.2.2, so you do:
- ios_config:
lines:
- ntp server 2.2.2.2
Simple, right? But the problem is that you would end up with 2 NTP servers in the configuration:
ntp server 1.1.1.1
ntp server 2.2.2.2
This is because the Ansible ios_config module does not see `ntp server 2.2.2.2` present in the configuration, so it sends the line. Since ntp server is a list, however, it adds a new NTP server instead of replacing the existing one, giving you 2 NTP servers (one that you do not want). To end up with just 2.2.2.2 as your NTP server, you would have to know that 1.1.1.1 was already defined as an NTP server and explicitly remove it… exactly what an operator would do. This is also the case with ACLs, IP prefix-lists, and any other list in IOS. Ansible does not have a native way to describe the desired end state of something simple like NTP servers on a network device, much less something more complex like OSPF, QoS, or Multicast.
Does that mean that Ansible is not a great tool for network automation? No, but like any tool, it needs to be used for the right task and can only complete a complex task when used in concert with other tools. As a framework, it is not a complete solution.
The intent of this blog series is to go beyond the hype and simple demonstrations prevalent in network automation conversations today and to dive more deeply into how and why to automate your network operations. In the next installment, I’ll talk about data models and why they are a critical piece of any automation framework.
That is not to say that Ansible is not a powerful framework, but it has no native linguistic ability to describe a network. When I say an “inventory of things,” it is because Ansible really does not care what that thing is. Because of its agentless approach, it can talk to many things: systems, network devices, clouds, lightbulbs, etc. This is a great capability and part of why Ansible is so popular, but Ansible truly does not know one thing from another. It has no innate prowess for automating networks. It is simply a tool for automating what an operator does task by task. You cannot “describe” what you want OSPF to look like on your network. You simply provide a bunch of key/value pairs that get passed to the devices on your network through modules in hopes of yielding the OSPF configuration that you want.
Configuring settings on an IOS device
To illustrate this, let’s look at configuring two simple settings on an IOS device: hostname and NTP servers. Using Ansible parlance, we’ll describe the desired end state of the hostname of a particular device. Hostname is a great use case because it is a scalar (i.e. a single value). To change the hostname, the Ansible ios_config module does a simple textual compare of the configuration. If ‘hostname newname’ is not present, it sends that line to the device. Since hostname is a scaler, the old hostname gets replaced by the desired hostname.
A list of NTP servers, however, is more difficult. Say you’ve set the NTP server to 1.1.1.1 with:
- ios_config:
lines:
- ntp server 1.1.1.1
Now you want to change your NTP server to 2.2.2.2, so you do:
- ios_config:
lines:
- ntp server 2.2.2.2
Simple, right? But the problem is that you would end up with 2 NTP servers in the configuration:
ntp server 1.1.1.1
ntp server 2.2.2.2
This is because the Ansible ios_config module does not see `ntp server 2.2.2.2` present in the configuration, so it sends the line. Since ntp server is a list, however, it adds a new NTP server instead of replacing the existing one, giving you 2 NTP servers (one that you do not want). To end up with just 2.2.2.2 as your NTP server, you would have to know that 1.1.1.1 was already defined as an NTP server and explicitly remove it… exactly what an operator would do. This is also the case with ACLs, IP prefix-lists, and any other list in IOS. Ansible does not have a native way to describe the desired end state of something simple like NTP servers on a network device, much less something more complex like OSPF, QoS, or Multicast.
The intent of this blog series is to go beyond the hype and simple demonstrations prevalent in network automation conversations today and to dive more deeply into how and why to automate your network operations. In the next installment, I’ll talk about data models and why they are a critical piece of any automation framework.