<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://www.sdnspot.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.sdnspot.com/" rel="alternate" type="text/html" /><updated>2026-07-03T10:23:11+00:00</updated><id>https://www.sdnspot.com/feed.xml</id><title type="html">SDNSpot</title><subtitle>SDNSpot — technical blog on network automation, cloud infrastructure, Python, AI, MCP integration and an honest CCIE Automation journey.
</subtitle><author><name>Balamurugan Indhiran</name></author><entry><title type="html">Halfway Through My CCIE Automation Prep, I Wondered If I’d Picked the Worst Possible Time to Study Automation</title><link href="https://www.sdnspot.com/ccie-journey/halfway-through-ccie-automation-prep/" rel="alternate" type="text/html" title="Halfway Through My CCIE Automation Prep, I Wondered If I’d Picked the Worst Possible Time to Study Automation" /><published>2026-06-30T09:00:00+00:00</published><updated>2026-06-30T09:00:00+00:00</updated><id>https://www.sdnspot.com/ccie-journey/halfway-through-ccie-automation-prep</id><content type="html" xml:base="https://www.sdnspot.com/ccie-journey/halfway-through-ccie-automation-prep/"><![CDATA[<p>When I started studying for CCIE Automation, ChatGPT was already a thing people used, but we weren’t yet in a world where AI agents write entire applications, open pull requests, and quietly judge your code style. That came later — somewhere in the middle of my prep, and it changed how I thought about the whole exercise.</p>

<p>Suddenly everyone online was talking about “vibe coding.” Just describe what you want and the AI builds it. Nobody needs to learn syntax anymore. You get the idea.</p>

<p>I won’t pretend I didn’t have a small crisis about it. I sat there one evening, deep in a Netmiko exception-handling rabbit hole, and asked myself a fairly uncomfortable question: was I spending hundreds of hours learning something AI was about to do in five minutes?</p>

<p>It wasn’t paranoia pulled out of nowhere either. Every networking vendor at the time had some version of “AI-powered operations” plastered on their homepage. It genuinely felt like I’d signed up to learn how to ride a bicycle right as everyone else started buying self-driving cars.</p>

<p>I sat with that thought for a while. And eventually I landed somewhere I didn’t expect: the journey was still worth it, just not for the reasons I assumed when I started.</p>

<h2 id="ai-is-great-at-answers-its-still-bad-at-filling-in-what-you-didnt-say">AI is great at answers. It’s still bad at filling in what you didn’t say.</h2>

<p>Here’s what became obvious pretty fast: AI produces excellent output when you ask a sharp, well-formed question. It produces mediocre output when the question is vague — and the gap between those two is almost entirely domain knowledge.</p>

<p>If I don’t actually understand routing behavior, API design, or what idempotency means in an Ansible playbook, my prompts end up sounding like “make my network automated,” which is roughly as useful as telling a mechanic to “make the car better.” You get something. It’s just not what you needed.</p>

<p>Studying automation properly didn’t just teach me tools — it taught me how to break a problem down into something specific enough to hand off, whether the thing I’m handing it off to is a colleague or a language model. Turns out that skill is exactly what makes AI useful instead of frustrating.</p>

<h2 id="you-still-have-to-read-what-it-gives-you">You still have to read what it gives you</h2>

<p>AI writes surprisingly clean code. It also writes confidently wrong code, and the two look identical at first glance.</p>

<p>In network automation specifically, whether a script “runs” was never the bar. The real questions are the boring ones: what happens if this fails halfway through a config push across forty devices? Is the rollback actually safe, or does it assume nothing went wrong? Are credentials handled properly, or hardcoded somewhere they shouldn’t be? Will this scale past the three devices I tested it on?</p>

<p>AI can help answer all of that. But I still need enough understanding to know which questions to even ask, and to catch the moment when an answer sounds right but isn’t. Maybe that changes eventually. For now, my job shifted less toward writing every line and more toward reviewing, pressure-testing, and steering what gets produced.</p>

<h2 id="the-good-questions-come-from-experience-not-from-nowhere">The good questions come from experience, not from nowhere</h2>

