Skip to main content

Model-Driven Programmability

· 6 min read

Introduction

As network automation becomes more and more important, it's important to be familiar with the key concepts involved. One of the most important concepts is model-driven programmability.

What is model-driven programmability?

Model-driven programmability is a way of representing network devices and their capabilities in a standardized way. This makes it easier to automate tasks on network devices, as we can use a single model to represent all of the devices in wer network.

Why is model-driven programmability important?

Traditionally, network devices have been managed using a CLI (command-line interface). This approach is not very well-suited for automation, as it's difficult to extract information from the unstructured text output of the CLI.

Model-driven programmability solves this problem by providing a standardized way to represent network devices and their capabilities. This makes it easier to write automation scripts that can interact with network devices in a consistent way.

Data models describe the syntax and semantics of working with specific data objects. They answer questions such as:

  • How is a VLAN object structured? What properties does it have?
  • What is the range of a valid VLAN ID?
  • Can a VLAN name have spaces in it?
  • Is the value a string or an integer?

A data model is like a standardized framework used to describe a data object of an environment. The thought behind the data model is to provide a consistent and reliable format to communicate the data objects we are working with on wer device.

One misconception is that data models handle the exchange of data, which is not the case. Data models are just standardized frameworks that describe wer environment. Data models have nothing to do with data transmission. Instead, protocols such as Network Configuration Protocol (NETCONF) and RESTCONF send structured data (e.g., XML or JSON) encoded documents using a framework a given model governs.

As models focus on what is the content and not so much on how it is exchanged, data models allow the API to do the following:

  • Provide efficient and easy-to-use tooling to consume it (programming libraries).
  • Support extensible and open interfaces (REST-based, NETCONF, and so on).
  • Add flexibility and support for different encoding formats (XML and JSON).
  • Support different types of transport (SSH, HTTPS, HTTP/2, etc.).

Now that we understand a little more about model-driven programmability, it can be taken a step further and discuss how model-driven programmability fits into the larger concepts of APIs and network programmability.

The core structure of a device API, therefore, consists of the following:

  • Data models: The foundation of the API consists of data models. Data models define the syntax and semantics, including constraints of working with the API. YANG is a data model used by NETCONF, RESTCONF, and gRPC.
  • Transport: Model-driven APIs support several transport methods, including SSH, TLS, and HTTP or HTTPS.
  • Encoding: Model-driven APIs support the choice of encoding, including XML and JSON, and custom encodings such as Google protocol buffers.
  • Protocols: Model-driven APIs also support multiple options for protocols, with the three core protocols being NETCONF, RESTCONF, and gRPC

Now that we have an idea of what a data model is and the role model-driven programmability plays in API architecture, then this would be a good time to take the 50,000-foot overview of model-driven programmability's role in network automation. Consider the following figure: Applications can now use programming libraries or development kits that leverage data models to simplify access to the API. Behind the scenes, different protocols using various encodings and transports may be used to exchange data but the application does not need to concern itself with that. It only needs to operate on the data models. Likewise, the API server uses the same model, regardless of the protocol, allowing the server to support as many protocols as required.

Imagine that an application has requested to retrieve an IP address on an interface of a network device. The data request is structured according to the application's YANG data model. The application then sends a request to the API server on the network device. The network device can identify what data the request asks and constructs a response for the application. The response is formatted in an expected structure or model that the application is expecting based on YANG. For example, if the request were for an IP address on an interface, it would use the proper data model for interface IP addresses. The network device sends the response back and is received by the application. The data structure is in a format that the application expects since both sides are using the YANG data model. The figure demonstrates the role of data models in network automation in action.

The two main data encoding formats commonly used are XML and JSON. Each provides a structured way of data formatting to send data between two computer systems. Since data conforms to the model, it is easier to navigate and extract relevant information programmatically. This is in stark contrast to using Secure Shell (SSH) issuing CLI commands in which data is sent as raw strings (text).

Sometimes the concept of data models gets confused with data encoding formats like XML and JSON. The data encoding formats involve the serialization format of how the data is transmitted. More simply stated, XML and JSON are not about the framework of how wer model is presented but the way the data is formatted for transport.

XML and JSON are used for data transmission because they have these features:

  • Human readable because they are self-describing
  • Hierarchical, because they store values within values
  • Parsable and used by many programming languages

As we finish up, it is worth mentioning YANG, which is a data modeling language we will likely encounter on wer network automation journey.

There are whole courses and even books dedicated to YANG, but for now, it is important to understand that YANG is a data modeling language used by protocols like NETCONF, RESTCONF, and gRPC. The YANG data modeling language was published in October 2010 as RFC 6020 to serve as a data modeling language for the protocol NETCONF. So to finally put it all together, the image below illustrates the YANG model-driven programmability stack.

As we can see, data models are not really new but are getting more attention as a building block of model-driven APIs.

References

Should be elegance in Python or not?

· 5 min read

While I am not an expert in Python, I always strive to enhance my code during the development process and I believe that everyone should aim to do so as well. As a result of my efforts, I have noticed significant improvements, particularly in terms of code readability and maintainability. Thus, I would like to share some insights on why elegance matters in Python.

caution

