Jekyll2019-10-15T22:01:27+00:00https://www.networktocode.com/blog/feed.xmlThe NTC MagNetwork to Codeinfo@networktocode.comAutomation Principles - Idempotent2019-10-15T00:00:00+00:002019-10-15T00:00:00+00:00https://www.networktocode.com/blog/post/Principle-Series-Idempotent<p>This is the first in a <a href="../Network-Automation-Principles/">series of posts</a> to understand Network Automation Principles.</p> <p>The term <code class="highlighter-rouge">idempotent</code> 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.</p> <p>The most common “real world” example you will will read about is calling an elevator, as described in this wikipedia quote.</p> <blockquote> <p>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.</p> </blockquote> <h1 id="idempotent-in-computer-science">Idempotent in Computer Science</h1> <p>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.</p> <p>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, you would have to modify the script 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.</p> <blockquote> <p>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.</p> </blockquote> <h1 id="example">Example</h1> <p>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.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="n">neighbors</span> <span class="o">=</span> <span class="p">[</span><span class="s">"nyc-rt01"</span><span class="p">]</span> <span class="o">&gt;&gt;&gt;</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">def</span> <span class="nf">update_list</span><span class="p">(</span><span class="n">neighbors</span><span class="p">,</span> <span class="n">neighbor</span><span class="p">):</span> <span class="o">...</span> <span class="n">neighbors</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">neighbor</span><span class="p">)</span> <span class="o">...</span> <span class="k">return</span> <span class="n">neighbors</span> <span class="o">...</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">neighbors</span> <span class="o">=</span> <span class="n">update_list</span><span class="p">(</span><span class="n">neighbors</span><span class="p">,</span> <span class="s">"nyc-rt02"</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">neighbors</span> <span class="o">=</span> <span class="n">update_list</span><span class="p">(</span><span class="n">neighbors</span><span class="p">,</span> <span class="s">"nyc-rt02"</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">print</span><span class="p">(</span><span class="n">neighbors</span><span class="p">)</span> <span class="p">[</span><span class="s">'nyc-rt01'</span><span class="p">,</span> <span class="s">'nyc-rt02'</span><span class="p">,</span> <span class="s">'nyc-rt02'</span><span class="p">]</span> <span class="o">&gt;&gt;&gt;</span> </code></pre></div></div> <blockquote> <p>Note: The result includes orange multiple times.</p> </blockquote> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> <span class="n">neighbors</span> <span class="o">=</span> <span class="p">[</span><span class="s">"nyc-rt01"</span><span class="p">]</span> <span class="o">&gt;&gt;&gt;</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">def</span> <span class="nf">update_list_idempotently</span><span class="p">(</span><span class="n">neighbors</span><span class="p">,</span> <span class="n">neighbor</span><span class="p">):</span> <span class="o">...</span> <span class="k">if</span> <span class="n">neighbor</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">neighbors</span><span class="p">:</span> <span class="o">...</span> <span class="n">neighbors</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">neighbor</span><span class="p">)</span> <span class="o">...</span> <span class="k">return</span> <span class="n">neighbors</span> <span class="o">...</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">neighbors</span> <span class="o">=</span> <span class="n">update_list_idempotently</span><span class="p">(</span><span class="n">neighbors</span><span class="p">,</span> <span class="s">"nyc-rt02"</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="n">neighbors</span> <span class="o">=</span> <span class="n">update_list_idempotently</span><span class="p">(</span><span class="n">neighbors</span><span class="p">,</span> <span class="s">"nyc-rt02"</span><span class="p">)</span> <span class="o">&gt;&gt;&gt;</span> <span class="k">print</span><span class="p">(</span><span class="n">neighbors</span><span class="p">)</span> <span class="p">[</span><span class="s">'nyc-rt01'</span><span class="p">,</span> <span class="s">'nyc-rt02'</span><span class="p">]</span> </code></pre></div></div> <blockquote> <p>Note: The result includes orange one time, even though attempting to “add orange” multiple times.</p> </blockquote> <h1 id="impotency-in-rest">Impotency in REST</h1> <p>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.</p> <p><img src="../../../static/images/blog_posts/principles/http_idempotent.png" alt="HTTP Idempotent Chart" /></p> <h1 id="real-life-use-case">Real Life Use Case</h1> <p>The Ansible <code class="highlighter-rouge">*_config</code> 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, <em>exactly</em> 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.</p> <p>Leveraging the Ansible ad-hoc command you can see the results of running this command three times.</p> <ol> <li>The first time, the config “int Gig1/0/24” interface description is applied, and the result is changed.</li> <li>The second time, the module is still attempting to build a configuration on “Gig1/0/24” and pushes the config again, since there was not an exact match.</li> <li>The third time the configuration is applied to “interface GigabitEthernet1/0/24”, a match is found in the configuration, and no change is required, and thus the module is idempotent.</li> </ol> <p><img src="../../../static/images/blog_posts/principles/ios_config_idempotent.png" alt="IOS Config Idempotent" /></p> <h1 id="conclusion">Conclusion</h1> <p>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.</p> <p>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.</p> <p>-Ken</p>Ken CelenzaThis is the first in a series of posts to understand Network Automation Principles.Automation Principles2019-10-14T00:00:00+00:002019-10-14T00:00:00+00:00https://www.networktocode.com/blog/post/Network-Automation-Principles<p>The principles and definitions of automation/programming is something that I am personally fairly passionate about. This is largely because I have spent so much time doing things the hard way without even knowing about it. Lacking peer review, and left to my own devices I was only ever building a solution to solve the current problem, never building a structure that could be built upon. Learning these concepts fundamentally changed the way I think and program.</p> <p>I have covered these topics at <a href="https://packetpushers.net/podcast/weekly-show-402-building-a-network-automation-framework/">Packet Pushers</a>, <a href="https://www.youtube.com/watch?v=S0UpV2P7wXI">Interop</a>, and anyone who is willing to listen or unfortunately stuck listening to me pontificate verbally. The intention of this post is to announce our intention to create the content released semi-regularly, and aggregate the links to the respective blogs as they are built out.</p> <h1 id="principles">Principles</h1> <p>You may note that these are not all strict principles…. well, you would be right, but they follow the spirit of what is intended here, which is provide the reader with context to avoid making mistakes.</p> <ul> <li>Atomicity</li> <li>Declarative/Imperative</li> <li>Data Normalization</li> <li>Data Modeling</li> <li>DRY (Do not Repeat Yourself)</li> <li>Inheritance</li> <li>Premature Optimization</li> <li><a href="../Principle-Series-Idempotent/">Idempotent</a></li> <li>Mutable vs Immutable</li> <li>Principle of least astonishment</li> <li>Robustness Principle</li> <li>The Mythical Man-Month</li> <li>YAGNI (You Aren’t Gonna Need It)</li> </ul> <p>-Ken</p>Ken CelenzaThe principles and definitions of automation/programming is something that I am personally fairly passionate about. This is largely because I have spent so much time doing things the hard way without even knowing about it. Lacking peer review, and left to my own devices I was only ever building a solution to solve the current problem, never building a structure that could be built upon. Learning these concepts fundamentally changed the way I think and program.NFD21 - NetBox Automation Integrations2019-10-10T00:00:00+00:002019-10-10T00:00:00+00:00https://www.networktocode.com/blog/post/netbox-automation-integrations<p>Wrapping up our presentations at <a href="https://techfieldday.com/appearance/network-to-code-presents-at-networking-field-day-21/">Networking Field Day 21</a> last week, I was fortunate to spend some time discussing NetBox and its role as a robust source of truth within a network automation framework. Most people who use NetBox are familiar with its web interface and to some extent its REST API, but there are a variety of ways NetBox enables the import and export of data to support highly automated workflows. If you haven’t had a chance to check out NetBox yet, you can find the project <a href="https://github.com/netbox-community/netbox">on GitHub</a> and the documentation <a href="https://netbox.readthedocs.io">here</a>.</p> <h3 id="csv-importexport">CSV Import/Export</h3> <p>NetBox supports CSV-formatted data import for most objects. This is very convenient for migrating data from spreadsheets to NetBox. All that’s needed is to ensure the required columns are present and named appropriately, and that the data is in a valid format. NetBox also provides a means to export data in CSV format for use by other applications.</p> <p><img src="../../../static/images/blog_posts/nfd21/20191010_csv_import_form.png" alt="CSV import form" /></p> <h3 id="export-templates">Export Templates</h3> <p>While the built-in CSV export function is handy, there is often a need to provide more control over the format of exported data. This can be done using export templates, which leverage the Jinja2 templating language to render arbitrary output. For instance, you might write an export template to generate a Nagios configuration file from a set of NetBox devices. Within the NetBox UI, you can then navigate to the devices list and select your custom template under the “export” button. The generated document will be provided for direct download.</p> <p><img src="../../../static/images/blog_posts/nfd21/20191010_export_template_form.png" alt="Export template form" /></p> <h3 id="webhooks">Webhooks</h3> <p>Sometimes it’s necessary to automatically update related systems whenever data in NetBox changes. For example, maybe you need to update a network monitoring system every time a new device is added in NetBox. This can be accomplished by creating a webhook under NetBox’s admin UI. Simply select the type(s) of object that you’re interested in, the actions on which to trigger (creation, modification, and/or deletion), and the attributes of the remote system to be notified. When the relevant changes occur, NetBox will automatically generate an HTTP POST request to the remote system with all the details of the change and the object’s new state.</p> <p><img src="../../../static/images/blog_posts/nfd21/20191010_webhooks_form.png" alt="Webhooks form" /></p> <p>To see a demonstration of webhooks in action, check out <a href="https://youtu.be/u0mYDbxH5-s?t=1368">Jason Edelman’s presentation</a>.</p> <h3 id="rest-api">REST API</h3> <p>The most robust mechanism for exchanging data with NetBox is its REST API. The API allows for nearly the same degree of functionality as the web UI (with a few caveats) and supports the creation, retrieval, modification, and deletion of objects. All objects are represented in the API using JSON serialization. NetBox provides a human-friendly browsable version of the API locally, as well as live documentation using OpenAPI (Swagger). Client libraries such as <a href="https://github.com/digitalocean/pynetbox">pynetbox</a> are available to simplify the consumption of NetBox’s API by external systems.</p> <p><img src="../../../static/images/blog_posts/nfd21/20191010_rest_api.png" alt="Browsable REST API" /></p> <h3 id="custom-scripts">Custom Scripts</h3> <p>Custom script are a new feature in NetBox, introduced in v2.6.3, which allows an administrator to extend NetBox with arbitrary Python code. These scripts are rendered as forms in the web UI, prompting the user for data which the script processes to perform some action. Scripts can create new objects in NetBox, manipulate existing objects, import data from or send data to remote systems – anything, really. This feature is especially powerful as it allows for the extension of NetBox’s core functionality without introducing the need to maintain a custom fork of the project.</p> <p><img src="../../../static/images/blog_posts/nfd21/20191010_custom_script_form.png" alt="Custom script form" /></p> <h3 id="netbox-and-network-to-code">NetBox and Network to Code</h3> <p>While NetBox offers plenty of avenues for integration with other systems, we understand that actually <em>writing</em> the code to engage these features is often easier said than done. Fortunately, Network to Code is proud to offer commercial support for NetBox as well as assistance with custom development. Leveraging our expertise with NetBox and related network automation tools, we are uniquely positioned to deliver extremely high value solutions with quick turnaround. Let us know what we can do for you!</p> <p>The full series of videos from our NFD21 presentation is posted on <a href="https://techfieldday.com/appearance/network-to-code-presents-at-networking-field-day-21/">Tech Field Day</a>.</p>Jeremy StretchWrapping up our presentations at Networking Field Day 21 last week, I was fortunate to spend some time discussing NetBox and its role as a robust source of truth within a network automation framework. Most people who use NetBox are familiar with its web interface and to some extent its REST API, but there are a variety of ways NetBox enables the import and export of data to support highly automated workflows. If you haven’t had a chance to check out NetBox yet, you can find the project on GitHub and the documentation here.NFD21 - Network Automation Architecture2019-10-08T00:00:00+00:002019-10-08T00:00:00+00:00https://www.networktocode.com/blog/post/nfd21-network-automation-architecture<p>This past week I had the honor and privilege of traveling out to Santa Clara, CA with some of my esteemed colleagues to participate in Networking Field Day 21 on behalf of Network to Code. My contribution to our joint presentation was an overview of the various components that go into building a successful network automation platform. While this was only one section of our overall presentation, the delegates proved to be very engaged with these concepts. At Network to Code, we try not to focus on individual technologies, and instead focus on transformational ideas and workflows which bring value to our clients. To that end, this section dealt with the higher level concepts and components that go into building a successful network automation platform. I want to call out a couple of sections and points here, but be sure to checkout the <a href="https://www.youtube.com/watch?v=VlDCYmItkzE">full video</a> from NFD21 that goes into even more detail and covers other architectural components such as Configuration Management, Orchestration, and Verification &amp; Testing.</p> <h2 id="human--business-interaction">Human &amp; Business Interaction</h2> <p>The tools and technologies that fall into this section deal with directly exposing interfaces to the automation that we build for the network. These are things like our IT Service Management (ITSM) ticketing applications, but also chat and communication platforms. ChatOps is a term used a lot in the industry and is continuing to pick up steam in network automation. Integrations with chat platforms allow a business to push network data and workflows into channels where conversations are already taking place. Perhaps more importantly, these avenues allow our network automation to be exposed to business stakeholders outside of the networking teams directly.</p> <h2 id="data-management">Data Management</h2> <p>If I were to pick a single section in my talk to call out as the most important, it would be this one. In terms of network automation, the industry is not talking about data enough. As with any other vertical in the tech space, data underpins everything we do, and network automation is no different. As the network automation community has grown, so has understanding in the concept of using a Source of Truth (SoT), which is an authoritative system of record for a particular data domain. That last part is key, because we can actually (and realistically do) have multiple sources of truth that do not overlap. For example, our IPAM and DCIM can be different systems because they control different domains of data. This is valid as long as we do not have more than one IPAM or DCIM tool, as this is where the phrase “Single Source of Truth” comes from, not that there is only one total system.</p> <p>Still though, having many different systems creates problems of its own. At first pass, each system in our network automation toolbox would potentially need to reference many different systems to get the data needed to perform automation. More importantly, this tightly couples the client to the data source and format. To combat this, we strive to implement an aggregation layer between the sources of truth and the systems consuming their data. This aggregation serves a couple of important use cases.</p> <p>First, it creates a single pane of glass for accessing all of the data from our various authoritative systems, thus allowing our tooling to reference a single place. Second, the aggregator implements a data translation layer which transforms the data coming from each source of true into an abstracted data model. This data model intentionally strips away any features of the data or its structure which make it identifiable with any vendor or source implementation.</p> <p>In doing so, we segway into the third point, which is that the aggregator interacts with the various source of true systems in a pluggable way. By implementing an adapter, the aggregator understands the data and format coming from an individual source of true and how to transform the data to conform to the abstracted data model. This allows the aggregator to easily implement support for different source of true platforms, such that they can be swapped out at anytime. If you want to switch IPAM vendors, all you have to do is create (or engage with NTC) an adaptor for the aggregator that understands what the data looks like coming out of the new IPAM.</p> <h2 id="monitoring-alerting-and-visibility">Monitoring, Alerting, and Visibility</h2> <p>It may seem a bit odd to be talking about monitoring and alerting in the context of network automation, but there is more to what we do than just configuration management. In fact, the core of this topic is centered around the concept of “closed loop automation,” or manufacturing a feedback loop into the automation platform that we build. In the video, you will hear me talk about the automation platform as a stack, and on one side we travel down the stack to the actual network infrastructure, but on the other side, events come out of the network and travel back up the stack. Indeed those events come in the traditional forms of SNMP polling, syslog messages, etc. They can also come in new forms such as time series metrics, and streaming telemetry. We have also revisited the storage and processing layer to implement more modern time series databases which allow for tagging data points with metadata labels which opens the door to more intelligent querying and visualization. Speaking of visualization, we want to empower business stakeholders with network data, and we want to do it in a self service fashion through modern dashboarding tools. Again, this is a case of bringing the network data and workflows to the business.</p> <p>Back to the storage engine, we need to get those network events out of their monitoring silos, and fed back into the automation platform. We do this with the aid of rules processing engines which assert business logic based on the metadata which is attached to the collected data points. Once the event streams have been plumbed back into our network automation platform, our feedback loop begins to take shape. We can now build automated remediation workflows which allow engineers to focus on actual engineering and architecture, and less on troubleshooting and remediating known, repeated events. In situations where human involvement is needed, the platform can at least collect important context from the network as a form of first level triage, obviating the need for manual data collection and reducing the overall time necessary to respond to network events.</p> <h2 id="operational-models">Operational Models</h2> <p>The final topic I want to bring up is the idea that no one network automation platform will ever suit the needs of all networks and organizations. The more important takeaways from this talk are the various components that go into architecting the platform that best suits your needs. It is true that company A may be able to purchase an off the shelf automation product from a vendor and be perfectly happy, while company B may require an entirely custom solution over a longer evolution timeline. In all cases, Network to Code is here to provide training, enablement, and implementation services.</p> <p>-John Anderson (@lampwins)</p>John AndersonThis past week I had the honor and privilege of traveling out to Santa Clara, CA with some of my esteemed colleagues to participate in Networking Field Day 21 on behalf of Network to Code. My contribution to our joint presentation was an overview of the various components that go into building a successful network automation platform. While this was only one section of our overall presentation, the delegates proved to be very engaged with these concepts. At Network to Code, we try not to focus on individual technologies, and instead focus on transformational ideas and workflows which bring value to our clients. To that end, this section dealt with the higher level concepts and components that go into building a successful network automation platform. I want to call out a couple of sections and points here, but be sure to checkout the full video from NFD21 that goes into even more detail and covers other architectural components such as Configuration Management, Orchestration, and Verification &amp; Testing.Who is Network to Code @ Networking Field Day 212019-10-07T00:00:00+00:002019-10-07T00:00:00+00:00https://www.networktocode.com/blog/post/who-is-network-to-code-networking-field-day-21<p>It was a milestone week for Network to Code. We had the opportunity to present last Thursday at <a href="https://techfieldday.com/event/nfd21/">Networking Field Day 21</a> in San Jose, California. This turned out to be the biggest Field Day event ever which spanned 4 days (not the usual 3 day event). Network automation played a big part of the event, and that was great to see. As you’re probably aware, network automation is our focus at Network to Code. It’s what drives us, motivates us, and is what we are all passionate about–enabling us to have a collective team that is second to no other in the industry.</p> <p>During our two-hour time slot that began bright and early at 8am local time, our team (John Anderson, Damien Garros, Jeremy Stretch, and myself) presented five topics. This blog is the first in a series that’ll summarize each of those topics.</p> <p>I kicked things off talking about who we are and what’re all about. I’ve written about this in the past, but things are moving quick, so here is another update.</p> <p>As we expect to grow to over 40 employees by the end of the year, we continue to work with many of the world’s largest organizations across numerous verticals. This includes companies from the Fortune 10 to 2000.</p> <p>We believe that workflows drive automation and there is generally too much focus on tools. We believe there must be focus on creating well-defined network automation architectures.</p> <p>In order to deliver value at a faster rate and ensure our clients have confidence in what we do, we employ a 3-pronged approach in order to execute for our clients: this includes (1) delivering and automating high-value “quick wins” or the “low hanging fruit” (2) building an Enterprise platform architecture and subsequently the platform itself and (3) continuously enabling our clients with formal and informal training, strategy, and peer &amp; code review. This all happens after we’ve successfully discovered and assessed the current state of the environment.</p> <p>Want to hear more?</p> <p><a href="https://www.youtube.com/watch?v=IlqDITtOMKw">Watch the Who is Network to Code video from Networking Field Day</a></p> <p>Happy Automating!</p> <p>-Jason (@jedelman8)</p>Jason EdelmanIt was a milestone week for Network to Code. We had the opportunity to present last Thursday at Networking Field Day 21 in San Jose, California. This turned out to be the biggest Field Day event ever which spanned 4 days (not the usual 3 day event). Network automation played a big part of the event, and that was great to see. As you’re probably aware, network automation is our focus at Network to Code. It’s what drives us, motivates us, and is what we are all passionate about–enabling us to have a collective team that is second to no other in the industry.Network to Code at AnsibleFest 20192019-10-03T00:00:00+00:002019-10-03T00:00:00+00:00https://www.networktocode.com/blog/post/network-to-code-at-ansiblefest<p><img src="../../../static/images/blog_posts/NTC_at_ansiblefest2.jpg" alt="Team Picture 1" /></p> <p><img src="../../../static/images/blog_posts/NTC_at_ansiblefest1.jpg" alt="Team Picture 2" /></p> <p>Members of the Network to Code team recently made their way down to sunny Atlanta for our third year as AnsibleFest sponsors. As regular users of the platform, we always look forward to hearing what’s next in the world Ansible, and this year’s Fest did not disappoint!</p> <p>A few key takeaways from our team:</p> <h1 id="more-and-more-network-automation">MORE AND MORE NETWORK AUTOMATION</h1> <p>As early adopters, you’ll always find us evangelizing about the importance of embracing network automation. This year, however, we were pleasantly surprised to notice that we weren’t the only ones. Conversations surrounding network automation took serious hold this year. Approximately half of the attendees we talked to were interested in network automation – a big leap from years past. We were also excited (excuse us for tooting our own horn for a moment) to hear that the Network to Code community was a big part of these conversations. As we’ve said before, community is a key component of everything we do here at Network to Code and were proud to foster a community that is leading the charge when it comes to network automation.</p> <h1 id="culture-is-key">CULTURE IS KEY</h1> <p>The NTC team was glad to see that culture was getting some well-deserved attention this year. You can have everything in place you need to automate a network – the right tools, the right talent, the right infrastructure – but, if you don’t have cultural buy-in, your network automation project is going to be an up-hill battle. Thinking not just about how organizations deploy tools and solutions, but also about how they work to change hearts and minds surrounding new technology is something we here at Network to Code give a lot of thought to. We’re glad to see the conversation moving forward!</p> <h1 id="whats-new-in-ansible-29">WHAT’S NEW IN ANSIBLE 2.9</h1> <p>With Ansible 2.9 just around the corner, there was no shortage of conversation surrounding upcoming changes to Ansible. There was a lot of discussion around the new collection system and how the main Ansible project will be reorganized in multiple repositories moving forward. It’s a healthy change for Ansible, and will help create cleaner delineation between the core engine, the core modules and the community modules. Each one will now be able to evolve at its own pace with its own release cycle. The collection introduced in Ansible 2.9 also offers a new packaging system for Ansible role, plugin and module, making it possible to split the main Ansible repository in multiple repositories.</p> <p>The team at Red Hat is putting a lot of effort into ensuring that the transition will be as transparent as possible for users. While dates and milestones have yet to be defined, it looks like things should be moving forward in the next six months or so. The Ansible core team published a very informative <a href="https://www.ansible.com/blog/thoughts-on-restructuring-the-ansible-project">blog</a> explaining the motivation behind this refactoring that we recommend you check out.</p> <p>The second big topic of conversation at the Network Contributor Summit had to do with the new Resource Modules and the Resource Module Builder. In Ansible 2.9, Ansible has developed 51 new modules called “Resource Modules” that will, overtime, replace some of the existing network modules. For example <code class="highlighter-rouge">ios_interface</code> will be replaced by <code class="highlighter-rouge">ios_interfaces</code>.</p> <p>These new modules will take structured data for one or more objects in a structured format. This format will be the same as what you get back from the facts module, allowing you to push and pull configs to/from a device. Last but not least, these modules will support a declarative approach and will be able to override all objects that are not defined.</p> <p>We look forward to seeing how these updates come to life in the coming months. In the meantime, happy automating!</p> <p>-Damien</p>Damien GarrosUsing VSCode Remote Containers &amp; Python Development2019-10-01T00:00:00+00:002019-10-01T00:00:00+00:00https://www.networktocode.com/blog/post/vscode-remote-containers<p>I’ve been using using VSCode for development for a little while and was excited when Microsoft announced they were developing extensions to attach to running containers and allow you to still develop natively within Windows using VSCode.</p> <p>Let’s go ahead and get started on setting up VSCode with the Remote - Containers and Python extensions.</p> <ol> <li>Launch VSCode and click on the <strong>extensions</strong> icon</li> <li>Search for Remote - Containers and click “Install”</li> <li>Search for Python and click “Install”</li> </ol> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/installextension.png" alt="Extension Install" /></p> <p>One of my use cases is that I have different Python Virtual Environments created in my docker image and I’d like to be able to use those to test code with different packages so i’ll show you how you can add custom paths for VSCode to search for virtual environments in.</p> <ol> <li>Click File &gt; Preferences &gt; Settings within VSCode</li> <li>Search python.venvpath</li> <li>Add the path in the container that your virtual environments are located in to the <code class="highlighter-rouge">Python: Venv Path</code> setting. Mine are located in <strong>/src/.venvs</strong></li> </ol> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/venvpathsetting.png" alt="Venv Path Setting" /></p> <p>Now that is complete, you should be able to attach to a running container.</p> <ol> <li>Start your docker container</li> <li>Click on the <strong>&gt;&lt;</strong> icon in the bottom left of VSCode</li> </ol> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/remoteicon.png" alt="Remote Icon" /></p> <ol> <li>Select <strong>Remote-Containers: Attach to Running Container</strong></li> </ol> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/attachcontainer.png" alt="Attach Container" /></p> <ol> <li>Select the container you want to attach to</li> </ol> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/remotecontainer.png" alt="Remote container" /></p> <p>A new window should have popped up and you should see that you’re attached to the container now.</p> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/vscoderemotecontainer.png" alt="VSCode Remote Container" /></p> <p>Now that we’re attached to the container within VSCode, we’ll need to install and enable the Python extension within the container.</p> <ol> <li>Click on the <strong>extensions</strong> icon and find Python</li> <li>Click <strong>Install in Attached Container</strong></li> <li>Click <strong>Reload Required</strong></li> </ol> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/remotepython.png" alt="Remote Python" /></p> <p>Once it has been reloaded, you can open a directory.</p> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/openfolder.png" alt="Open Folder" /></p> <p>Now if I open a Python file, it will prompt me to select the Python interpeter I want to use. In this case, you’ll see the built-in Python’s that were found within <strong>PYTHONPATH</strong> and my virtual environments in <strong>/src/.venvs/</strong></p> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/pythoninterpreter.png" alt="Python Interpreter" /></p> <p>NOTE: If pylint or your selected linter isn’t installed, it will prompt to be installed.</p> <p>As you can see, we can execute from the built-in terminal in VSCode and it is using my virtual environment.</p> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/vscodeterminal1.png" alt="VSCode Terminal1" /></p> <p>Now that I have tested the code in one virtual environment, let me test it within another. We’ll select the <strong>yang</strong> virtual environment. Click on the Python portion in the bottom left hand corner of VSCode to select a new Python interpreter</p> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/selectinterpreter.png" alt="Select Interpreter" /></p> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/newinterpreter.png" alt="New Interpreter" /></p> <p>Now let’s check the terminal within VSCode to validate it is using the new virtual environment</p> <p><img src="../../../static/images/blog_posts/vscode-remote-containers/vscodeterminal2.png" alt="VSCode Terminal2" /></p> <p>Now we can take advantage of developing natively within Windows, the benefits of using the different virtual environments to test code within, and the VSCode Python extension that helps with linting and formatting (if configured).</p> <p>-Mikhail</p>Mikhail YohmanI’ve been using using VSCode for development for a little while and was excited when Microsoft announced they were developing extensions to attach to running containers and allow you to still develop natively within Windows using VSCode.How to Create an SSH Tunnel via Command line2019-09-24T00:00:00+00:002019-09-24T00:00:00+00:00https://www.networktocode.com/blog/post/how-to-create-an-ssh-tunnel-via-command-line<p>Do you have a network device or server that can only be reached behind a jumphost? This is not an uncommon scenario, as security best practice often requires such. This can cause some challanges. However, as long as you have access to the jumphost, you may be able to use an ssh tunnel to mimic being directly connected to a network with access to the otherwise inaccessible hosts.</p> <h1 id="what-is-an-ssh-tunnel">What is an SSH Tunnel?</h1> <p>An ssh tunnel aka ssh port forwarding, allows an encrypted tunnel to be established over an untrusted network between an SSH Client and SSH server. You specify a local port for SSH to listen on, such as <code class="highlighter-rouge">4001</code>, and all connections destined for port <code class="highlighter-rouge">4001</code> will be tunneled via SSH to a specified remote port, such as <code class="highlighter-rouge">22</code>.</p> <h1 id="access-a-network-deviceserver-that-is-only-accessible-via-a-jumphost">Access a network device/server that is only accessible via a jumphost</h1> <p><code class="highlighter-rouge">ssh username@172.18.50.100 -Nf -p 20622 -L 127.0.0.1:4001:192.168.20.10:22</code></p> <p>Let’s break this down:</p> <ul> <li><code class="highlighter-rouge">ssh</code> is the command we are using for our ssh tunnel.</li> <li><code class="highlighter-rouge">username</code> is the username to log into the jump host.</li> <li><code class="highlighter-rouge">172.18.50.100</code> is the SSH server that we will be connecting to. This is the jump server</li> <li><code class="highlighter-rouge">-p 20622</code> is optional. It tells ssh to establish the tunnel on the remote port (destination port) <code class="highlighter-rouge">20622</code>. Typically, this will be <code class="highlighter-rouge">22</code>, however there could be some security controls in place that do not allow SSH on the common port. This option is not always necessary.</li> <li><code class="highlighter-rouge">-L 127.0.0.1:4001:192.168.20.10:22</code> tells ssh to listen locally on port <code class="highlighter-rouge">4001</code>. Any connections to <code class="highlighter-rouge">127.0.0.1</code> on port <code class="highlighter-rouge">4001</code> will be forwarded to <code class="highlighter-rouge">192.168.20.10</code> on port <code class="highlighter-rouge">22</code> through the SSH tunnel. You could also use <code class="highlighter-rouge">localhost</code> in place of <code class="highlighter-rouge">127.0.0.1</code>, assuming you haven’t modified that entry in your <code class="highlighter-rouge">/etc/hosts</code> file.</li> <li><code class="highlighter-rouge">-N</code> is optional, tells ssh to not execute a remote command. This is useful for just forwarding ports.</li> <li><code class="highlighter-rouge">-f</code> is optional, requests ssh to go to background just before command execution. This is useful if ssh is going to ask for passwords or passphrases, but the user wants it in the background.</li> </ul> <h1 id="observe-the-connections">Observe the connections</h1> <p>If you are curious about what your system is doing from a network perspective, open a separate terminal and run the following command before you create the SSH tunnel:</p> <p><code class="highlighter-rouge">watch 'netstat -abn | grep 4001'</code></p> <p>This will run the command every 2 seconds and print the output to the screen. Observe the output:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Every 2.0s: netstat <span class="nt">-abn</span> | <span class="nb">grep </span>4001 My-Cool-Macbook..local: Mon Sep 16 16:18:48 2019 tcp4 0 0 127.0.0.1.4001 <span class="k">*</span>.<span class="k">*</span> LISTEN 0 0 </code></pre></div></div> <h1 id="connect-to-host-using-local-port">Connect to host using local port</h1> <p>Now, connect to <code class="highlighter-rouge">192.168.20.10</code> using the following command:</p> <p><code class="highlighter-rouge">username@127.0.0.1 -p 4001</code></p> <p>Notice the <code class="highlighter-rouge">netstat</code> output:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Every 2.0s: netstat <span class="nt">-abn</span> | <span class="nb">grep </span>4001 My-Cool-Macbook.local: Mon Sep 16 16:08:03 2019 tcp4 0 0 127.0.0.1.4001 127.0.0.1.64356 ESTABLISHED 3877 0 tcp4 0 0 127.0.0.1.64356 127.0.0.1.4001 ESTABLISHED 3683 0 tcp4 0 0 127.0.0.1.4001 <span class="k">*</span>.<span class="k">*</span> LISTEN 0 0 </code></pre></div></div> <p>The SSH client is forarding traffic over local port <code class="highlighter-rouge">4001</code> to randomly selected open local port <code class="highlighter-rouge">64356</code> which is then sent over the SSH tunnel, which eventually lands at <code class="highlighter-rouge">192.168.20.10</code> port <code class="highlighter-rouge">22</code>. This allows for multiple connections to be forwarded through port <code class="highlighter-rouge">4001</code>. So if you were to make another connection, you will see an additional <code class="highlighter-rouge">netstat</code> entry with another local port generated for the second connection.</p> <h1 id="terminal-the-ssh-tunnel">Terminal the SSH Tunnel</h1> <p>To terminate the ssh tunnel, run <code class="highlighter-rouge">ps aux | grep ssh</code>, search for the correct tunnel and PID, and then run <code class="highlighter-rouge">kill 12345</code> replacing <code class="highlighter-rouge">12345</code> with the PID on your machine.</p> <h1 id="socks-proxy">SOCKS Proxy</h1> <p>Another cool feature enabled on many systems is SOCKS. This allows you to proxy application traffic and send it to a jump host. This is useful for browsing to a website that is normally not directly accessible. You can use this technique to access internal websites that remain only accessible behind a jump host. Multiple SOCKS proxies can be created, meaning multiple endpoints can be configured to proxy your local machine’s traffic.</p> <p>Let’s say we wanted to use the same jump host in the example above and send our web traffic to the jumphost to access a website hosted behind it on an internal network. In this example, we will set up a local SOCKS proxy and SSH tunnel. The SOCKS proxy will send traffic via the SSH tunnel to the jump host. We will be using Firefox, however many other browsers such as Google Chrome support SOCKS5.</p> <p>At the command-line, run the following: <code class="highlighter-rouge">ssh -D 4000 -C -N -q -f username@192.168.20.10 -p 64356</code></p> <p>Let’s break this down:</p> <ul> <li><code class="highlighter-rouge">-D 4000</code> is used for dynamic application-level port forwarding. This will open a SOCKS proxy on port <code class="highlighter-rouge">4000</code>. Ensure that this port is not already being used on your local machine.</li> <li><code class="highlighter-rouge">C</code> optional, is used to compress data in the tunnel to conserve bandwidth.</li> <li><code class="highlighter-rouge">q</code> optional, this is for quiet mode.</li> <li><code class="highlighter-rouge">N</code> optional, directs ssh to not execute remote commands.</li> <li><code class="highlighter-rouge">username</code> is the username for the jump host.</li> <li><code class="highlighter-rouge">192.168.20.10</code> is the jump host IP address.</li> <li><code class="highlighter-rouge">-p 20622</code> is optional, it tells ssh to establish the tunnel on the remote port <code class="highlighter-rouge">20622</code>. Typically, this will be <code class="highlighter-rouge">22</code>, however there could be some security controls in place that do not allow SSH on the common port. This option is not always necessary.</li> <li><code class="highlighter-rouge">f</code> is optional, requests ssh to go to background just before command execution. This is useful if ssh is going to ask for passwords or passphrases, but the user wants it in the background.</li> </ul> <h3 id="configure-your-browser-to-use-socks">Configure your browser to use SOCKS</h3> <p>Now head over to your browser:</p> <ol> <li>Click the hamburger menu button in the top right-hand corner</li> <li>Click <code class="highlighter-rouge">Preferences</code>.</li> <li>Click on the <code class="highlighter-rouge">General</code> tab if not already there and scroll all the way down to <code class="highlighter-rouge">Network Settings</code> &gt; <code class="highlighter-rouge">Settings...</code></li> <li>Select the <code class="highlighter-rouge">Manual Proxy configuration</code> radio button and enter <code class="highlighter-rouge">127.0.0.1</code> with port <code class="highlighter-rouge">4000</code>, and ensure SOCKS v5 is selected.</li> </ol> <p><img src="/static/images/ssh-tunnel/firefox-socks-proxy-configuration.png" alt="Firefox SOCKS Proxy Configuration Screenshot" /> <em>Firefox SOCKS Proxy Configuration Screenshot</em></p> <p>Once you click <code class="highlighter-rouge">OK</code> you should now be proxying your web connections and should be able to access internal websites. To kill the SSH tunnel, thus the SOCKS proxy connection, search for the PID and kill the process.</p> <p>This is not an exaustive list of cool things you can do with SSH tunnels, but are some of the most common methods. We hope this article was informative to you, Happy coding!</p> <p>-Kenny B</p>Kenneth BuchananDo you have a network device or server that can only be reached behind a jumphost? This is not an uncommon scenario, as security best practice often requires such. This can cause some challanges. However, as long as you have access to the jumphost, you may be able to use an ssh tunnel to mimic being directly connected to a network with access to the otherwise inaccessible hosts.NSO Tips and Tricks - NSO Actions2019-09-16T00:00:00+00:002019-09-16T00:00:00+00:00https://www.networktocode.com/blog/post/NSO-tips-and-tricks<p>I recently brought up the usefulness of custom NSO Actions in a <a href="https://twitter.com/JasonBelk3/status/1171592388172603393">Twitter discussion with several Cisco friends</a>, and promised to provide a brief demo to illustrate how to use them. This is not the first time I have had this type of discussion. People who have been exposed to NSO’s functionality often gravitate towards and focus on the service mechanics, and rightly so. They are incredibly powerful and a core feature for the product. However, the flexible utility of using NSO actions is an underrated feature of NSO, which I want to educate others about.</p> <h1 id="nso-actions">NSO Actions</h1> <p>What is an NSO Action?</p> <p>It is effectively an RPC call used by NSO to execute some arbitrary set of code associated with that Yang NSO Action. For example, the common action of <code class="highlighter-rouge">sync-from</code> is a built in NSO action which does not store any data on its own, but when triggered, causes NSO to execute code behind the scenes to log into a network device and sync the data from the device into NSO’s CDB.</p> <p>The NSO Action is a combination of a Yang model, defining the constraints / data model of the inputs expected or outputs expected for the action, and then some code linked to it when the action is triggered. When a NSO action is created, just like any custom package in NSO, the Yang propagates the action to be available across all of the NSO APIs (CLI, GUI, REST/RESTCONF, Python, etc). This means with a few lines of Yang and a few lines of Python, you very quickly have an API interface exposed to any user who has access to NSO.</p> <h2 id="custom-nso-actions">Custom NSO Actions</h2> <p>This blog is not meant to be a deep dive on the nitty gritty details of custom NSO actions, but rather a quick primer on what they are and how to use them. Also, for the sake of simplicity, I will focus on Python NSO actions, rather than Java, etc.</p> <p>A NSO custom action is defined in a package’s yang file with the following syntax:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nx">tailf</span><span class="p">:</span><span class="nx">action</span> <span class="kr">double</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">actionpoint</span> <span class="nx">ACTION</span><span class="o">-</span><span class="nx">NAME</span><span class="o">-</span><span class="nx">action</span><span class="p">;</span> <span class="nx">input</span> <span class="p">{</span> <span class="nx">leaf</span> <span class="nx">some</span><span class="o">-</span><span class="nx">input</span><span class="o">-</span><span class="nx">name</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">string</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">output</span> <span class="p">{</span> <span class="nx">leaf</span> <span class="nx">result</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">string</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>and just for comparison, looking at the NSO source Yang files(<code class="highlighter-rouge">tailf-ncs-devices.yang</code>), you can see for example, the NSO <code class="highlighter-rouge">sync-from</code> action Yang which is actually used by the NSO:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">tailf</span><span class="p">:</span><span class="nx">action</span> <span class="nx">sync</span><span class="o">-</span><span class="k">from</span> <span class="p">{</span> <span class="nx">description</span> <span class="s2">"Synchronize the configuration by pulling from the device."</span><span class="p">;</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">info</span> <span class="s2">"Synchronize the config by pulling from the device"</span><span class="p">;</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">actionpoint</span> <span class="nx">ncsinternal</span> <span class="p">{</span> <span class="na">tailf</span><span class="p">:</span><span class="nx">internal</span><span class="p">;</span> <span class="p">}</span> <span class="nx">input</span> <span class="p">{</span> <span class="nx">container</span> <span class="nx">dry</span><span class="o">-</span><span class="nx">run</span> <span class="p">{</span> <span class="nx">presence</span> <span class="s2">""</span><span class="p">;</span> <span class="nx">leaf</span> <span class="nx">outformat</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">outformat2</span><span class="p">;</span> <span class="nx">description</span> <span class="s2">"Report what would be done towards CDB, without actually doing anything."</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">uses</span> <span class="nx">wait</span><span class="o">-</span><span class="k">for</span><span class="o">-</span><span class="nx">lock</span><span class="p">;</span> <span class="p">}</span> <span class="nx">output</span> <span class="p">{</span> <span class="nx">uses</span> <span class="nx">sync</span><span class="o">-</span><span class="nx">result</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>You may notice additional data points in there, but it follows the same general pattern:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">tailf</span><span class="p">:</span><span class="nx">action</span> <span class="nx">NAME</span><span class="o">-</span><span class="nx">OF</span><span class="o">-</span><span class="nx">ACTION</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">actionpoint</span> <span class="nx">NAME</span><span class="o">-</span><span class="nx">WHERE</span><span class="o">-</span><span class="nx">CODE</span><span class="o">-</span><span class="nx">LINKS</span><span class="o">-</span><span class="nx">TO</span><span class="o">-</span><span class="nx">IT</span> <span class="nx">input</span> <span class="p">{</span> <span class="nx">YANG</span><span class="o">-</span><span class="nx">INPUT</span><span class="o">-</span><span class="nx">NODES</span> <span class="p">}</span> <span class="nx">output</span> <span class="p">{</span> <span class="nx">YANG</span><span class="o">-</span><span class="nx">OUTPUT</span><span class="o">-</span><span class="nx">NODES</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Basically what is going on there–and this will be more evident when you see a live example with everything working–is the Yang model tells the NSO application, “Hey! I have a custom set of code I want to execute, and here is the name I want the action to be called in the NSO application, and here is the Yang model constraints on the input and output”.</p> <h1 id="nso-action-example">NSO Action Example</h1> <p>The easiest way to get started with an NSO action is to use the bash command flag in <code class="highlighter-rouge">ncs-make-package</code> to give you a dummy example: <code class="highlighter-rouge">ncs-make-package --service-skeleton python --action-example ntc-action-example</code></p> <p>This command should be run in your <code class="highlighter-rouge">nso-run/packages</code> folder, or wherever you keep your other packages and NEDs. It will create the following folders and files:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>packages<span class="nv">$ </span>tree ntc-action-example/ ntc-action-example/ ├── README ├── package-meta-data.xml ├── python │   └── ntc-action-example │   ├── __init__.py │   └── main.py ├── src │   ├── Makefile │   └── yang │   └── ntc-action-example.yang ├── templates └── <span class="nb">test</span> ├── Makefile └── internal ├── Makefile └── lux ├── Makefile ├── action │   ├── Makefile │   └── run.lux └── service ├── Makefile ├── dummy-device.xml ├── dummy-service.xml ├── pyvm.xml └── run.lux 10 directories, 16 files </code></pre></div></div> <p>The most relevant ones for this example will be the Yang file: <code class="highlighter-rouge">ntc-action-example/src/yang/ntc-action-example.yang</code> and the Python file: <code class="highlighter-rouge">ntc-action-example/python/ntc_action_example/main.py</code>.</p> <h2 id="trimming-down-the-yang-file">Trimming Down the Yang file</h2> <p>First, by default this is what the Yang file will look like:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">module</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span> <span class="p">{</span> <span class="nx">namespace</span> <span class="s2">"http://example.com/ntc-action-example"</span><span class="p">;</span> <span class="nx">prefix</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span><span class="p">;</span> <span class="k">import</span> <span class="nx">ietf</span><span class="o">-</span><span class="nx">inet</span><span class="o">-</span><span class="nx">types</span> <span class="p">{</span> <span class="nx">prefix</span> <span class="nx">inet</span><span class="p">;</span> <span class="p">}</span> <span class="k">import</span> <span class="nx">tailf</span><span class="o">-</span><span class="nx">common</span> <span class="p">{</span> <span class="nx">prefix</span> <span class="nx">tailf</span><span class="p">;</span> <span class="p">}</span> <span class="k">import</span> <span class="nx">tailf</span><span class="o">-</span><span class="nx">ncs</span> <span class="p">{</span> <span class="nx">prefix</span> <span class="nx">ncs</span><span class="p">;</span> <span class="p">}</span> <span class="nx">description</span> <span class="s2">"Bla bla..."</span><span class="p">;</span> <span class="nx">revision</span> <span class="mi">2016</span><span class="o">-</span><span class="mi">01</span><span class="o">-</span><span class="mi">01</span> <span class="p">{</span> <span class="nx">description</span> <span class="s2">"Initial revision."</span><span class="p">;</span> <span class="p">}</span> <span class="nx">container</span> <span class="nx">action</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">action</span> <span class="kr">double</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">actionpoint</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span><span class="o">-</span><span class="nx">action</span><span class="p">;</span> <span class="nx">input</span> <span class="p">{</span> <span class="nx">leaf</span> <span class="nx">number</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">uint8</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">output</span> <span class="p">{</span> <span class="nx">leaf</span> <span class="nx">result</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">uint16</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">list</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span> <span class="p">{</span> <span class="nx">description</span> <span class="s2">"This is an RFS skeleton service"</span><span class="p">;</span> <span class="nx">key</span> <span class="nx">name</span><span class="p">;</span> <span class="nx">leaf</span> <span class="nx">name</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">info</span> <span class="s2">"Unique service id"</span><span class="p">;</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">cli</span><span class="o">-</span><span class="nx">allow</span><span class="o">-</span><span class="nx">range</span><span class="p">;</span> <span class="nx">type</span> <span class="nx">string</span><span class="p">;</span> <span class="p">}</span> <span class="nx">uses</span> <span class="nx">ncs</span><span class="p">:</span><span class="nx">service</span><span class="o">-</span><span class="nx">data</span><span class="p">;</span> <span class="nl">ncs</span><span class="p">:</span><span class="nx">servicepoint</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span><span class="o">-</span><span class="nx">servicepoint</span><span class="p">;</span> <span class="c1">// may replace this with other ways of refering to the devices.</span> <span class="nx">leaf</span><span class="o">-</span><span class="nx">list</span> <span class="nx">device</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">leafref</span> <span class="p">{</span> <span class="nx">path</span> <span class="s2">"/ncs:devices/ncs:device/ncs:name"</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">// replace with your own stuff here</span> <span class="nx">leaf</span> <span class="nx">dummy</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">inet</span><span class="p">:</span><span class="nx">ipv4</span><span class="o">-</span><span class="nx">address</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <p>Since I used the service skeleton bash flag, we see the dummy <code class="highlighter-rouge">list ntc-action-example</code> and since I did an action example it added the <code class="highlighter-rouge">container action</code> part in the Yang file. In this example, I am not going to use the service mechanics, so I will remove it and other unused part of the Yang file just to keep it simple, so the new yang file is:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">module</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span> <span class="p">{</span> <span class="nx">namespace</span> <span class="s2">"http://networktocode.com/ntc-action-example"</span><span class="p">;</span> <span class="nx">prefix</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span><span class="p">;</span> <span class="k">import</span> <span class="nx">tailf</span><span class="o">-</span><span class="nx">common</span> <span class="p">{</span> <span class="nx">prefix</span> <span class="nx">tailf</span><span class="p">;</span> <span class="p">}</span> <span class="k">import</span> <span class="nx">tailf</span><span class="o">-</span><span class="nx">ncs</span> <span class="p">{</span> <span class="nx">prefix</span> <span class="nx">ncs</span><span class="p">;</span> <span class="p">}</span> <span class="nx">description</span> <span class="s2">"An action example"</span><span class="p">;</span> <span class="nx">revision</span> <span class="mi">2019</span><span class="o">-</span><span class="mi">09</span><span class="o">-</span><span class="mi">12</span> <span class="p">{</span> <span class="nx">description</span> <span class="s2">"Giving some details for an action example."</span><span class="p">;</span> <span class="p">}</span> <span class="nx">container</span> <span class="nx">action</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">action</span> <span class="kr">double</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">actionpoint</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span><span class="o">-</span><span class="nx">action</span><span class="p">;</span> <span class="nx">input</span> <span class="p">{</span> <span class="nx">leaf</span> <span class="nx">number</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">uint8</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">output</span> <span class="p">{</span> <span class="nx">leaf</span> <span class="nx">result</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">uint16</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <h2 id="trimming-down-the-python-file">Trimming down the Python File</h2> <p>By default NSO creates the following Python file for all the action and service tie ins:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># -*- mode: python; python-indent: 4 -*- </span><span class="kn">import</span> <span class="nn">ncs</span> <span class="kn">from</span> <span class="nn">ncs.application</span> <span class="kn">import</span> <span class="n">Service</span> <span class="kn">from</span> <span class="nn">ncs.dp</span> <span class="kn">import</span> <span class="n">Action</span> <span class="c1"># --------------- # ACTIONS EXAMPLE # --------------- </span><span class="k">class</span> <span class="nc">DoubleAction</span><span class="p">(</span><span class="n">Action</span><span class="p">):</span> <span class="o">@</span><span class="n">Action</span><span class="o">.</span><span class="n">action</span> <span class="k">def</span> <span class="nf">cb_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uinfo</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">kp</span><span class="p">,</span> <span class="nb">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">trans</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'action name: '</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'action input.number: '</span><span class="p">,</span> <span class="nb">input</span><span class="o">.</span><span class="n">number</span><span class="p">)</span> <span class="c1"># Updating the output data structure will result in a response </span> <span class="c1"># being returned to the caller. </span> <span class="n">output</span><span class="o">.</span><span class="n">result</span> <span class="o">=</span> <span class="nb">input</span><span class="o">.</span><span class="n">number</span> <span class="o">*</span> <span class="mi">2</span> <span class="c1"># ------------------------ # SERVICE CALLBACK EXAMPLE # ------------------------ </span><span class="k">class</span> <span class="nc">ServiceCallbacks</span><span class="p">(</span><span class="n">Service</span><span class="p">):</span> <span class="c1"># The create() callback is invoked inside NCS FASTMAP and </span> <span class="c1"># must always exist. </span> <span class="o">@</span><span class="n">Service</span><span class="o">.</span><span class="n">create</span> <span class="k">def</span> <span class="nf">cb_create</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tctx</span><span class="p">,</span> <span class="n">root</span><span class="p">,</span> <span class="n">service</span><span class="p">,</span> <span class="n">proplist</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'Service create(service='</span><span class="p">,</span> <span class="n">service</span><span class="o">.</span><span class="n">_path</span><span class="p">,</span> <span class="s">')'</span><span class="p">)</span> <span class="c1"># The pre_modification() and post_modification() callbacks are optional, </span> <span class="c1"># and are invoked outside FASTMAP. pre_modification() is invoked before </span> <span class="c1"># create, update, or delete of the service, as indicated by the enum </span> <span class="c1"># ncs_service_operation op parameter. Conversely </span> <span class="c1"># post_modification() is invoked after create, update, or delete </span> <span class="c1"># of the service. These functions can be useful e.g. for </span> <span class="c1"># allocations that should be stored and existing also when the </span> <span class="c1"># service instance is removed. </span> <span class="c1"># @Service.pre_lock_create </span> <span class="c1"># def cb_pre_lock_create(self, tctx, root, service, proplist): </span> <span class="c1"># self.log.info('Service plcreate(service=', service._path, ')') </span> <span class="c1"># @Service.pre_modification </span> <span class="c1"># def cb_pre_modification(self, tctx, op, kp, root, proplist): </span> <span class="c1"># self.log.info('Service premod(service=', kp, ')') </span> <span class="c1"># @Service.post_modification </span> <span class="c1"># def cb_post_modification(self, tctx, op, kp, root, proplist): </span> <span class="c1"># self.log.info('Service premod(service=', kp, ')') </span> <span class="c1"># --------------------------------------------- # COMPONENT THREAD THAT WILL BE STARTED BY NCS. # --------------------------------------------- </span><span class="k">class</span> <span class="nc">Main</span><span class="p">(</span><span class="n">ncs</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">Application</span><span class="p">):</span> <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="c1"># The application class sets up logging for us. It is accessible </span> <span class="c1"># through 'self.log' and is a ncs.log.Log instance. </span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'Main RUNNING'</span><span class="p">)</span> <span class="c1"># Service callbacks require a registration for a 'service point', </span> <span class="c1"># as specified in the corresponding data model. </span> <span class="c1"># </span> <span class="bp">self</span><span class="o">.</span><span class="n">register_service</span><span class="p">(</span><span class="s">'ntc-action-example-servicepoint'</span><span class="p">,</span> <span class="n">ServiceCallbacks</span><span class="p">)</span> <span class="c1"># When using actions, this is how we register them: </span> <span class="c1"># </span> <span class="bp">self</span><span class="o">.</span><span class="n">register_action</span><span class="p">(</span><span class="s">'ntc-action-example-action'</span><span class="p">,</span> <span class="n">DoubleAction</span><span class="p">)</span> <span class="c1"># If we registered any callback(s) above, the Application class </span> <span class="c1"># took care of creating a daemon (related to the service/action point). </span> <span class="c1"># When this setup method is finished, all registrations are </span> <span class="c1"># considered done and the application is 'started'. </span> <span class="k">def</span> <span class="nf">teardown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="c1"># When the application is finished (which would happen if NCS went </span> <span class="c1"># down, packages were reloaded or some error occurred) this teardown </span> <span class="c1"># method will be called. </span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'Main FINISHED'</span><span class="p">)</span> </code></pre></div></div> <p>Since I am focusing just on the action, I can reduce the Python file to simply this:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># -*- mode: python; python-indent: 4 -*- </span><span class="kn">import</span> <span class="nn">ncs</span> <span class="kn">from</span> <span class="nn">ncs.dp</span> <span class="kn">import</span> <span class="n">Action</span> <span class="c1"># --------------- # ACTIONS EXAMPLE # --------------- </span><span class="k">class</span> <span class="nc">DoubleAction</span><span class="p">(</span><span class="n">Action</span><span class="p">):</span> <span class="o">@</span><span class="n">Action</span><span class="o">.</span><span class="n">action</span> <span class="k">def</span> <span class="nf">cb_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uinfo</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">kp</span><span class="p">,</span> <span class="nb">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">trans</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'action name: '</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'action input.number: '</span><span class="p">,</span> <span class="nb">input</span><span class="o">.</span><span class="n">number</span><span class="p">)</span> <span class="c1"># Updating the output data structure will result in a response </span> <span class="c1"># being returned to the caller. </span> <span class="n">output</span><span class="o">.</span><span class="n">result</span> <span class="o">=</span> <span class="nb">input</span><span class="o">.</span><span class="n">number</span> <span class="o">*</span> <span class="mi">2</span> <span class="c1"># --------------------------------------------- # COMPONENT THREAD THAT WILL BE STARTED BY NCS. # --------------------------------------------- </span><span class="k">class</span> <span class="nc">Main</span><span class="p">(</span><span class="n">ncs</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">Application</span><span class="p">):</span> <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="c1"># The application class sets up logging for us. It is accessible </span> <span class="c1"># through 'self.log' and is a ncs.log.Log instance. </span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'Main RUNNING'</span><span class="p">)</span> <span class="c1"># When using actions, this is how we register them: </span> <span class="c1"># </span> <span class="bp">self</span><span class="o">.</span><span class="n">register_action</span><span class="p">(</span><span class="s">'ntc-action-example-action'</span><span class="p">,</span> <span class="n">DoubleAction</span><span class="p">)</span> <span class="c1"># If we registered any callback(s) above, the Application class </span> <span class="c1"># took care of creating a daemon (related to the service/action point). </span> <span class="c1"># When this setup method is finished, all registrations are </span> <span class="c1"># considered done and the application is 'started'. </span> <span class="k">def</span> <span class="nf">teardown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="c1"># When the application is finished (which would happen if NCS went </span> <span class="c1"># down, packages were reloaded or some error occurred) this teardown </span> <span class="c1"># method will be called. </span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'Main FINISHED'</span><span class="p">)</span> </code></pre></div></div> <h2 id="loading-in-the-package-and-using-the-action">Loading in the Package and Using the Action</h2> <p>As with any package, the Yang module needs to be compiled and the NSO packages need to be reloaded:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>packages<span class="nv">$ </span><span class="nb">cd </span>ntc-action-example/ ntc-action-example<span class="nv">$ </span><span class="nb">cd </span>src src<span class="nv">$ </span><span class="nb">ls </span>Makefile yang src<span class="nv">$ </span>make <span class="nb">mkdir</span> <span class="nt">-p</span> ../load-dir <span class="nb">mkdir</span> <span class="nt">-p</span> java/src /Users/jabelk/ncs-all/nso-5-install/bin/ncsc <span class="sb">`</span><span class="nb">ls </span>ntc-action-example-ann.yang <span class="o">&gt;</span> /dev/null 2&gt;&amp;1 <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"-a ntc-action-example-ann.yang"</span><span class="sb">`</span> <span class="se">\</span> <span class="nt">-c</span> <span class="nt">-o</span> ../load-dir/ntc-action-example.fxs yang/ntc-action-example.yang packages<span class="nv">$ </span>ncs_cli <span class="nt">-C</span> <span class="nt">-u</span> admin admin connected from 127.0.0.1 using console on ntc-jasonbelk-macbook-pro.local admin@ncs# packages reload force <span class="o">&gt;&gt;&gt;</span> System upgrade is starting. <span class="o">&gt;&gt;&gt;</span> Sessions <span class="k">in </span>configure mode must <span class="nb">exit </span>to operational mode. <span class="o">&gt;&gt;&gt;</span> No configuration changes can be performed <span class="k">until </span>upgrade has completed. <span class="o">&gt;&gt;&gt;</span> System upgrade has completed successfully. reload-result <span class="o">{</span> package ntc-action-example result <span class="nb">true</span> <span class="o">}</span> admin@ncs# conf Entering configuration mode terminal admin@ncs<span class="o">(</span>config<span class="o">)</span><span class="c"># action double ?</span> Possible completions: number &lt;cr&gt; admin@ncs<span class="o">(</span>config<span class="o">)</span><span class="c"># action double number 22</span> result 44 admin@ncs<span class="o">(</span>config<span class="o">)</span><span class="c"># action double QQ</span> <span class="nt">---------------------------------</span>^ syntax error: expecting number - admin@ncs<span class="o">(</span>config<span class="o">)</span><span class="c"># </span> </code></pre></div></div> <p>We can see the <code class="highlighter-rouge">ntc-action-example</code> shows up in our packages, and it executes the Python, doubling whatever integer we give it. Since it has a Yang model enforcing the inputs, we are unable to give it the invalid <code class="highlighter-rouge">QQ</code> value, thus protecting the Python code from executing a doubling action on a string.</p> <h3 id="how-did-that-work">How did that work?</h3> <p>From the Yang side, the key connecting statement to note is <code class="highlighter-rouge">tailf:actionpoint ntc-action-example-action;</code>, and also the names of the leafs used (in this case just <code class="highlighter-rouge">number</code> under input).</p> <p>Then in the Python file note at the bottom of the Python code, under the <code class="highlighter-rouge">Main</code> class in the <code class="highlighter-rouge">setup</code> function: <code class="highlighter-rouge">self.register_action('ntc-action-example-action', DoubleAction)</code>, is registering the action <code class="highlighter-rouge">ntc-action-example-action</code> to be associated with the Python class defined in that file <code class="highlighter-rouge">DoubleAction</code>.</p> <p>From the Python <code class="highlighter-rouge">DoubleAction</code> Class the key lines to note are:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'action name: '</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'action input.number: '</span><span class="p">,</span> <span class="nb">input</span><span class="o">.</span><span class="n">number</span><span class="p">)</span> <span class="n">output</span><span class="o">.</span><span class="n">result</span> <span class="o">=</span> <span class="nb">input</span><span class="o">.</span><span class="n">number</span> <span class="o">*</span> <span class="mi">2</span> </code></pre></div></div> <p>Where we use the syntax <code class="highlighter-rouge">input.LEAFNAME</code>, in this case <code class="highlighter-rouge">input.number</code> to access the value passed in by the action input, and then store the result in the <code class="highlighter-rouge">output.result</code> leaf.</p> <h1 id="modifying-the-nso-custom-action">Modifying the NSO Custom Action</h1> <p>Let’s make a custom action which gives us random chuck norris jokes! This is inspired by <a href="https://www.youtube.com/watch?v=RiNvIg-Db_k">Hank Preston’s demos using Norris to demo REST APIs</a>.</p> <p>Basically, I am taking the DoubleAction example, and tweaking the class names, editing the leafs a bit (and namespace to NTC!), and adding in a Python <code class="highlighter-rouge">requests</code> call in the action. The reason I am showing requests is because something you might want to have available in an NSO package is a requests call to some other application (like Netbox, ServiceNow, etc) to grab some data and then use it to make a decision, or store it in the NSO CDB.</p> <h2 id="the-yang-file">The Yang File</h2> <p>In this Yang file we have an input of <code class="highlighter-rouge">number-of-jokes</code> which is expecting an integer of how many jokes we want, and then a <code class="highlighter-rouge">result</code> leaf which is the string of all the jokes we got.</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">module</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span> <span class="p">{</span> <span class="nx">namespace</span> <span class="s2">"http://networktocode.com/ntc-action-example"</span><span class="p">;</span> <span class="nx">prefix</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span><span class="p">;</span> <span class="k">import</span> <span class="nx">tailf</span><span class="o">-</span><span class="nx">common</span> <span class="p">{</span> <span class="nx">prefix</span> <span class="nx">tailf</span><span class="p">;</span> <span class="p">}</span> <span class="k">import</span> <span class="nx">tailf</span><span class="o">-</span><span class="nx">ncs</span> <span class="p">{</span> <span class="nx">prefix</span> <span class="nx">ncs</span><span class="p">;</span> <span class="p">}</span> <span class="nx">description</span> <span class="s2">"An action example"</span><span class="p">;</span> <span class="nx">revision</span> <span class="mi">2019</span><span class="o">-</span><span class="mi">09</span><span class="o">-</span><span class="mi">12</span> <span class="p">{</span> <span class="nx">description</span> <span class="s2">"Giving some details for an action example."</span><span class="p">;</span> <span class="p">}</span> <span class="nx">container</span> <span class="nx">action</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">action</span> <span class="nx">random</span><span class="o">-</span><span class="nx">norris</span><span class="o">-</span><span class="nx">joke</span> <span class="p">{</span> <span class="nl">tailf</span><span class="p">:</span><span class="nx">actionpoint</span> <span class="nx">ntc</span><span class="o">-</span><span class="nx">action</span><span class="o">-</span><span class="nx">example</span><span class="o">-</span><span class="nx">action</span><span class="p">;</span> <span class="nx">input</span> <span class="p">{</span> <span class="nx">leaf</span> <span class="nx">number</span><span class="o">-</span><span class="k">of</span><span class="o">-</span><span class="nx">jokes</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">uint8</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">output</span> <span class="p">{</span> <span class="nx">leaf</span> <span class="nx">result</span> <span class="p">{</span> <span class="nx">type</span> <span class="nx">string</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre></div></div> <h2 id="the-python-file">The Python File</h2> <p>In this updated <code class="highlighter-rouge">main.py</code>, we added an import for <code class="highlighter-rouge">requests</code>, changed the <code class="highlighter-rouge">DoubleAction</code> in the class name and in the <code class="highlighter-rouge">Main</code> <code class="highlighter-rouge">setup</code> function. The Python code will take the integer input, call the API, extract the data from the nested data structure given from the API, and then store it in the <code class="highlighter-rouge">result</code> leaf with some newline padding for readability.</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># -*- mode: python; python-indent: 4 -*- </span><span class="kn">import</span> <span class="nn">ncs</span> <span class="kn">from</span> <span class="nn">ncs.dp</span> <span class="kn">import</span> <span class="n">Action</span> <span class="kn">import</span> <span class="nn">requests</span> <span class="k">class</span> <span class="nc">NorrisAction</span><span class="p">(</span><span class="n">Action</span><span class="p">):</span> <span class="o">@</span><span class="n">Action</span><span class="o">.</span><span class="n">action</span> <span class="k">def</span> <span class="nf">cb_action</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">uinfo</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">kp</span><span class="p">,</span> <span class="nb">input</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">trans</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'action name: '</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'action input.number: '</span><span class="p">,</span> <span class="nb">input</span><span class="o">.</span><span class="n">number_of_jokes</span><span class="p">)</span> <span class="n">url</span> <span class="o">=</span> <span class="s">"http://api.icndb.com/jokes/random/"</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="nb">input</span><span class="o">.</span><span class="n">number_of_jokes</span><span class="p">)</span> <span class="n">resp</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span> <span class="n">data</span> <span class="o">=</span> <span class="n">resp</span><span class="o">.</span><span class="n">json</span><span class="p">()</span> <span class="n">jokes</span> <span class="o">=</span> <span class="n">data</span><span class="p">[</span><span class="s">"value"</span><span class="p">]</span> <span class="n">joke_response</span> <span class="o">=</span> <span class="s">"</span><span class="se">\n\n</span><span class="s">"</span> <span class="k">for</span> <span class="n">joke</span> <span class="ow">in</span> <span class="n">jokes</span><span class="p">:</span> <span class="n">joke_response</span> <span class="o">=</span> <span class="n">joke_response</span> <span class="o">+</span> <span class="n">joke</span><span class="p">[</span><span class="s">"joke"</span><span class="p">]</span> <span class="n">joke_response</span> <span class="o">=</span> <span class="n">joke_response</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n\n</span><span class="s">"</span> <span class="n">output</span><span class="o">.</span><span class="n">result</span> <span class="o">=</span> <span class="n">joke_response</span> <span class="k">class</span> <span class="nc">Main</span><span class="p">(</span><span class="n">ncs</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">Application</span><span class="p">):</span> <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'Main RUNNING'</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">register_action</span><span class="p">(</span><span class="s">'ntc-action-example-action'</span><span class="p">,</span> <span class="n">NorrisAction</span><span class="p">)</span> <span class="k">def</span> <span class="nf">teardown</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s">'Main FINISHED'</span><span class="p">)</span> </code></pre></div></div> <h3 id="seeing-is-believing">Seeing is Believing</h3> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>admin@ncs# packages reload reload-result <span class="o">{</span> package ntc-action-example result <span class="nb">true</span> <span class="o">}</span> admin@ncs# conf Entering configuration mode terminal admin@ncs<span class="o">(</span>config<span class="o">)</span><span class="c"># action random-norris-joke number-of-jokes 2</span> result Chuck Norris has banned rainbows from the state of North Dakota. When you play Monopoly with Chuck Norris, you <span class="k">do </span>not pass go, and you <span class="k">do </span>not collect two hundred dollars. You will be lucky <span class="k">if </span>you make it out alive. admin@ncs<span class="o">(</span>config<span class="o">)</span><span class="c"># action random-norris-joke number-of-jokes 6</span> result Chuck Norris once roundhouse kicked someone so hard that his foot broke the speed of light, went back <span class="k">in </span><span class="nb">time</span>, and killed Amelia Earhart <span class="k">while </span>she was flying over the Pacific Ocean. Chuck Norris doesnt shave<span class="p">;</span> he kicks himself <span class="k">in </span>the face. The only thing that can <span class="nb">cut </span>Chuck Norris is Chuck Norris. Chuck Norris is the only man to ever defeat a brick wall <span class="k">in </span>a game of tennis. Chuck Norris doesn<span class="s1">'t throw up if he drinks too much. Chuck Norris throws down! Newton'</span>s Third Law is wrong: Although it states that <span class="k">for </span>each action, there is an equal and opposite reaction, there is no force equal <span class="k">in </span>reaction to a Chuck Norris roundhouse kick. Chuck Norris causes the Windows Blue Screen of Death. admin@ncs<span class="o">(</span>config<span class="o">)</span><span class="c">#</span> </code></pre></div></div> <h2 id="conclusion">Conclusion</h2> <p>One other thing to note, is that NSO actions are incredibly powerful since they also can access the NSO CDB. Since the NSO CDB has a parsed representation of the snapshot of the entire network device inventory, this means you can leverage any data easily using the NSO Python <code class="highlighter-rouge">ncs</code> library to apply CRUD operations on any data within the CDB.</p> <p>If you need a primer on the NSO Python API, check out my tutorial published on <a href="https://developer.cisco.com/learning/lab/hands-on-nso-simple-python/step/1">DevNet here</a>.</p> <p>The final package code can <a href="https://github.com/jabelk/ntc-action-example/">be viewed here</a>.</p> <p>-JB</p>Jason BelkI recently brought up the usefulness of custom NSO Actions in a Twitter discussion with several Cisco friends, and promised to provide a brief demo to illustrate how to use them. This is not the first time I have had this type of discussion. People who have been exposed to NSO’s functionality often gravitate towards and focus on the service mechanics, and rightly so. They are incredibly powerful and a core feature for the product. However, the flexible utility of using NSO actions is an underrated feature of NSO, which I want to educate others about.Find us at AnsibleFest2019-08-29T00:00:00+00:002019-08-29T00:00:00+00:00https://www.networktocode.com/blog/post/find-us-at-ansiblefest<p>As I’m sure you know, we here at Network to Code are regular Ansible users – Ansible not only allows us to quickly start automating networks for our clients, it also helps us to focus on big picture issues instead of getting bogged down in minor details. With this year’s AnsibleFest just around the corner, the Network to Code team is ready to learn more about what’s next when it comes to new collections, the evolution of the Network Engine and the development of Ansible tower.</p> <p>But conferences like AnsibleFest aren’t just an exciting opportunity to hear more about future developments, they also give us the opportunity to meet members of the Network to Code online community in real life. We hope you’ll have a chance to stop by our booth for our demonstrations or a quick chat if you’re in Atlanta.</p> <p>Do you have a specific issue you’re struggling with or an interest in discussing your own network automation project with one of our experts? Good news–we’ve also set aside time for one-on-one meetings. <a href="https://calendly.com/networktocode">Sign-ups are open now,</a> so be sure to grab a spot today.</p> <p>We look forward to seeing you in Atlanta!</p> <p>-Diana</p>Diana DillAs I’m sure you know, we here at Network to Code are regular Ansible users – Ansible not only allows us to quickly start automating networks for our clients, it also helps us to focus on big picture issues instead of getting bogged down in minor details. With this year’s AnsibleFest just around the corner, the Network to Code team is ready to learn more about what’s next when it comes to new collections, the evolution of the Network Engine and the development of Ansible tower.