<p>I remember reading something from Aravind Srinivas, Perplexity’s CEO, about how the next competitive edge might be asking better questions rather than just retrieving answers faster. That stuck with me longer than I expected it to.</p>

<p>Good questions aren’t random. They come from having done the thing badly a few times first. The more I actually understood about network design and automation trade-offs, the less generic my questions to AI became.</p>

<p>Early on I’d ask something like “how do I automate VLAN configuration.” Now the question looks more like: how would you design this so it works across multiple vendors, stays idempotent, plugs into a GitOps workflow, and doesn’t fall over if one device in the batch is unreachable. Same topic, completely different conversation — and a completely different quality of answer.</p>

<h2 id="memorizing-syntax-stopped-feeling-like-the-point">Memorizing syntax stopped feeling like the point</h2>

<p>Somewhere along the way I stopped caring about memorizing exact command syntax or API parameters. Those are searchable. AI generates them on request. The documentation exists and isn’t going anywhere.</p>

<p>What started mattering more was understanding why something works, when to reach for it, when specifically not to, and what trade-off is quietly hiding underneath a clean-looking solution. Honestly, it was the CCIE prep itself that pushed me toward that realization — it made the difference between “studying for recall” and “studying for intuition” pretty obvious once I noticed it.</p>

<h2 id="automation-was-never-really-about-the-script">Automation was never really about the script</h2>

<p>People hear “automation” and picture a folder of Python scripts. In practice, most of the actual work is risk reduction. It’s recognizing repetitive, error-prone manual work and figuring out how to remove the human from the loop without removing accountability. It’s thinking about what breaks, how you’d notice, and how you’d recover.</p>

<p>None of that disappears just because a model wrote the script for you. If anything, it matters more, because now there’s an extra layer between you and the code that you didn’t personally type.</p>

<h2 id="experience-is-still-the-tiebreaker">Experience is still the tiebreaker</h2>

<p>AI is fast, and it’s generous — it’ll happily hand you three or four reasonable-looking approaches to the same problem. Picking the right one is still the hard part, and that choice comes from having been burned by the wrong one before.</p>

<p>I’ve thrown out AI suggestions that looked fine on paper but didn’t match how our environment actually behaved. I’ve also taken suggestions that were genuinely better than what I would have written myself. Either way, making that call required actually understanding the technology well enough to judge it — not just accept or reject it on vibes.</p>

<h2 id="so-would-i-do-it-again">So would I do it again?</h2>

<p>If someone asked me today whether I’d still start the CCIE Automation track knowing what I know now about how fast AI moved, the answer is yes.</p>

<p>Not because I think I’ll be hand-writing every automation script for the rest of my career. But because the process changed how I think about problems — how to structure them, how different pieces of infrastructure connect, and honestly, how to have a more useful conversation, whether the other party is a colleague or a model.</p>

<p>And if none of that convinces you, there’s a simpler reason. Learning something genuinely difficult is just good exercise for the brain. Some people do crosswords. I apparently chose BGP, REST APIs, and Python exception handling. Questionable life choices aside, the brain workout was real.</p>

<p>AI didn’t make this journey pointless. It just changed what I actually got out of it. I don’t think of the certification as proof I can recall commands anymore — I think of it as proof I can reason through a complex system and work alongside AI instead of getting replaced by it.</p>

