Reliable Automation
Retry Policies, Not Blind Retry
What you'll learn
- The difference between a transient failure worth retrying and a permanent one that isn't
- How to write a bounded retry loop — not blind, unlimited retrying
- Why an authentication failure should never be retried
- How to verify retry logic without a real device, using fake functions
- How this connects to the timeout vs. authentication distinction Netmiko already gave you
Listen to a guided explanation of this lesson.
Spoken narration isn't supported in this browser.
Not every failure deserves a second try
"Connecting to a Device with Netmiko" already taught you two different exceptions for two different problems: a timeout (no response — a network-path problem) and an authentication failure (a response, but rejected credentials). This lesson is about what to actually do with that distinction: a timeout is often worth trying again — the device might have been briefly busy. An authentication failure never is — the same wrong password will be wrong again, instantly, every time you retry it.
Blind retry is not reliability
Retrying everything, unconditionally, forever, isn't more reliable than not retrying at all — it just means a script that will never succeed takes much longer to fail, and a script retrying the right thing has no limit on how long it might hang. Reliable retry is bounded (a maximum number of attempts) and selective (only for failures that might actually resolve).
A bounded, selective retry wrapper
import time
def call_with_retry(connect_fn, device, max_attempts=3, delay=2):
"""
Calls connect_fn(device), retrying on a timeout up to max_attempts
times. Never retries a permission/authentication failure -- retrying
the same wrong credentials wastes time and won't change the outcome.
"""
for attempt in range(1, max_attempts + 1):
try:
return connect_fn(device)
except TimeoutError:
if attempt == max_attempts:
raise
print(f" Attempt {attempt} timed out — retrying in {delay}s...")
time.sleep(delay)
except PermissionError:
raisemax_attempts bounds how many times this will ever try — never infinite. The except TimeoutError branch waits and loops again, but only if attempts remain; the except PermissionError branch re-raises immediately, on the very first failure, no retry at all.
Verifying it without a device
Retry logic is pure control flow — it's fully testable with fake functions standing in for a real connection, no device required:
call_count = {"n": 0}
def flaky_connect(device):
call_count["n"] += 1
if call_count["n"] < 3:
raise TimeoutError("simulated timeout")
return "connected"
result = call_with_retry(flaky_connect, "SW1", max_attempts=3, delay=0)
print(result) # connected
print(call_count["n"]) # 3 -- it took three attemptsflaky_connect fails twice, then succeeds — a realistic stand-in for a device that was briefly slow. This test was actually run while writing this lesson: it prints 'connected' after exactly 3 calls, confirming the retry loop keeps going until it succeeds or runs out of attempts.
Connecting this back to Netmiko
The real connection function from "Connecting to a Device with Netmiko" already distinguishes NetmikoTimeoutException from NetmikoAuthenticationException — this lesson's pattern applies directly: wrap the ConnectHandler(...) call in this same bounded loop, retrying only the timeout branch. Lesson 5 does exactly that, folding this retry pattern into the real connection function without changing what it promises to callers — still a device in, a state or a ConnectionError out.
Key takeaways
- Reliable retry is bounded (a maximum attempt count) and selective (only for failures that might actually resolve) — never infinite, never blind.
- A timeout is worth retrying; an authentication failure never is — the same wrong credentials fail identically every time.
- call_with_retry(connect_fn, device, max_attempts, delay) wraps any connection function with this policy without changing what it returns on success.
- Retry logic is pure control flow, fully testable with fake functions that fail a controlled number of times — no device needed.
- This same pattern gets folded directly into the real Netmiko connection function in Lesson 5, wrapping only the timeout path.