<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>RESTCONF on 0x2142 | Networking Nonsense</title>
    <link>https://0x2142.com/tags/restconf/</link>
    <description>Recent content in RESTCONF on 0x2142 | Networking Nonsense</description>
    <image>
      <title>0x2142 | Networking Nonsense</title>
      <url>https://0x2142.com/logo.jpg</url>
      <link>https://0x2142.com/logo.jpg</link>
    </image>
    <generator>Hugo -- 0.143.1</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 21 Feb 2023 19:52:12 +0000</lastBuildDate>
    <atom:link href="https://0x2142.com/tags/restconf/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Getting Started with Cisco YANG Suite</title>
      <link>https://0x2142.com/getting-started-with-cisco-yang-suite/</link>
      <pubDate>Tue, 21 Feb 2023 19:52:12 +0000</pubDate>
      <guid>https://0x2142.com/getting-started-with-cisco-yang-suite/</guid>
      <description>An introduction to setting up Cisco YANG Suite for exploring and testing network device automation via NETCONF &amp;amp; RESTCONF - including an example of automating wireless pre-shared key rotation.</description>
      <content:encoded><![CDATA[<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/nnd4KqeeqIw?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Back when I started diving into studying for the Cisco DevNet certifications, I thought a lot of the REST API stuff was fairly easy. But I always struggled a bit with wrapping my head around YANG models &amp; how exactly NETCONF/RESTCONF worked. A lot of it seemed more abstract, and browsing through YANG models isn&rsquo;t quite as intuitive as most REST API documentation.</p>
<p>These days I end up working quite a bit with NETCONF - and if there&rsquo;s one tool that&rsquo;s helped me more than anything, it&rsquo;s certainly YANG suite. So in this post, I wanted to spend a little bit of time walking through how to get set up with this tool - and walk through a quick example of how it can be used.</p>
<hr>
<h2 id="whats-yang-suite">What&rsquo;s YANG Suite?</h2>
<p>To put it simply: YANG Suite is a web-based tool that allows you to easily browse through YANG models &amp; find what you need. It also has a bunch of tools to run test queries against devices, which makes developing NETCONF automation easier. It can also auto-generate code snippets - and who doesn&rsquo;t like that? 😄</p>
<p>YANG suite is an open source tool maintained by Cisco &amp; the GitHub repo <a href="https://github.com/CiscoDevNet/yangsuite">here</a>.</p>
<h2 id="initial-setup">Initial Setup</h2>
<p>YANG suite is a containerized application, so we&rsquo;ll need a container host to run it on. You could use something like Docker Desktop to run it on your local PC, but I&rsquo;ll be using a dedicated Ubuntu machine in my lab with Docker installed.</p>
<blockquote>
<p>Note: YANG suite now supports a local Python pip-based install. Check out the <a href="https://github.com/CiscoDevNet/yangsuite">GitHub</a> repo for more info on that.</p></blockquote>
<h3 id="deploy-with-docker">Deploy with Docker</h3>
<p>First thing we&rsquo;ll do is pull down the code from GitHub:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">matt@yangsuite:~/yangsuite/docker$ git clone https://github.com/CiscoDevNet/yangsuite.git
</span></span></code></pre></div><p>Next, we can execute the setup script to help provision the YANG Suite containers, which is located at <code>yangsuite/docker/start_yang_suite.sh</code>. The script will prompt you to set up an admin user and generate self-signed certificates:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">matt@yangsuite:~/yangsuite/docker$ <span class="nb">cd</span> yangsuite/docker
</span></span><span class="line"><span class="cl">matt@yangsuite:~/yangsuite/docker$ ./start_yang_suite.sh
</span></span><span class="line"><span class="cl">Hello, please setup YANG Suite admin user.
</span></span><span class="line"><span class="cl">username: matt
</span></span><span class="line"><span class="cl">password:
</span></span><span class="line"><span class="cl">confirm password:
</span></span><span class="line"><span class="cl">email: matt@0x2142.local
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Setup <span class="nb">test</span> certificates? <span class="o">(</span>y/n<span class="o">)</span>: y
</span></span><span class="line"><span class="cl">  -- output omitted -- 
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">-----
</span></span><span class="line"><span class="cl">You are about to be asked to enter information that will be incorporated
</span></span><span class="line"><span class="cl">into your certificate request.
</span></span><span class="line"><span class="cl">What you are about to enter is what is called a Distinguished Name or a DN.
</span></span><span class="line"><span class="cl">There are quite a few fields but you can leave some blank
</span></span><span class="line"><span class="cl">For some fields there will be a default value,
</span></span><span class="line"><span class="cl">If you enter <span class="s1">&#39;.&#39;</span>, the field will be left blank.
</span></span><span class="line"><span class="cl">-----
</span></span><span class="line"><span class="cl">Country Name <span class="o">(</span><span class="m">2</span> letter code<span class="o">)</span> <span class="o">[</span>AU<span class="o">]</span>: US
</span></span><span class="line"><span class="cl">State or Province Name <span class="o">(</span>full name<span class="o">)</span> <span class="o">[</span>Some-State<span class="o">]</span>:
</span></span><span class="line"><span class="cl">Locality Name <span class="o">(</span>eg, city<span class="o">)</span> <span class="o">[]</span>:
</span></span><span class="line"><span class="cl">Organization Name <span class="o">(</span>eg, company<span class="o">)</span> <span class="o">[</span>Internet Widgits Pty Ltd<span class="o">]</span>:
</span></span><span class="line"><span class="cl">Organizational Unit Name <span class="o">(</span>eg, section<span class="o">)</span> <span class="o">[]</span>:
</span></span><span class="line"><span class="cl">Common Name <span class="o">(</span>e.g. server FQDN or YOUR name<span class="o">)</span> <span class="o">[]</span>:
</span></span><span class="line"><span class="cl">Email Address <span class="o">[]</span>:
</span></span><span class="line"><span class="cl">Certificates generated...
</span></span><span class="line"><span class="cl">Building docker containers...
</span></span></code></pre></div><p>Once all of the above info is provided, the script will build the YANG Suite contianer images &amp; launch them. However, by default it will launch in the foreground - so we can kill the script with <code>Ctrl-C</code> &amp; relaunch the containers with <code>docker-compose up -d</code>.</p>
<h3 id="allow-access-from-remote-ips">Allow Access from Remote IPs</h3>
<p>We may need to make a quick change depending on how we&rsquo;ll be accessing YANG Suite.</p>
<p>By default, YANG Suite will only permit access to the web UI from <code>localhost</code> - so if you&rsquo;re running Docker on your local PC, then no change is necessary.</p>
<p>However, if you&rsquo;re running Docker on a dedicated VM like I am, then we&rsquo;ll need to allow external IPs to access the YANG Suite UI. This is controlled by the <code>DJANGO_ALLOWED_HOSTS</code> environment variable that gets supplied to the container.</p>
<p>As long as we&rsquo;ve already run the setup script once - our environment variables will be saved to <code>yangsuite/docker/yangsuite/setup.env</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">DJANGO_SETTINGS_MODULE</span><span class="o">=</span>yangsuite.settings.production
</span></span><span class="line"><span class="cl"><span class="nv">MEDIA_ROOT</span><span class="o">=</span>/ys-data/
</span></span><span class="line"><span class="cl"><span class="nv">STATIC_ROOT</span><span class="o">=</span>/ys-static/
</span></span><span class="line"><span class="cl"><span class="nv">DJANGO_STATIC_ROOT</span><span class="o">=</span>/ys-static/
</span></span><span class="line"><span class="cl"><span class="nv">DJANGO_ALLOWED_HOSTS</span><span class="o">=</span>localhost
</span></span><span class="line"><span class="cl"><span class="nv">YS_ADMIN_USER</span><span class="o">=</span>&lt;removed&gt;
</span></span><span class="line"><span class="cl"><span class="nv">YS_ADMIN_PASS</span><span class="o">=</span>&lt;removed&gt;
</span></span><span class="line"><span class="cl"><span class="nv">YS_ADMIN_EMAIL</span><span class="o">=</span>&lt;removed&gt;
</span></span></code></pre></div><p>We&rsquo;ll need to change <code>DJANGO_ALLOWED_HOSTS=localhost</code> to the IP address <strong>of the Docker host</strong>. So if your Docker host is reachable at <code>192.168.1.1</code>, then we&rsquo;ll set: <code>DJANGO_ALLOWED_HOSTS=192.168.1.1</code>.</p>
<p>After which, we can bring up the containers again with <code>docker-compose up -d</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">matt@yangsuite:~/yangsuite/docker$ docker-compose up -d
</span></span><span class="line"><span class="cl">Recreating docker-yangsuite-1 ... <span class="k">done</span>
</span></span><span class="line"><span class="cl">Recreating docker-backup-1    ... <span class="k">done</span>
</span></span><span class="line"><span class="cl">Recreating docker-nginx-1     ... <span class="k">done</span>
</span></span></code></pre></div><p>Once everything is running, the web UI can be reached at <code>https://&lt;ip address&gt;:8443</code>.</p>
<h3 id="loading-yang-models">Loading YANG Models</h3>
<p>After we get logged in, we&rsquo;ll need to load the appropriate YANG models for whatever devices we&rsquo;re using.</p>
<p>For example, I&rsquo;ll be using Cisco IOS-XE devices running version 17.6.x code - so I&rsquo;ll need to download the YANG models from: <code>https://github.com/YangModels/yang/tree/main/vendor/cisco/xe/1761</code>.</p>
<p>Within YANG Suite, we&rsquo;ll navigate to <strong>Setup &gt; YANG files and repositories</strong>. Then click the button to add a <strong>New repository</strong>:</p>
<p><img alt="add-yang-repo-name-1" loading="lazy" src="/content/images/2022/12/add-yang-repo-name-1.png#center"></p>
<p>You can name the repo whatever you like. In my case, I named it <code>ios-xe-1761</code> which describes the models it will hold.</p>
<p>Next we can tell YANG Suite where we want to pull our models from. There are a couple of options, including a direct upload from your local PC or querying models directly from a device - but I&rsquo;ll choose <code>Git</code>.</p>
<p>After which, we&rsquo;ll need to fill in the following info:</p>
<p><img alt="add-yang-repo-from-git" loading="lazy" src="/content/images/2022/12/add-yang-repo-from-git.png#center"></p>
<p>The <strong>Repository URL</strong> will be the base Git repo that we&rsquo;re pulling from. In this case, that&rsquo;s <code>https://github.com/YangModels/yang.git</code>.</p>
<p>We&rsquo;ll also need to specify which <strong>Git branch</strong> to pull from, which for the <strong>YangModels</strong> repo will be <code>main</code>.</p>
<p>Then we&rsquo;ll specify which directory to import. The <strong>YangModels</strong> repository holds models for a large number of vendors &amp; devices - so we only want to pull in what is necessary. In my case, I&rsquo;ve set this to <code>vendor/cisco/xe/1761</code> - which will only pull in the YANG models for Cisco IOS-XE 17.6.x devices. I&rsquo;ve also checked the box for <strong>Include subdirectories</strong>.</p>
<p>Once that&rsquo;s all set, we can hit <strong>Import YANG files</strong>. Depending on how many models are being downloaded &amp; how quick your Docker host is, this may take a few minutes.</p>
<p>After it finishes, mine displayed a message showing that 827 models had been added:</p>
<p><img alt="add-yang-repo-complete" loading="lazy" src="/content/images/2022/12/add-yang-repo-complete.png#center"></p>
<h3 id="selecting-yang-module-sets">Selecting YANG Module Sets</h3>
<p>Once we have all of our models downloaded into YANG Suite, it&rsquo;s time to set up some module sets. You can think of these as filters for the YANG models.</p>
<p>For example, later on we&rsquo;ll show how to change a wireless PSK with NETCONF. So since I already know what type of information I&rsquo;ll be working with, I can create a module set to only contain those models.</p>
<p>YANG Suite will require at least one module set to be configured, even if you don&rsquo;t want to filter anything out.</p>
<p>To do this, we&rsquo;ll navigate to <strong>Setup &gt; YANG model sets</strong>. Then click <strong>New YANG set</strong>:</p>
<p><img alt="add-yang-module-set" loading="lazy" src="/content/images/2023/02/add-yang-module-set.png#center"></p>
<p>Again, since we&rsquo;ll be working with wireless data in a bit, I&rsquo;ll name my model set <code>wireless</code>. We&rsquo;ll also have to select which YANG repository this will pull from, which will be <code>ios-xe-1761</code>.</p>
<p>Next, we&rsquo;ll get to select which YANG models we want to include in our module set. To make things easy, I&rsquo;ll filter the models by <code>wireless</code> and then click <strong>Add selected</strong>.</p>
<p><img alt="add-wireless-models" loading="lazy" src="/content/images/2023/02/add-wireless-models.png#center"></p>
<p>Once those are added, you&rsquo;ll likely see an error about missing dependencies. Some of the models we selected may have sub-models or data types that are located in other models.</p>
<p>In order to quickly add these, YANG Suite provides a one-click-button to <strong>Locate and add missing dependencies</strong>:</p>
<p><img alt="add-dependencies" loading="lazy" src="/content/images/2023/02/add-dependencies.png#center"></p>
<p>That&rsquo;s it - our model set is ready to use.</p>
<h3 id="adding-devices">Adding Devices</h3>
<p>Okay, so now that we have all of our necessary YANG models set up, let&rsquo;s take a add a device that we can use for testing. I&rsquo;ll be adding a Catalyst 9800 Wireless controller.</p>
<p>To do this, we&rsquo;ll head over to <strong>Setup &gt; Device Profiles</strong>. Then click on <strong>Create new device</strong>.</p>
<p><img alt="add-device" loading="lazy" src="/content/images/2023/02/add-device.png#center"></p>
<p>Here we&rsquo;ll fill in a name for our device profile, along with the device&rsquo;s IP/FQDN and login credentials.</p>
<p>Scrolling down a bit, we&rsquo;ll also need to make sure we enable the appropriate management protocols. So I&rsquo;ll be checking the box for both NETCONF and RESTCONF, since I have both enabled. I&rsquo;ll also add SSH, which isn&rsquo;t shown in the screenshot below.</p>
<p><img alt="add-device-enable-netconf" loading="lazy" src="/content/images/2023/02/add-device-enable-netconf.png#center"></p>
<p>Please note, you&rsquo;ll likely want to check the box for <em>Skip SSH key validation for this device</em> under the NETCONF settings.</p>
<p>After that&rsquo;s all done, we can click <strong>Create Profile</strong> at the bottom.</p>
<p>Then, to validate everything works - we&rsquo;ll select our device &amp; click the button for <strong>Check selected device&rsquo;s reachability</strong>.</p>
<p>With any luck, you should see a similar result as shown below:</p>
<p><img alt="check-device-reachability" loading="lazy" src="/content/images/2023/02/check-device-reachability.png#center"></p>
<h2 id="example-changing-wireless-psk">Example: Changing Wireless PSK</h2>
<p>Alright, now let&rsquo;s take a look at how we might use YANG Suite to test NETCONF / RESTCONF calls to a wireless controller.</p>
<p>In this example, perhaps we&rsquo;re developing automation to rotate the guest wireless network password on a regular basis. We need to figure out how specifically to accomplish that by finding the right YANG models &amp; understanding what data is sent and received with those calls.</p>
<p>We&rsquo;ll explore how to do this with both NETCONF and RESTCONF.</p>
<h3 id="via-netconf">Via NETCONF</h3>
<p>To start off, we&rsquo;ll navigate to <strong>Protocols &gt; NETCONF</strong>. On this page, we&rsquo;ll have full access to explore models, build XML payloads, and test them against our configured device.</p>
<h4 id="get-config">Get-config</h4>
<p>First thing we&rsquo;ll need is to select our <strong>YANG set</strong> &amp; <strong>Modules</strong>. In my case, the <strong>YANG set</strong> will be <code>wireless</code>, and I&rsquo;ll select the module: <code>Cisco-IOS-XE-wireless-wlan-cfg</code>.</p>
<p>Then click the button to <strong>Load Modules</strong>:</p>
<p><img alt="netconf-select-models" loading="lazy" src="/content/images/2023/02/netconf-select-models.png#center"></p>
<p>By default, our <strong>NETCONF Operation</strong> will be set to <code>get-config</code>, which will work for now since we only want to retrieve configuration and not change anything yet.</p>
<p>Under the <strong>Nodes</strong> pane, we can expand the tree a bit &amp; dig in to find which call we need.</p>
<p>For our example, we&rsquo;ll need to examine the current configured WLAN profiles. So we&rsquo;ll check the box next to <code>wlan-cfg-entries</code>:</p>
<p><img alt="netconf-nodes" loading="lazy" src="/content/images/2023/02/netconf-nodes.png#center"></p>
<p>This will retrieve all configured WLAN profiles on the controller. For now, let&rsquo;s do that so we can figure out what data we&rsquo;ll get back.</p>
<p>We can now click the button to <strong>Build RPC</strong>, and YANG Suite will automatically build the appropriate XML payload for us:</p>
<p><img alt="netconf-build-rpc" loading="lazy" src="/content/images/2023/02/netconf-build-rpc.png#center"></p>
<p>You&rsquo;ll also notice that I selected the <strong>catalyst 9800</strong> as the target device. Let&rsquo;s go ahead and run this with the <strong>Run RPC(s)</strong> button, which should pop open a new window &amp; establish a connection to our device.</p>
<p><img alt="netconf-rpc-response-1" loading="lazy" src="/content/images/2023/02/netconf-rpc-response-1.png#center"></p>
<p>We can see in the response above, that we have two WLAN profiles configured: <code>test-lab-network</code> and <code>test-guest-network</code>. For the purposes of this demo, I have opted to have the guest network PSK stored in plain text, so we can validate our changes more easily.</p>
<p>Okay, so what if we wanted to refine our query XML to only retrieve data on the <code>test-guest-network</code>? Maybe we also want to filter our the additional information returned, and only see the current PSK?</p>
<p>Back on the payload editor, we can expand <code>wlan-cfg-entries</code> and input our desired <code>profile-name</code>:</p>
<p><img alt="netconf-build-rpc-filtered-1" loading="lazy" src="/content/images/2023/02/netconf-build-rpc-filtered-1.png#center"></p>
<p>We&rsquo;ll also find the <code>psk</code> item further down in the list. Now we want to pull back this field, regardless of what the PSK is currently set to. So we&rsquo;ll click the value field, but leave it blank:</p>
<p><img alt="netconf-build-rpc-filtered-psk" loading="lazy" src="/content/images/2023/02/netconf-build-rpc-filtered-psk.png#center"></p>
<p>Once we click <strong>Build RPC</strong>, we can see our payload now shows the additional XML filters asking only for a single profile &amp; it&rsquo;s associated PSK:</p>
<p><img alt="netconf-rpc-payload-filtered" loading="lazy" src="/content/images/2023/02/netconf-rpc-payload-filtered.png#center"></p>
<p>If we run that RPC call, we&rsquo;ll now see that we only get back a limited set of data:</p>
<p><img alt="netconf-filtered-rpc-response" loading="lazy" src="/content/images/2023/02/netconf-filtered-rpc-response.png#center"></p>
<p>Now we know exactly what payload to send if we want to get back the current configured PSK for any specific WLAN profile.</p>
<p>Let&rsquo;s try changing it!</p>
<h4 id="edit-config">Edit-config</h4>
<p>Lucky for us, we&rsquo;re already nearly set up for that. We&rsquo;ll need to make two quick changes. First, set the <strong>NETCONF Operation</strong> to <code>edit-config</code>:</p>
<p><img alt="netconf-op-edit-config" loading="lazy" src="/content/images/2023/02/netconf-op-edit-config.png#center"></p>
<p>Then, under the YANG tree, we&rsquo;ll set a new PSK:</p>
<p><img alt="netconf-new-psk" loading="lazy" src="/content/images/2023/02/netconf-new-psk.png#center"></p>
<p>Again, we&rsquo;ll click <strong>Build RPC</strong> - and take a quick look at the proposed change:</p>
<p><img alt="netconf-new-psk-payload" loading="lazy" src="/content/images/2023/02/netconf-new-psk-payload.png#center"></p>
<p>Look good to you? Okay, let&rsquo;s send it:</p>
<p><img alt="netconf-new-psk-response" loading="lazy" src="/content/images/2023/02/netconf-new-psk-response.png#center"></p>
<p>Sure enough, the new configuration was sent to the wireless controller and we got a response of: <strong>ok</strong>.</p>
<p>But of course, we can check quickly by switching our operation back to <code>get-config</code> and re-running our previous XML payload:</p>
<p><img alt="netconf-new-psk-validation" loading="lazy" src="/content/images/2023/02/netconf-new-psk-validation.png#center"></p>
<h4 id="auto-generated-snippets">Auto-generated snippets</h4>
<p>Well if you&rsquo;re new to NETCONF, you might be asking yourself &ldquo;Okay great, but how do I get this into Python?!?!&rdquo;</p>
<p>YANG Suite has some built in tools to auto-generate Python code or Ansible playbooks, which can help us get started on our code more quickly.</p>
<p>To generate these, click the <strong>Replays</strong> button:</p>
<p><img alt="netconf-python-ansible" loading="lazy" src="/content/images/2023/02/netconf-python-ansible.png#center"></p>
<p>For example, I selected to auto-generate a Python script. Here&rsquo;s what that looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="ch">#! /usr/bin/env python</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">traceback</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">lxml.etree</span> <span class="k">as</span> <span class="nn">et</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">argparse</span> <span class="kn">import</span> <span class="n">ArgumentParser</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">ncclient</span> <span class="kn">import</span> <span class="n">manager</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">ncclient.operations</span> <span class="kn">import</span> <span class="n">RPCError</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">payload</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"><span class="s1">&#39;&#39;&#39;
</span></span></span><span class="line"><span class="cl"><span class="s1">&lt;get-config xmlns=&#34;urn:ietf:params:xml:ns:netconf:base:1.0&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">    &lt;source&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">      &lt;running/&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">    &lt;/source&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">    &lt;filter&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">      &lt;wlan-cfg-data xmlns=&#34;http://cisco.com/ns/yang/Cisco-IOS-XE-wireless-wlan-cfg&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">        &lt;wlan-cfg-entries&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">          &lt;wlan-cfg-entry&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">            &lt;profile-name&gt;test-guest-network&lt;/profile-name&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">            &lt;psk/&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">          &lt;/wlan-cfg-entry&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">        &lt;/wlan-cfg-entries&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">      &lt;/wlan-cfg-data&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">    &lt;/filter&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">  &lt;/get-config&gt;
</span></span></span><span class="line"><span class="cl"><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1">&#39;&#39;&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">&#39;__main__&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">parser</span> <span class="o">=</span> <span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s1">&#39;Usage:&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># script arguments</span>
</span></span><span class="line"><span class="cl">    <span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;-a&#39;</span><span class="p">,</span> <span class="s1">&#39;--host&#39;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">str</span><span class="p">,</span> <span class="n">required</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">help</span><span class="o">=</span><span class="s2">&#34;Device IP address or Hostname&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;-u&#39;</span><span class="p">,</span> <span class="s1">&#39;--username&#39;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">str</span><span class="p">,</span> <span class="n">required</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">help</span><span class="o">=</span><span class="s2">&#34;Device Username (netconf agent username)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;-p&#39;</span><span class="p">,</span> <span class="s1">&#39;--password&#39;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">str</span><span class="p">,</span> <span class="n">required</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">help</span><span class="o">=</span><span class="s2">&#34;Device Password (netconf agent password)&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">parser</span><span class="o">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s1">&#39;--port&#39;</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">830</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                        <span class="n">help</span><span class="o">=</span><span class="s2">&#34;Netconf agent port&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="o">.</span><span class="n">parse_args</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># connect to netconf agent</span>
</span></span><span class="line"><span class="cl">    <span class="k">with</span> <span class="n">manager</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="n">args</span><span class="o">.</span><span class="n">host</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                         <span class="n">port</span><span class="o">=</span><span class="n">args</span><span class="o">.</span><span class="n">port</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                         <span class="n">username</span><span class="o">=</span><span class="n">args</span><span class="o">.</span><span class="n">username</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                         <span class="n">password</span><span class="o">=</span><span class="n">args</span><span class="o">.</span><span class="n">password</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                         <span class="n">timeout</span><span class="o">=</span><span class="mi">90</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                         <span class="n">hostkey_verify</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                         <span class="n">device_params</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;name&#39;</span><span class="p">:</span> <span class="s1">&#39;csr&#39;</span><span class="p">})</span> <span class="k">as</span> <span class="n">m</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1"># execute netconf operation</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">rpc</span> <span class="ow">in</span> <span class="n">payload</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">response</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">dispatch</span><span class="p">(</span><span class="n">et</span><span class="o">.</span><span class="n">fromstring</span><span class="p">(</span><span class="n">rpc</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">xml</span>
</span></span><span class="line"><span class="cl">            <span class="k">except</span> <span class="n">RPCError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">xml</span>
</span></span><span class="line"><span class="cl">                <span class="k">pass</span>
</span></span><span class="line"><span class="cl">            <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">traceback</span><span class="o">.</span><span class="n">print_exc</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1"># beautify output</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">et</span><span class="o">.</span><span class="n">iselement</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                <span class="n">data</span> <span class="o">=</span> <span class="n">et</span><span class="o">.</span><span class="n">tostring</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">pretty_print</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">out</span> <span class="o">=</span> <span class="n">et</span><span class="o">.</span><span class="n">tostring</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">                    <span class="n">et</span><span class="o">.</span><span class="n">fromstring</span><span class="p">(</span><span class="n">data</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">                    <span class="n">pretty_print</span><span class="o">=</span><span class="kc">True</span>
</span></span><span class="line"><span class="cl">                <span class="p">)</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">            <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">traceback</span><span class="o">.</span><span class="n">print_exc</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">                <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
</span></span></code></pre></div><h3 id="via-restconf">Via RESTCONF</h3>
<p>What if we prefer working with REST &amp; JSON instead of XML? This time we&rsquo;ll navigate to <strong>Protocols &gt; RESTCONF</strong>.</p>
<p>Here, we&rsquo;ll select our YANG set &amp; modules again. In case you skipped the NETCONF section, we&rsquo;re working with the <code>wireless</code> YANG set &amp; the <code>Cisco-IOS-XE-wireless-wlan-cfg</code> module.</p>
<p>We&rsquo;ll also select a device to use, then click <strong>Load Module(s)</strong>:</p>
<p><img alt="restconf-nodes" loading="lazy" src="/content/images/2023/02/restconf-nodes.png#center"></p>
<p>Again, we&rsquo;ll see a similar structure to the NETCONF side. But this time, we&rsquo;ll utilize it quite a bit differently.</p>
<p>In the screenshot above, I&rsquo;ve already selected the <code>wlan-cfg-entries</code> entry. Next, we&rsquo;ll click the button to <strong>Generate API(s)</strong>:</p>
<p><img alt="restconf-wlan-all" loading="lazy" src="/content/images/2023/02/restconf-wlan-all.png#center"></p>
<p>We&rsquo;ll see auto-generated API documentation for all of the available calls. This list will contain every variation possible, so it can be quite long. Since we already went through the NETCONF method, we know what data we need to find / use which should make this easier.</p>
<p>To start with, I&rsquo;ll expand the <strong>GET</strong> for <code>/data/Cisco-IOS-XE-wireless-wlan-cfg:wlan-cfg-data/wlan-cfg-entries</code>. We&rsquo;ll have the ability to test the RESTCONF calls directly from this interface as well by clicking on <strong>Try it out</strong>:</p>
<p><img alt="restconf-example-query-wlan" loading="lazy" src="/content/images/2023/02/restconf-example-query-wlan.png#center"></p>
<p>In the screenshot above, I&rsquo;ve already submitted the test request &amp; gotten a response from the controller. While the screenshot is cut off a bit, we can see both of our wireless profile names.</p>
<p>If we wanted to see only the PSK, like we did with our previous example - we could scroll down quite a bit to find the <strong>GET</strong> request for <code>/data/Cisco-IOS-XE-wireless-wlan-cfg:wlan-cfg-data/wlan-cfg-entries/wlan-cfg-entry={wlan-cfg-entry-profile-name}/psk</code></p>
<p>In the screenshot below, I&rsquo;ve already input our <code>test-guest-network</code> profile name &amp; executed the API call:</p>
<p><img alt="restconf-example-query-psk" loading="lazy" src="/content/images/2023/02/restconf-example-query-psk.png#center"></p>
<p>Sure enough, we see the PSK that we had just configured via NETCONF a few minutes ago.</p>
<p>Now let&rsquo;s change it. We&rsquo;ll expand the <strong>PATCH</strong> API call for the same endpoint, and fill in the request body with our new PSK:</p>
<p><img alt="restconf-patch-psk" loading="lazy" src="/content/images/2023/02/restconf-patch-psk.png#center"></p>
<p>Executing that call, the wireless controller returned a status code of <strong>204</strong>.</p>
<p>So let&rsquo;s check again to see if that updated properly:</p>
<p><img alt="restconf-updated-psk" loading="lazy" src="/content/images/2023/02/restconf-updated-psk.png#center"></p>
<p>Now that we have all the appropriate REST calls, it should be much easier to start developing our automation.</p>
<hr>
<p>Okay, I think that&rsquo;s about all I wanted to cover in this post. I&rsquo;ve seen a lot of people struggle with NETCONF much like I used to, and I think the real key to getting familiar is just having the right tools &amp; spending enough time with it. YANG Suite has really helped me get more familiar with the various model structures and how to use them.</p>
<p>Hopefully this information helps others to get started. NETCONF &amp; YANG are certainly big hills to climb, but the right tools can make all the difference.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
