Ansible Declares Declarative Intent

by | Jul 24, 2017 | Developer Productivity

Back in January 2016, Ansible delivered the first integration of network support into the Ansible 2.0 code base.  The initial introduction of network support was originally conceived to help operators focus on being able to execute configuration changes on network devices with a set of imperative-based configuration modules.

The configuration modules, by and large, focused on pushing configuration statements to devices. It was a small step, but an important one in the journey towards full configuration management of physical network devices.

Since then, we have turned our attention to looking at how to better help organizations become more agile in their ability to manage configurations.  Over the course of this year, we  plan to phase in a more intelligent approach to building and configuring network elements.  This approach will combine elements of idempotent configuration management with ephemeral state validation to handle updating network element’s active configuration.

These changes are designed to enable network operations teams to focus on describing the desired configuration and operating state of the network infrastructure.  The benefits of this approach allow network operations teams to focus less on defining the low-level commands to achieve their desired results.  This shift in focus can streamline organizations ability to push configuration changes into the infrastructure implementing a more agile and scalable operations environment.

Resources

Some early adopters have already begun to implement a more declarative approach to configuring network resources.  In this context, resources can be defined as logical components of the network operating system configuration.  For instance, interfaces or routing policy or protocol configuration are all good examples of resources.   

Beginning with Ansible 2.3, Ansible began supporting declarative configuration modules.  Unlike modules that define a sequence of steps to achieve an end state, declarative configuration modules allow playbook designers to define the desired end configuration state.  From there, the module will take the appropriate set of sequential steps, based on the current configuration, to arrive at the desired end state.

- name: configure the “management” vrf
   ios_vrf:
     name: management
     description: oob mgmt vrf
     state: present

The example above provides a module for configuring virtual routing and forwarding (VRF) resources on Cisco IOS devices.  The declarative nature of the module will transform the module as defined above into a set of calls to the device based on what the current device configuration is.  It will loosely perform the following sequential tasks:

  1. Connect to device and get current list of configured VRFs
  2. Translate the desired values into a set of appropriate configuration commands
  3. Validate if the desired values are in the current configuration
  4. If a match is found, do nothing
  5. If a match is not found, add the configuration to the node

This works well for pushing new resources to network elements.  It also works to remove resources from the current device configuration by simply changing the state value to absent.

Resource Collections

There are many resources in a network configuration that contain multiple definitions.  Expanding on the example above, it is not uncommon–in fact it’s quite common–to have many VRFs defined in the network device configuration.  When designing playbooks, it’s often the intention of the playbook designer to be able to reconcile the list of resources without prior knowledge of what has been previously configured on the device.

The declarative modules currently in development support this inherently.  Assuming there is a finite set of VRFs that should be configured on a given network element, the declarative modules can be used to declare this intent.

- name: configure the “management” vrf
  ios_vrf:
    vrfs:
      - { name: management, description: management vrf }
      - { name: services, description: services vrf }
      - { name: operations }
    description: oob mgmt vrf
    purge: yes
    state: present

The modified example above focuses on the set of VRF resources that should be applied to a given network element.  In this case, the module will be responsible for ensuring the final running configuration of the device includes three VRFs (management, services, operations) and removes all other VRF configurations.

In this way, playbook designers have a powerful tool to push configuration changes to network devices.  By providing modules that allow network operations teams to define the overall set of resources to be configured on the network element, teams can focus on pushing the desired final state to network elements instead of having to decipher how to reach the end state through a series of incremental steps.

Ephemeral State

One of the significant drawbacks to automating the configuration of network elements is rooted in ephemeral state validation.  Consider for a moment the overall goal of configuration automation is to automatically build (or tear down) parts of the whole to achieve a working end to end service or application.  

In the case of network devices, the first step is to begin to transform from imperative configuration management (having to define each step of the process) to declarative configuration management (defining the end state and letting the platform figure out the steps to get there).   

Ansible is taking this a step further to allow network operations teams to begin to move beyond simple declarative configuration of resources and resource collections to being able to define the intended operating state of those resources.

Consider the goal of automating the configuration of a network element interface configuration.  The declarative module might look something like the following:

- name: configure an access IP interface
  ios_interface:
    name: Ethernet2/1
    description: web access services 
    ipv4: 172.26.4.1/24
    mtu: 9000
    state: present

As discussed earlier, the Ansible playbook can easily accept the declarative configuration module and use it to build an appropriate configuration on the end device.  In addition, the module operates idempotently so continued pushes will validate the configuration on the network element and make no changes if the current configuration is valid.

While this works well and makes it easy to define the desired configuration state of the interface in this case, if the physical cable was never (or worse wrongly) plugged into the port, the device will not have achieved the operational (ephemeral) state intended.  When considering this task as part of a larger playbook that configures networks and applications and services, it becomes obvious the desired application or service will not fully function.  

Further compounding this problem is that repeated attempts of running the playbook will not cause any further changes to the network element configuration since the current configuration is both valid and matches the desired configuration state.  In short, the configuration did not match the intention of the playbook designer.

To address this situation, we are focusing on adding intent or behavior driven arguments to network modules for validating device ephemeral state.  Here is the updated module:

- name: configure an access IP interface
  net_interface:
    name: Ethernet2/1
    description: web access services 
    ipv4: 172.26.4.1/24
    mtu: 9000
    state: present
    lldp_enabled: yes
    oper_state: up
    neighbors: 
      - host: websrv01
        port: eth0

The updated module now allows network operations teams to define both configuration state and intended state.  It will now first validate the active configuration and make appropriate changes to the network element as necessary to achieve the desired configuration state.  Once the configuration state is consistent with the desired state, the module will then check the ephemeral state of the interface, looking at, in this example, the operational state of the interface (must be up) and the LLDP neighbor(s) (must be connected to host websrv01 on port eth0).

Declarative intent modules begin to open a whole new approach to defining network configuration state for operations teams.  Playbooks can be built to define both the desired configuration state AND the desired operational state.  This allows playbooks to define the intended operating behavior of the network infrastructure and provides operations teams with a high degree of certainty that, at the conclusion of a playbook run, the infrastructure is operating as it was designed and intended.

We expect the transition to managing network configuration state using declarative modules to change the way organizations approach configuration management.  Network operators can begin to focus on the collective network deployment without needing to address incompatibilities and/or incongruencies between vendor implementations.  In addition, declarative modules bring the ancillary benefit of delivering a consistent, repeatable configuration pattern regardless of the device operating system that can be delivered at scale.  

We are phasing in these modules starting with the Ansible 2.3 release.  As these modules are being designed and developed, we encourage you to get involved to help drive the module definitions.  We love to hear your feedback, thoughts, ideas and welcome you to participate in the networking community.