Brief introduction into pySROS and CI/CD pipelines
The pySROS python library
pySROS is a Python module that provides an API for managing and automating Nokia routers running SR OS. It allows network engineers and developers to:
- Connect to SR OS devices using NetConf
- Execute commands
- Retrieve operational data
- Configure network elements
This library simplifies the process of automating tasks on Nokia’s SR OS, enabling more efficient network management and configuration.
Besides the use-cases listed above, pySROS scripts can also be executed locally on Nokia Routers running SR OS. This allows the creation of:
- local, on-box automations
- custom CLI commands
More details on the pySROS library can be found here.
CI/CD pipelines
A CI/CD (Continuous Integration/Continuous Delivery) pipeline is an automated software development practice that enables frequent, reliable updates to applications. It involves:
- Code integration
- Automated building
- Testing
- Deployment
This process helps catch bugs early, improves collaboration, and allows for rapid, consistent software releases.
In the context of Telecommunication Network Automation, a CI/CD pipeline focuses on automating network changes and configurations. It typically involves:
- Code integration for network configurations
- Automated testing of network changes
- Staged deployment across network devices
- Monitoring and rollback capabilities
This approach enables telecom operators to implement network changes more efficiently, reduce errors, and maintain network stability while adapting to new technologies and service requirements.
Use-Case description and reasoning
Having a python library that simplifies the programmable interaction with Nokia SR OS based routers it simply makes sense to investigate the usage of this library in a CI/CD pipeline. In a first step the basic use-case being explored here is the programmatic, scheduled retrieval of the network elements full configuration.
Basic Implementation example
Environment
For this example implementation of a CI/CD pipeline a GitLab Community server is being used. The GitLab runner uses the docker executor to run jobs. See below table for more information on software and their respective releases used in this example.
| Software | Version | |
|---|---|---|
| GitLab Community Edition | 15.4.6 | |
| pySROS | 24.7.1 | |
| SROS | 23.7R2 |
Implementation Description
As a start, the most basic implementation here only features a single stage CI/CD pipeline. In that single state a pySROS based script is being called to retrieve the full configuration.
Example gitlab-ci.yaml definition:
gitlab-ci.yaml
image: python:3.11.3
stages:
- backup
before_script:
- pip install virtualenv
- virtualenv venv
- source venv/bin/activate
- git config --global user.email "$GITLAB_USER_EMAIL"
- git config --global user.name "$GITLAB_USER_ID"
backup-job:
stage: backup
variables:
GIT_STRATEGY: clone
script:
- git checkout "$CI_COMMIT_REF_NAME"
- pip install -r requirements.txt
- python backup.py
- git add ./*
- git commit -m "push back from pipeline"
- git remote set-url --push origin "https://$TOKEN_NAME:$ACCESS_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git"
- git push --set-upstream origin $CI_COMMIT_BRANCH -o ci.skip
tags:
- runner8
What this pipeline does is the following:
- it prepares the environment in the before script
- it clones this git repo
- it runs a script which is part of this git repo called “backup.py”
- it adds all files to this local representation of the git repo
- it creates a new commit
- it sets the remote git repo URL and pushes the local definition to the remote repo
What you’ll notice is that there are a few variable being used in this pipeline definition. Some of them are default variable such as “$CI_SERVER_HOST” or “$CI_PROJECT_PATH”. But others need to be specifically defined in the git repo such as “$TOKEN_NAME” and “$ACCESS_TOKEN”.
In addition, a custom GitLab Runner is being used that is selected using the tag “runner8”. This is required as in this specific case only this dedicated runner has access to the network elements.
The backup.py script is also quite simple. See the script used in this example below:
Backup.py
import yaml
import sys
import os
from pysros.management import connect
from pathlib import Path
def get_connection(host=None, username=None, password=None, port=830, hostkey_verify=False):
"""
Function definition to obtain a Connection object to a specific
SR OS device and access the model-driven information.
This function also checks whether the script is being executed
locally on a pySROS capable SROS device or on a remote machine.
:parameter host: The hostname or IP address of the SR OS node.
:type host: str
:paramater credentials: The username and password to connect
to the SR OS node.
:type credentials: dict
:parameter port: The TCP port for the connection to the SR OS node.
:type port: int
:returns: Connection object for the SR OS node.
:rtype: :py:class:`pysros.management.Connection`
"""
try:
connection_object = connect(
host=host,
username=username,
password=password,
port=port,
hostkey_verify=hostkey_verify
)
except RuntimeError as error1:
print("Failed to connect. Error:", error1)
sys.exit(-1)
return connection_object
def getConfig(connection_object, path):
"""
Dedicated function to retrieve required config.
:parameter connection_object: The connection object
:type connection_object: dict
:paramater path: xpath pointing towards desired config
:type path: str
:returns: A tuple holding the required data.
:rtype: tuple
"""
config = connection_object.running.get(path)
return config
def ConfigToJson(connection_object, path, config):
"""
Dedicated function to convert config to json.
:parameter connection_object: The connection object
:type connection_object: dict
:paramater config: config retrieved from node
:type config: dict
:returns:
:rtype:
"""
jsonConfig = connection_object.convert(path=path, payload=config, source_format="pysros", destination_format="json", pretty_print=True)
return jsonConfig
def loadInventory(inventoryFile):
f = open(inventoryFile)
inv = yaml.safe_load(f)
return inv
def main():
"""
"""
path = '/nokia-conf:configure'
inventory = loadInventory('inventory.yaml')
NeUsername = os.getenv('NEUSERNAME')
NePassword = os.getenv('NEPASSWORD')
for entry in inventory['hosts']:
filepath = Path(entry + '_config.json')
print("Establishing Connection to "+ entry +"\n")
connection_object = get_connection(host=entry, username=NeUsername, password=NePassword)
print("Fetching config from "+ entry +"\n")
actualConfig = getConfig(connection_object, path)
actualJsonConfig = ConfigToJson(connection_object, path, actualConfig)
with filepath.open("w", encoding ="utf-8") as f:
f.write(actualJsonConfig)
f.close()
if __name__ == "__main__":
main()
What this script does is the following:
- it loads and loops through an inventory file called inventory.yaml
- for each item in the inventory file it does:
- establish a connection
- fetch the whole config from “/nokia-conf:configure” path
- transform the config into JSON format
- write the config into a file
Note: Credentials are loaded from the environment variables. This leverages the GitLab variable management again.
The example inventory.yaml file looks like this:
inventory.yaml
hosts:
- r131
[...]
- r137
Note: In order to use hostnames for the communication with remote routers, the GitLab Runner must be able to resolve them.
Result
The result of this basic implementation of using a pySROS based script in a GitLab CI/CD pipeline is that we can retrieve the Nokia SR OS configuration from the Routers, transform it into human friendly JSON format and automatically push it back into the git repo. This helps to:
- keep scheduled backups of your SR OS based routers configuration
- easily track changes between backups based on well-known git version control
Issues with this implementation
-
One of the key features of pySROS is the simplification of the NetConf communication with Nokia SR OS based devices. This simplification is achieved through its YANG awareness. In default operations, pySROS is retrieving the YANG models from the network elements upon first session establishment. These YANG models are then usually being cached to accelerate operations in subsequent sessions. That does not work in this implementation of a CI/CD pipeline as the cache is not persistent across pipeline executions.
-
There is no concurrency. Network Elements are being accessed one after the other. For simple labs this is surely not an issue. For huge networks this can potentially pose an issue as the runtime of the script executing would be quite long.