<p>That feels like a better outcome than the one I was originally chasing.</p>]]></content><author><name>Balamurugan Indhiran</name></author><category term="ccie-journey" /><category term="ccie" /><category term="automation" /><category term="ai" /><category term="career" /><category term="python" /><summary type="html"><![CDATA[Somewhere in the middle of studying for CCIE Automation, AI exploded and I started wondering if I was learning a skill that was about to become obsolete. Here's what I actually concluded.]]></summary></entry><entry><title type="html">Netmiko Deep Dive: SSH Automation for Network Engineers</title><link href="https://www.sdnspot.com/netmiko/python/netmiko-deep-dive/" rel="alternate" type="text/html" title="Netmiko Deep Dive: SSH Automation for Network Engineers" /><published>2025-02-01T09:00:00+00:00</published><updated>2025-02-01T09:00:00+00:00</updated><id>https://www.sdnspot.com/netmiko/python/netmiko-deep-dive</id><content type="html" xml:base="https://www.sdnspot.com/netmiko/python/netmiko-deep-dive/"><![CDATA[<p>Netmiko is one of the first tools a network automation engineer encounters. Built by Kirk Byers, it abstracts the messiness of SSH handling across dozens of vendor platforms into a clean, consistent API.</p>

<h2 id="installation">Installation</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>pip <span class="nb">install </span>netmiko
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="basic-connection">Basic Connection</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="n">netmiko</span> <span class="kn">import</span> <span class="n">ConnectHandler</span>

<span class="n">device</span> <span class="o">=</span> <span class="p">{</span>
    <span class="sh">"</span><span class="s">device_type</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">cisco_ios</span><span class="sh">"</span><span class="p">,</span>
    <span class="sh">"</span><span class="s">host</span><span class="sh">"</span><span class="p">:</span>        <span class="sh">"</span><span class="s">192.168.1.1</span><span class="sh">"</span><span class="p">,</span>
    <span class="sh">"</span><span class="s">username</span><span class="sh">"</span><span class="p">:</span>    <span class="sh">"</span><span class="s">admin</span><span class="sh">"</span><span class="p">,</span>
    <span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="p">:</span>    <span class="sh">"</span><span class="s">cisco123</span><span class="sh">"</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">with</span> <span class="nc">ConnectHandler</span><span class="p">(</span><span class="o">**</span><span class="n">device</span><span class="p">)</span> <span class="k">as</span> <span class="n">conn</span><span class="p">:</span>
    <span class="n">output</span> <span class="o">=</span> <span class="n">conn</span><span class="p">.</span><span class="nf">send_command</span><span class="p">(</span><span class="sh">"</span><span class="s">show ip interface brief</span><span class="sh">"</span><span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Always use the <code class="language-plaintext highlighter-rouge">with</code> statement — it ensures the connection is properly closed even if an exception occurs.</p>

<h2 id="supported-platforms">Supported Platforms</h2>

<table>
  <thead>
    <tr>
      <th>Platform</th>
      <th><code class="language-plaintext highlighter-rouge">device_type</code></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Cisco IOS / IOS-XE</td>
      <td><code class="language-plaintext highlighter-rouge">cisco_ios</code></td>
    </tr>
    <tr>
      <td>Cisco IOS-XR</td>
      <td><code class="language-plaintext highlighter-rouge">cisco_xr</code></td>
    </tr>
    <tr>
      <td>Cisco NX-OS</td>
      <td><code class="language-plaintext highlighter-rouge">cisco_nxos</code></td>
    </tr>
    <tr>
      <td>Arista EOS</td>
      <td><code class="language-plaintext highlighter-rouge">arista_eos</code></td>
    </tr>
    <tr>
      <td>Juniper JunOS</td>
      <td><code class="language-plaintext highlighter-rouge">juniper_junos</code></td>
    </tr>
  </tbody>
</table>

<h2 id="robust-error-handling">Robust Error Handling</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="n">netmiko</span> <span class="kn">import</span> <span class="n">ConnectHandler</span>
<span class="kn">from</span> <span class="n">netmiko.exceptions</span> <span class="kn">import</span> <span class="p">(</span>
    <span class="n">NetmikoTimeoutException</span><span class="p">,</span>
    <span class="n">NetmikoAuthenticationException</span><span class="p">,</span>
<span class="p">)</span>
<span class="kn">import</span> <span class="n">logging</span>

<span class="n">log</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nf">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">connect</span><span class="p">(</span><span class="n">params</span><span class="p">:</span> <span class="nb">dict</span><span class="p">):</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">conn</span> <span class="o">=</span> <span class="nc">ConnectHandler</span><span class="p">(</span><span class="o">**</span><span class="n">params</span><span class="p">)</span>
        <span class="n">log</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Connected to </span><span class="si">{</span><span class="n">params</span><span class="p">[</span><span class="sh">'</span><span class="s">host</span><span class="sh">'</span><span class="p">]</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">conn</span>
    <span class="k">except</span> <span class="n">NetmikoTimeoutException</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Timeout: </span><span class="si">{</span><span class="n">params</span><span class="p">[</span><span class="sh">'</span><span class="s">host</span><span class="sh">'</span><span class="p">]</span><span class="si">}</span><span class="s"> unreachable</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">except</span> <span class="n">NetmikoAuthenticationException</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Auth failed: </span><span class="si">{</span><span class="n">params</span><span class="p">[</span><span class="sh">'</span><span class="s">host</span><span class="sh">'</span><span class="p">]</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="n">log</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Unexpected: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="bp">None</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="multi-device-with-threadpoolexecutor">Multi-Device with ThreadPoolExecutor</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="n">concurrent.futures</span> <span class="kn">import</span> <span class="n">ThreadPoolExecutor</span><span class="p">,</span> <span class="n">as_completed</span>

<span class="n">INVENTORY</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span><span class="sh">"</span><span class="s">device_type</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">cisco_ios</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">host</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">10.0.0.1</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">username</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">admin</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">cisco</span><span class="sh">"</span><span class="p">},</span>
    <span class="p">{</span><span class="sh">"</span><span class="s">device_type</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">arista_eos</span><span class="sh">"</span><span class="p">,</span><span class="sh">"</span><span class="s">host</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">10.0.0.2</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">username</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">admin</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">password</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">arista</span><span class="sh">"</span><span class="p">},</span>
<span class="p">]</span>

<span class="k">def</span> <span class="nf">run_cmd</span><span class="p">(</span><span class="n">device</span><span class="p">,</span> <span class="n">cmd</span><span class="p">):</span>
    <span class="n">result</span> <span class="o">=</span> <span class="p">{</span><span class="sh">"</span><span class="s">host</span><span class="sh">"</span><span class="p">:</span> <span class="n">device</span><span class="p">[</span><span class="sh">"</span><span class="s">host</span><span class="sh">"</span><span class="p">],</span> <span class="sh">"</span><span class="s">output</span><span class="sh">"</span><span class="p">:</span> <span class="bp">None</span><span class="p">,</span> <span class="sh">"</span><span class="s">error</span><span class="sh">"</span><span class="p">:</span> <span class="bp">None</span><span class="p">}</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">with</span> <span class="nc">ConnectHandler</span><span class="p">(</span><span class="o">**</span><span class="n">device</span><span class="p">)</span> <span class="k">as</span> <span class="n">conn</span><span class="p">:</span>
            <span class="n">result</span><span class="p">[</span><span class="sh">"</span><span class="s">output</span><span class="sh">"</span><span class="p">]</span> <span class="o">=</span> <span class="n">conn</span><span class="p">.</span><span class="nf">send_command</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="n">result</span><span class="p">[</span><span class="sh">"</span><span class="s">error</span><span class="sh">"</span><span class="p">]</span> <span class="o">=</span> <span class="nf">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">result</span>

<span class="k">def</span> <span class="nf">run_all</span><span class="p">(</span><span class="n">inventory</span><span class="p">,</span> <span class="n">cmd</span><span class="p">,</span> <span class="n">workers</span><span class="o">=</span><span class="mi">10</span><span class="p">):</span>
    <span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">with</span> <span class="nc">ThreadPoolExecutor</span><span class="p">(</span><span class="n">max_workers</span><span class="o">=</span><span class="n">workers</span><span class="p">)</span> <span class="k">as</span> <span class="n">ex</span><span class="p">:</span>
        <span class="n">futures</span> <span class="o">=</span> <span class="p">{</span><span class="n">ex</span><span class="p">.</span><span class="nf">submit</span><span class="p">(</span><span class="n">run_cmd</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">cmd</span><span class="p">):</span> <span class="n">d</span><span class="p">[</span><span class="sh">"</span><span class="s">host</span><span class="sh">"</span><span class="p">]</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">inventory</span><span class="p">}</span>
        <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="nf">as_completed</span><span class="p">(</span><span class="n">futures</span><span class="p">):</span>
            <span class="n">results</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="nf">result</span><span class="p">())</span>
    <span class="k">return</span> <span class="n">results</span>

<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="nf">run_all</span><span class="p">(</span><span class="n">INVENTORY</span><span class="p">,</span> <span class="sh">"</span><span class="s">show version</span><span class="sh">"</span><span class="p">):</span>
    <span class="n">status</span> <span class="o">=</span> <span class="sh">"</span><span class="s">OK</span><span class="sh">"</span> <span class="k">if</span> <span class="n">r</span><span class="p">[</span><span class="sh">"</span><span class="s">output</span><span class="sh">"</span><span class="p">]</span> <span class="k">else</span> <span class="sa">f</span><span class="sh">"</span><span class="s">ERR: </span><span class="si">{</span><span class="n">r</span><span class="p">[</span><span class="sh">'</span><span class="s">error</span><span class="sh">'</span><span class="p">]</span><span class="si">}</span><span class="sh">"</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">r</span><span class="p">[</span><span class="sh">'</span><span class="s">host</span><span class="sh">'</span><span class="p">]</span><span class="si">:</span><span class="mi">15</span><span class="si">}</span><span class="s"> </span><span class="si">{</span><span class="n">status</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="textfsm-structured-output">TextFSM Structured Output</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="k">with</span> <span class="nc">ConnectHandler</span><span class="p">(</span><span class="o">**</span><span class="n">device</span><span class="p">)</span> <span class="k">as</span> <span class="n">conn</span><span class="p">:</span>
    <span class="n">interfaces</span> <span class="o">=</span> <span class="n">conn</span><span class="p">.</span><span class="nf">send_command</span><span class="p">(</span>
        <span class="sh">"</span><span class="s">show ip interface brief</span><span class="sh">"</span><span class="p">,</span>
        <span class="n">use_textfsm</span><span class="o">=</span><span class="bp">True</span>        <span class="c1"># returns list of dicts
</span>    <span class="p">)</span>

<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">interfaces</span><span class="p">:</span>
    <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">i</span><span class="p">[</span><span class="sh">'</span><span class="s">intf</span><span class="sh">'</span><span class="p">]</span><span class="si">:</span><span class="mi">25</span><span class="si">}</span><span class="s"> </span><span class="si">{</span><span class="n">i</span><span class="p">[</span><span class="sh">'</span><span class="s">ipaddr</span><span class="sh">'</span><span class="p">]</span><span class="si">:</span><span class="mi">18</span><span class="si">}</span><span class="s"> [</span><span class="si">{</span><span class="n">i</span><span class="p">[</span><span class="sh">'</span><span class="s">status</span><span class="sh">'</span><span class="p">].</span><span class="nf">upper</span><span class="p">()</span><span class="si">}</span><span class="s">]</span><span class="sh">"</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="key-takeaways">Key Takeaways</h2>

<ul>
  <li>Always use <code class="language-plaintext highlighter-rouge">with ConnectHandler(...)</code> for automatic cleanup</li>
  <li>Handle <code class="language-plaintext highlighter-rouge">NetmikoTimeoutException</code> and <code class="language-plaintext highlighter-rouge">NetmikoAuthenticationException</code> explicitly</li>
  <li>Use <code class="language-plaintext highlighter-rouge">ThreadPoolExecutor</code> for multi-device — never loop serially in production</li>
  <li><code class="language-plaintext highlighter-rouge">use_textfsm=True</code> gives you structured output for free</li>
</ul>

<p><strong>Next up:</strong> Nornir — a framework that adds proper inventory management and task runners on top of Netmiko’s connection model.</p>]]></content><author><name>Your Name</name></author><category term="netmiko" /><category term="python" /><category term="netmiko" /><category term="python" /><category term="ssh" /><category term="cisco" /><category term="automation" /><summary type="html"><![CDATA[Netmiko is the go-to Python library for SSH-based network device automation. This deep dive covers connection handling, error management, multi-vendor support, and building a reusable framework.]]></summary></entry></feed>