Extension¶
Extending the Connect module requires the following steps:
Reading the MEF standard specification for your desired feature
Translating the
MEFspecification to Inmanta modelExtending the
LSMDeveloping
pluginsWriting unit tests
There are circumstances where you need to introduce additional features to the Connect API which are not part of the MEF standard. For instance, deploying E-Line with either LDP or EVPN. In such cases, we distinctively use the Ext_ prefix for those entities and if required, define custom data types. In other words, by following these steps we update and extend the existing LSM API, and diverge from the MEF standard.
Let’s take L2 MPLS VPN as an example to extend the existing API:
Define the required
types:typedef connectivity_type as string matching self in ["VPWS", "VPLS"] """ L2 connectivity type. VPWS or VPLS. """
Define a new
entity, representing the service/technology:entity Ext_L2MPLSType extends lsm::EmbeddedEntity: """ The entity represents L2 MPLS VPN technology. :attr type: Can be VPWS or VPLS. """ connectivity_type type end
Please note that the
entityis prefixed withExt_and is extending the existinglsm::EmbeddedEntityobject. All extensions to the API have to follow the same structure.EmbeddedEntity contains attributes that should be embedded into a ServiceEntity or another EmbeddedEntity.
Define the implementations (
refinement) for theentities:implementation with_VPWS for Ext_L2MPLSType: """The specific VPWS implementation goes here""" std::print("VPWS selected") end implementation with_VPLS for Ext_L2MPLSType: """The specific VPLS implementation goes here""" std::print("VPLS selected") end
In
with_VPLSimplementation, the attribute types declaration is omitted for brevity; however, the steps are the same as for thewith_VPWSexample.Implement the
refinements:implement Ext_L2MPLSType using with_VPWS when self.connectivity_type=="VPWS" implement Ext_L2MPLSType using with_VPLS when self.connectivity_type=="VPLS"
The
implementis used to construct and connect theentities,refinementsand their attributes together.whenis a compile timeif, which gives us control over how the model should be constructed.
Developing Plugins¶
There are times when you need to perform some additional validation, modification or conversions on the input data. A very simple example would be converting the connectivity_type value provided by the user in lowercase to uppercase. Plugins can be used to provide the aforementioned functionalities.
Head to __init__.py file under the plugins directory and implement the solution:
from inmanta.plugins import plugin
@plugin
def transform_to_uppercase(word: "string?") -> "string":
return word.upper()
There are a few point to note here:
It is strongly advised to use type hinting; however, the types here are accommodated with double quotes since they differ from Python’s built-in data types.
The
@plugindecorator makes the function available to Inmanta model and it can then be referenced inside your model. This decorated function (transform_to_uppercase) can utilize any other undecorated function defined inside the__init__.pyfile for further processing.
Writing Unit Tests¶
The most basic type of testing is a compile test in which an Inmanta model is fed to project.compile() and then compiler will run the initial assessments on our provided data/model.
import pytest
from pytest_inmanta.plugin import Project
def test_connectivity_type(project: Project) -> None:
project.compile(
"""
import connect
backend = connect::Ext_L2MPLSType(
type="VPWS"
)
"""
)