These are all subjective things. That means that there is no single right way to write Python code. What works for one person may not work for another. If you disagree with anything I say, I encourage you to leave a comment or write me an email. Diversity of opinion is enriching, and I am always happy to learn new things.

Introduction

In software development, there is a common debate about whether elegance matters. Some people believe that the most important thing is to get the job done, regardless of how the code looks. Others believe that code should be elegant, readable, and easy to maintain.

In Python, elegance is often considered to be more important than in other programming languages. This is because Python is a very expressive language, and it is possible to write code that is both concise and readable.

The reasons why elegance matters in Python.

First, it makes code easier to read and understand. This is important for anyone who needs to work on the code in the future, whether it is the original author or someone else.

Second, elegant code is more likely to be maintainable. This is because it is easier to modify and update. If a piece of code is not elegant, it can be difficult to figure out how it works, and this can make it difficult to make changes.

Third, elegant code is more likely to be reusable. This is because it is easier to understand and adapt to other projects. If a piece of code is not elegant, it can be difficult to use in other contexts.

Don't call code unreadable just because you don't understand it

If you see code that you don't understand, don't be quick to call it unreadable. It's possible that the code is perfectly readable, but you just don't know how to read it.

For example, if you don't know the Assembly Language you might look at an expression and think it's unreadable. But to an AL programmer, the expression is perfectly readable.

Python vs Assembly Language:
def add(a, b):
return a + b

In general, something is unreadable when you know what all the constituent pieces mean, and yet the pieces are put together in a way that doesn't convey the global meaning well. This could be due to the usage of the wrong constituent pieces, because pieces are missing, because there are superfluous pieces, etc.

So, if you see code that you don't understand, don't call it unreadable. Take the time to learn what the different pieces mean, and then you can judge whether the code is well-written or not.

When looking back..

If you are constantly learning, you will eventually look back at your old code and think "I know a better way to do that." If you write simple and elegant code, you will be able to understand your old code quickly and easily, so you can modify it to include the new, improved solution. This will make your code even clearer and more elegant, and the cycle can continue indefinitely.

Idiomatic code is code whose semantics are easy to understand. If you write idiomatic code, you are opening up the doors for future rewrites of your code. This is a good thing, because code that can be changed is code that can be improved over time.

So, the next time you sit down to write some code, ask yourself: do I want to write code that is easy to understand and modify, or do I want to write code that everyone (including my future self) is scared of going near to?

A few tips for writing elegant Python code

Of course, there are times when elegance is not the most important consideration. For example, if you are writing code that will only be used once, then it may not be worth the time to make it elegant. However, in general, elegance is a good thing to strive for in Python.

Here are a few tips for writing elegant Python code:

  • Use the right tool for the job. Don't use a complex feature if a simpler one will do the job just as well.
  • Use idiomatic code. This means using the features of the language in the way that they were intended to be used.
  • Keep your code concise. This doesn't mean that you should always write the shortest possible code, but it does mean that you should avoid writing code that is unnecessarily long or complex.
  • Use comments. Comments can help to explain what your code is doing, which can make it easier to read and understand. (optional)
  • Test your code. This will help to ensure that it works as expected, and it will also make it easier to debug if something goes wrong.

Usage of underscore in Python

· One min read
  1. Last used value in python console
>>> 10 + 10
20
>> _ * 2
40
  1. Make number more readable
number: int = 1_000_000
  1. Unimportant values
for _ in range(3):
print('hello')
  1. Unimportant exception
try:
result = 1/0
except ZeroDivisionError as _:
print('Handled.')
  1. Unpacking
sample_tuple = (1,2,3,4)
a, _, b, _ = sample_tuple
print(a,b) # 1, 3
  1. Super Unpacking
sample_tuple = (1, 2, 3, 4, 5)
a, *_, b = sample_tuple
print(a, b) #1, 5
  1. protected
from uuid import uuid4
class User:
def __init__(self):
self._id = uuid4()
def _get_id(self):
return self._id

user = User()
# typing user. will not show _get_id from IDE
user._get_id()
  1. Private
def __init__(self):
self.__id = uuid4()
def __get_id(self):
return self.__id
  1. Reserved names
if_ = 10
class_ = "class"
str_ = "str"
  1. dundor method
class Fruit:
def __init__(self):
self.id = uuid4()
def __str__(self):
return f"Fruit: {self.id}"
fruit = Fruit()
print(fruit)
>>> Fruit: 6075f5a9-24bf-4df3-b1ba-3fea2100dd4d

Welcome

· One min read

Welcome to My Website!

Welcome to the site where I share my knowledge and experience in API, network, web, and mobile automation, as well as other useful tips and tricks in Python and other programming languages that I am proficient in! This documentation is intended to provide notes, tips, and best practices for Python automation testing in web and networking contexts, whether you're an experienced tester or just starting out with Python.

I will be updating this documentation regularly with the latest information and insights, so be sure to check back often for new updates. If you have any questions, comments, or suggestions for topics you would like to see covered in this documentation, please don't hesitate to contact me.

Thank you for visiting this documentation, and I hope you find it helpful and informative on your Python automation testing journey!

Best regards,

Thanh Nguyen