This is part of a series of posts to understand Network Automation Principles.
The term idempotent
has it’s origin rooted in mathematics. It states that “if the application will have the same result if ran once or multiple times.” As an example, if you multiply a value by 1 or 0 multiple times it will result in the answer after 1 or N times, thus these are considered to be idempotent. However, if you add a result by 1, 1 or N times, it would change the value of each time, and thus not be considered idempotent.
The most common “real world” example you will will read about is calling an elevator, as described in this wikipedia quote.
The initial activation of the button moves the system into a requesting state, until the request is satisfied. Subsequent activations of the button between the initial activation and the request being satisfied have no effect, unless the system is designed to adjust the time for satisfying the request based on the number of activations.
How does this exactly get applied in Computer Science? A function is considered idempotent, if it does not result in a change after running one or multiple times. Being able to run a function with knowledge that no change will happen, unless it needs to happen, inherently makes it a safer change.
Imagine if you run a script that has ten actions, and the script fails for reason outside of your control, such as the resource not being available or the connection failing on step five. If the ten functions are not idempotent, the script would need to run from where it failed. To even accomplish this, you would either need to modify the script, or have built in other logic into the script to create a starting point, which may or may not be practical, such as if step three gathered information you needed later, you would still need to run this step no matter what. This puts the burden on the user of the script and is a less than optimal user experience. However, if all the functions were idempotent, you can simply rerun the script.
Note: While certainly not the first or only system to do so, given it’s popularity, it is worth noting that Ansible attempts to apply this technique to all the modules created.
The mechanism to create an idempotent function is to first check the state or value is whatever you intend it to be, and only if not, then make the change. As you can see in these contrived examples, the first example is not idempotent, and the second is.
>>> neighbors = ["nyc-rt01"]
>>>
>>> def update_list(neighbors, neighbor):
... neighbors.append(neighbor)
... return neighbors
...
>>> neighbors = update_list(neighbors, "nyc-rt02")
>>> neighbors = update_list(neighbors, "nyc-rt02")
>>> print(neighbors)
['nyc-rt01', 'nyc-rt02', 'nyc-rt02']
>>>
Note: The result includes nyc-rt02 multiple times.
>>> neighbors = ["nyc-rt01"]
>>>
>>> def update_list_idempotently(neighbors, neighbor):
... if neighbor not in neighbors:
... neighbors.append(neighbor)
... return neighbors
...
>>> neighbors = update_list_idempotently(neighbors, "nyc-rt02")
>>> neighbors = update_list_idempotently(neighbors, "nyc-rt02")
>>> print(neighbors)
['nyc-rt01', 'nyc-rt02']
Note: The result includes nyc-rt02 one time, even though attempting to “add nyc-rt02” multiple times.
This is often covered within the REST framework between which methods should and should not be idempotent. This screenshot taken from wikipedia can describe which are idempotent or not.
The Ansible *_config
modules are idempotent. But what does that mean when you are sending configurations via the cli? In this case, the state the module is referring to is whether or not the configuration already exists, exactly as the configuration renders in a “show run”. To reiterate, this is an exact match in the truest sense, so even though IOS will accept “int gig0/1” that will not match the configuration snippet of “interface GigabithEthernet0/1” which actually shows on the configuration, the same is true for spacing mismatches. In fact there is no relationship what so ever between what the cli will accept and what the module will send. So you can send bad commands, the module will just cause an error, or you can have a spacing or syntax mismatch that will work on the cli, but not be treated as an idempotent function by the module, so the module will push configuration changes every time.
Leveraging the Ansible ad-hoc command you can see the results of running this command three times.
Building idempotent functions will be contingent on your use case on how to actually implement. Understanding this as a concept should hopefully allow you to build better and more reliable automation.
Additionally, understanding the impact of building an idempotent function should always be considered. In the contrived example provided, it is actually far more computationally efficient to create a set from the list. So there was a method to obtain the intended end result without the computational impact. This highlights the importance on understanding your use case, and impact it can have on your application.
-Ken
Share details about yourself & someone from our team will reach out to you ASAP!