Run your Python code using Python Virtual Machine (VM).
NSO is capable of starting one or several Python VMs where Python code in user-provided packages can run.
An NSO package containing a python
directory will be considered to be a Python Package. By default, a Python VM will be started for each Python package that has a python-class-name
defined in its package-meta-data.xml
file. In this Python VM, the PYTHONPATH
environment variable will be pointing to the python
directory in the package.
If any required package that is listed in the package-meta-data.xml
contains a python
directory, the path to that directory will be added to the PYTHONPATH
of the started Python VM and thus its accompanying Python code will be accessible.
Several Python packages can be started in the same Python VM if their corresponding package-meta-data.xml
files contain the same python-package/vm-name
.
A Python package skeleton can be created by making use of the ncs-make-package
command:
The tailf-ncs-python-vm.yang
defines the python-vm
container which, along with ncs.conf
, is the entry point for controlling the NSO Python VM functionality. Study the content of the YANG model in the example below (The Python VM YANG Model). For a full explanation of all the configuration data, look at the YANG file and man ncs.conf
. Here will follow a description of the most important configuration parameters.
Note that some of the nodes beneath python-vm
are by default invisible due to a hidden attribute. To make everything under python-vm
visible in the CLI, two steps are required:
First, the following XML snippet must be added to ncs.conf
:\
Next, the unhide
command may be used in the CLI session:
The sanity-checks
/self-assign-warning
controls the self-assignment warnings for Python services with off, log, and alarm (default) modes. An example of a self-assignment:
As several service invocations may run in parallel, self-assignment will likely cause difficult-to-debug issues. An alarm or a log entry will contain a warning and a keypath to the service instance that caused the warning. Example log entry:
With the logging
/level
, the amount of logged information can be controlled. This is a global setting applied to all started Python VMs unless explicitly set for a particular VM, see Debugging of Python packages. The levels correspond to the pre-defined Python levels in the Python logging
module, ranging from level-critical
to level-debug
.
Refer to the official Python documentation for the logging
module for more information about the log levels.
The logging
/log-file-prefix
define the prefix part of the log file path used for the Python VMs. This prefix will be appended with a Python VM-specific suffix which is based on the Python package name or the python-package/vm-name
from the package-meta-data.xml
file. The default prefix is logs/ncs-python-vm
so e.g., if a Python package named l3vpn
is started, a logfile with the name logs/ncs-python-vm-l3vpn.log
will be created.
The status
/start
and status
/current
contains operational data. The status
/start
command will show information about what Python classes, as declared in the package-meta-data.xml
file, were started and whether the outcome was successful or not. The status
/current
command will show which Python classes that are currently running in a separate thread. The latter assumes that the user-provided code cooperates by informing NSO about any thread(s) started by the user code, see Structure of the User-provided Code.
The start
and stop
actions make it possible to start and stop a particular Python VM.
The package-meta-data.xml
file must contain a component
of type application
with a python-class-name
specified as shown in the example below.
The component name (L3VPN Service
in the example) is a human-readable name of this application component. It will be shown when doing show python-vm
in the CLI. The python-class-name
should specify the Python class that implements the application entry point. Note that it needs to be specified using Python's dot notation and should be fully qualified (given the fact that PYTHONPATH
is pointing to the package python
directory).
Study the excerpt of the directory listing from a package named l3vpn
below.
Look closely at the python
directory above. Note that directly under this directory is another directory named the package (l3vpn
) that contains the user code. This is an important structural choice that eliminates the chance of code clashes between dependent packages (only if all dependent packages use this pattern of course).
As you can see, the service.py
is located according to the description above. There is also a __init__.py
(which is empty) there to make the l3vpn
directory considered a module from Python's perspective.
Note the _namespaces/l3vpn_ns.py
file. It is generated from the l3vpn.yang
model using the ncsc --emit-python
command and contains constants representing the namespace and the various components of the YANG model, which the User code can import and make use of.
The service.py
file should include a class definition named Service
which acts as the component's entry point. See The Application Component for details.
Notice that there is also a file named upgrade.py
present which holds the implementation of the upgrade
component specified in the package-meta-data.xml
excerpt above. See The Upgrade Component for details regarding upgrade
components.
application
ComponentThe Python class specified in the package-meta-data.xml
file will be started in a Python thread which we call a component
thread. This Python class should inherit ncs.application.Application
and should implement the methods setup()
and teardown()
.
NSO supports two different modes for executing the implementations of the registered callpoints, threading
and multiprocessing
.
The default threading
mode will use a single thread pool for executing the callbacks for all callpoints.
The multiprocessing
mode will start a subprocess for each callpoint. Depending on the user code, this can greatly improve the performance on systems with a lot of parallel requests, as a separate worker process will be created for each Service, Nano Service, and Action.
The behavior is controlled by three factors:
callpoint-model
setting in the package-meta-data.xml
file.
Number of registered callpoints in the Application
.
Operating System support for killing child processes when the parent exits.
If the callpoint-model
is set to multiprocessing
, more than one callpoint is registered in the Application
and the Operating System supports killing child processes when the parent exits, NSO will enable multiprocessing mode.
The Service
class will be instantiated by NSO when started or whenever packages are reloaded. Custom initialization, such as registering service and action callbacks should be done in the setup()
method. If any cleanup is needed when NSO finishes or when packages are reloaded it should be placed in the teardown()
method.
The existing log functions are named after the standard Python log levels, thus in the example above the self.log
object contains the functions debug
,info
,warning
,error
,critical
. Where to log and with what level can be controlled from NSO?
upgrade
ComponentThe Python class specified in the upgrade
section of package-meta-data.xml
will be run by NSO in a separately started Python VM. The class must be instantiable using the empty constructor and it must have a method called upgrade
as in the example below. It should inherit ncs.upgrade.Upgrade
.
Python code packages are not running with an attached console and the standard out from the Python VMs are collected and put into the common log file ncs-python-vm.log
. Possible Python compilation errors will also end up in this file.
Normally the logging objects provided by the Python APIs are used. They are based on the standard Python logging
module. This gives the possibility to control the logging if needed, e.g., getting a module local logger to increase logging granularity.
The default logging level is set to info
. For debugging purposes, it is very useful to increase the logging level:
This sets the global logging level and will affect all started Python VMs. It is also possible to set the logging level for a single package (or multiple packages running in the same VM), which will take precedence over the global setting:
The debugging output is printed to separate files for each package and the log file naming is ncs-python-vm-
pkg_name
.log
Log file output example for package l3vpn
:
There are occasions where the standard Python installation is incompatible or maybe not preferred to be used together with NSO. In such cases, there are several options to tell NSO to use another Python installation for starting a Python VM.
By default NSO will use the file $NCS_DIR/bin/ncs-start-python-vm
when starting a new Python VM. The last few lines in that file read:
As seen above NSO first looks for python3
and if found it will be used to start the VM. If python3
is not found NSO will try to use the command python
instead. Here we describe a couple of options for deciding which Python NSO should start.
NSO can be configured to use a custom start command for starting a Python VM. This can be done by first copying the file $NCS_DIR/bin/ncs-start-python-vm
to a new file and then changing the last lines of that file to start the desired version of Python. After that, edit ncs.conf
and configure the new file as the start command for a new Python VM. When the file ncs.conf
has been changed reload its content by executing the command ncs --reload
.
Example:
Add the following snippet to ncs.conf
:
The new start-command
will take effect upon the next restart or configuration reload.
python3
or python
Another way of telling NSO to start a specific Python executable is to configure the environment so that executing python3
or python
starts the desired Python. This may be done system-wide or can be made specific for the user running NSO.
Changing the last line of $NCS_DIR/bin/ncs-start-python-vm
is of course an option but altering any of the installation files of NSO is discouraged.