NetProvisionCLI - leofurtadonyc/Network-Automation GitHub Wiki
This rebranded customer service provisioning tool is derived from smaller projects previously found in this repository. It will incorporate many things I believe a customer service provisioning tool like this should include. My intention is to develop this beyond a few scripts and design it as a fully functional system. The project will experience quick wins and incremental additions, such as new features or enhanced robustness over time until it reaches the desired level of maturity.
Naturally, it will take considerable time to reach that stage as I continue to add features and fix things that break here and there while avoiding corner cases or anything that might compromise network availability and operational excellence.
Currently, the system uses separate scripts for configuration generation, deployment, and other tasks. I aim to simplify this process by implementing a "one-click" operation that combines configuration generation and deployment into a single action. This will maintain configuration consistency and operational safety. Additionally, I plan to port the system to a web framework and enable integration with BSS/OSS systems for a fully automated customer service provisioning experience.
NetProvisionCLI is a tool designed to automate the generation and deployment of network device configurations for Internet Service Providers (ISPs). It's particularly used for deploying overlay-type configurations for various service offerings.
In ISP environments, network device configurations have two logical layers: the underlay and the overlay. Underlay configurations support the entire business by providing the necessary technologies to enable service offerings. Once deployed and matured, these configurations are mostly immutable and rarely modified unless we need to deploy new devices and scale our networks. Examples include underlay IGP (IS-IS or OSPF), MPLS and MPLS-TE, and internal BGP sessions in any form (like full mesh, clusters of Route Reflectors, and Confederations), along with QoS policies.
NetProvisionCLI, however, does not manage underlay configurations, as that's not its intended purpose. Its primary function is to deploy overlay configurations.
These configurations include Internet access for corporate subscribers, point-to-point and multipoint L2VPN MPLS for private communications among business offices, and L3VPN MPLS. NetProvisionCLI is specifically designed to handle these types of scenarios.
Why use NetProvisionCLI? It ensures configuration consistency across all customers for a particular service. This makes it easier to understand, document, deploy, and troubleshoot. Additionally, it enhances operational safety by reducing configuration errors on network devices. Lastly, it allows for quick and effortless operations.
Let's examine how to get it running in the correct order. There are two ways to interact with NetProvisionCLI utilities. You can use them with Python to manage devices and customer configurations, generate and deploy configurations, and more. Alternatively, you can use the NetProvisionCLI Shell commands to work with this project. Please refer to the corresponding section of this document for information about netprovisioncli_shell.py
.
First, you need to create a user to manage the environment:
python netprovisioncli_adduser.py --username lfurtado --password Lab
User credentials updated.
The credentials are securely stored and salted under devices/usercredentials.sec
. They won't be stored in MongoDB if that's your project's data source. The database was designed to receive customer and device data from northbound APIs. It uses this data to apply configuration rendering logic and generate the correct deployment configurations rather than managing admin users. This concept may change in the future.
Next, you might want to validate or test your credentials:
python netprovisioncli_testuser.py
Enter your username: lfurtado
Enter your password:
Authentication successful.
Next, set up the data source. NetProvisionCLI can operate using YAML files for network devices and either YAML or JSON files for customer data, or MongDB for customer and device data along with InfluxDB for auditing log and deployed configurations. When running it with data-source set to YAML, this allows NetProvisionCLI to function without a database, making it ideal for use in labs without the need for additional software. We will discuss the database setup later on this page. To start, use the netprovisioncli_settings.py file:
python netprovisioncli_settings.py --show
Current Settings:
data_source: yaml
influxdb:
bucket: netprovision
org: Leonardo Furtado
token: tWd9KFd8P5Urz1Ei6CesC2E5sWuQtiHygCfuwGOu2AIpLqCT_rEM46RCFhT7Ffyiv7w8g0EfDxSw2Inq7UP8Uw==
url: http://localhost:8086
mongodb_connection:
database_name: netprovision
uri: mongodb://localhost:27017/
From this, you can see that the data source is set to YAML, which is the default setting. These configurations are stored in the settings/settings.yaml
file:
cat settings/settings.yaml
data_source: yaml
influxdb:
bucket: netprovision
org: Leonardo Furtado
token: tWd9KFd8P5Urz1Ei6CesC2E5sWuQtiHygCfuwGOu2AIpLqCT_rEM46RCFhT7Ffyiv7w8g0EfDxSw2Inq7UP8Uw==
url: http://localhost:8086
mongodb_connection:
database_name: netprovision
uri: mongodb://localhost:27017/
If you wish to explore more advanced features of NetProvisionCLI, set your data-source to 'mongodb'. Next, configure the MongoDB parameters (database_name and uri) and InfluxDB parameters (bucket, org, token, url) according to your specific installation and use case. We'll discuss these configurations later in this wiki page.
When using a data source set to YAML, devices are stored under devices/network_devices.yaml
. This is a simple and easy-to-understand YAML file. Let's examine some of the important fields, using two devices as examples:
devices:
a-cisco-01:
device_role: 'access'
device_type: 'cisco_xe'
ip_address: '192.168.255.11'
loopback: '10.255.255.1'
customer_provisioning: true
forbidden_interfaces:
- '^GigabitEthernet[2-4]$' # Interfaces not intended for customer configurations; reserved for uplinks and/or management
- '^Loopback[0-9999]$' # No customers should be configured on loopback
pe-juniper-01:
device_role: 'pe'
device_type: 'juniper_junos'
ip_address: '192.168.255.15'
loopback: '10.255.255.5'
customer_provisioning: true
-
device_role
: Currently, either 'access' or 'pe'. This includes a safety check to prevent the deployment of configurations intended for an Access device to a PE device. If such an error occurs during deployment, the script will not deploy an Access configuration to a PE device or vice-versa. -
device_type
: Currently, cisco_xe, cisco_xr, juniper_junos, huawei_vrp, huawei_vrp_xpl. More will follow. -
ip_address
: Management IP address. -
loopback
: IP address of the Loopback interface. This will primarily be used to render the configurations of the L2VPN pseudowires between Access and PE devices. -
customer_provisioning
: Currently, either 'true' or 'false'. This flag essentially allows us to control whether we want to activate new customers on the device. Suppose the device is nearing its capacity limit, or you're planning to replace it and don't want new customers added. In this case, simply set the flag to 'false'. Consequently, the code will prevent you from generating configurations for that device and return an appropriate error message. -
forbidden_interfaces
: One of the worst mistakes you can make on your network is applying customer configurations to incorrect interfaces, such as uplink and loopback interfaces. This parameter helps protect your infrastructure from such errors. If the --access-interface falls under the forbidden interfaces regex, the code will not generate the configurations and will return a message instead.
When working with data-source YAML, there isn't much required for adding customers to a database, as there aren't any. You can generate customer configurations entirely in-line, either by parsing the appropriate parameters or by using recipes written in YAML or JSON. Currently, NetProvisionCLI supports the generation and deployment of configurations for a specific type of service frequently found in ISPs: Internet access over a point-to-point or point-to-multipoint L2VPN over MPLS from an Access device to a PE device where routing occurs.
Of course, I plan to add a variety of combinations and service offerings to meet your needs. I also aim to enable you to define your service offerings and Jinja2 templates without writing a single line of Python code. I'll get there, don't worry! For now, let's examine how this single service is supported by NetProvisionCLI. You may want to explore netprovisioncli_generate.py -h for instructions on how to use it. Here is a sample of a customer config generation:
python netprovisioncli_generate.py \
--customer-name LEONARDOFURTADO \
--access-device a-cisco-01 \
--access-interface GigabitEthernet2 \
--circuit-id 20 \
--qos-input 500 \
--qos-output 500 \
--vlan-id 20 \
--vlan-id-outer 600 \
--pw-id 20 \
--irb-ipaddr 172.16.1.1/30 \
--irb-ipv6addr 2001:db8:1234:1234::1/127 \
--ipv4-lan 172.17.0.0/16 \
--ipv4-nexthop 172.16.1.2 \
--ipv6-lan 2001:db8:2222::/48 \
--ipv6-nexthop 2001:db8:1234:1234::2 \
--pe-device pe-juniper-01 \
--service-type p2p
Using template: p2p_cisco_xe_to_generic_remove.j2 to remove any previous configuration for this customer from the Access device
Using template: p2p_juniper_junos_to_generic_remove.j2 to remove any previous configuration for this customer from the PE device
Using template: p2p_cisco_xe_to_generic.j2 for Access device new configuration
Using template: p2p_juniper_junos_to_generic.j2 for PE device new configuration
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Configuration generation completed successfully.
Configuration generation completed in 0.02 seconds.
The code will render, generate, and write the customer's configurations under the generated_configs/
folder, using appropriate filenames. These files will be kept for the duration of the deployment and removed after completion to avoid issues with outdated configurations and clutter.
If you attempt to generate configurations for a device that is not accepting new customers (customer_provisioning: false
) or using an interface listed under forbidden_interfaces
, the script will fail to produce the configurations. Instead, it will create an error log JSON file in the generated_configs/
directory, stating the reason for the failure. As long as this JSON error file remains in the folder, you will be unable to deploy any configurations on the network until the issue is resolved.
python netprovisioncli_generate.py \
> --customer-name LEONARDOFURTADO \
> --access-device a-cisco-01 \
> --access-interface GigabitEthernet2 \
> --circuit-id 20 \
> --qos-input 500 \
> --qos-output 500 \
> --vlan-id 20 \
> --vlan-id-outer 600 \
> --pw-id 20 \
> --irb-ipaddr 172.16.1.1/30 \
> --irb-ipv6addr 2001:db8:1234:1234::1/127 \
> --ipv4-lan 172.17.0.0/16 \
> --ipv4-nexthop 172.16.1.2 \
> --ipv6-lan 2001:db8:2222::/48 \
> --ipv6-nexthop 2001:db8:1234:1234::2 \
> --pe-device pe-juniper-01 \
> --service-type p2p
Error: The specified interface GigabitEthernet2 on the Access device a-cisco-01 is not allowed for customer configurations.
Error logged to generated_configs/LEONARDOFURTADO_error.json
Checking the contents of that JSON file:
cat generated_configs/LEONARDOFURTADO_error.json
{
"error_message": "The specified interface GigabitEthernet2 on the Access device a-cisco-01 is not allowed for customer configurations.",
"error_code": 400,
"timestamp": "2024-05-24 11:16:07"
}
If you attempt to deploy the configuration for that customer, it will fail if a JSON error file is present. Consider this a safety mechanism.
python netprovisioncli_deploy.py --customer LEONARDOFURTADO --access-device a-cisco-01 --pe-device pe-juniper-01 --username lfurtado
Password:
Deployment aborted due to config generation errors: The specified interface GigabitEthernet2 on the Access device a-cisco-01 is not allowed for customer configurations. (Error Code: 400)
Using recipes to generate customer configurations can make things more interesting. These recipes are either YAML or JSON files, simplifying the creation of customer configurations. This feature was created to receive deployment parameters through northbound APIs (something I will work on soon), which can be stored in either type of file. Let's examine a case using a JSON recipe:
cat recipes/customers/LEONARDOFURTADO.json
{
"customer": {
"name": "LEONARDOFURTADO",
"service_type": "p2p",
"devices": {
"access": {
"name": "a-cisco-01",
"interface": "GigabitEthernet1"
},
"pe": {
"name": "pe-juniper-01"
}
},
"configuration": {
"circuit_id": 20,
"qos": {
"input": 500,
"output": 500
},
"vlan": {
"id": 20,
"outer_id": 600
},
"pseudowire_id": 20,
"irb": {
"ipv4_address": "172.16.1.1/30",
"ipv6_address": "2001:db8:20:20::1/127"
},
"lan": {
"ipv4": {
"network": "172.18.0.0/16",
"next_hop": "172.16.1.2"
},
"ipv6": {
"network": "2001:db8:2222::/48",
"next_hop": "2001:db8:20:20::2"
}
}
}
}
}
I've included some recipe samples in the recipes/
folder for you to explore. To start, let's generate a configuration using one of these recipes. You can choose either JSON or YAML, as both formats are supported.
python netprovisioncli_generate.py --recipe recipes/customers/LEONARDOFURTADO.json
Using template: p2p_cisco_xe_to_generic_remove.j2 to remove any previous configuration for this customer from the Access device
Using template: p2p_juniper_junos_to_generic_remove.j2 to remove any previous configuration for this customer from the PE device
Using template: p2p_cisco_xe_to_generic.j2 for Access device new configuration
Using template: p2p_juniper_junos_to_generic.j2 for PE device new configuration
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deleted any old error log to unblock deployment: generated_configs/LEONARDOFURTADO_error.json
Configuration generation completed successfully.
Configuration generation completed in 0.03 seconds.
Note that the message “Deleted any old error log to unblock deployment” will appear if a JSON error file for a customer is found in the generated_configs/
directory during config generation. If the new config generation is successful, the JSON error file will be deleted, clearing the way for future deployments for that customer.
The heart of our configuration generation features is Jinja2 templates. These templates use variables provided by the user, either in-line or through recipes, to identify the devices and device types for which it should render and generate configurations. You can easily customize these templates, which are located in the templates/
folder. Let's take a look at one of these templates:
{# Policy Template for a Cisco ASR 920 L2VPN Xconnect towards a Cisco IOS XR device #}
interface {{ interface_name }}
service instance {{ service_instance_id }} ethernet
description {{ customer_name }}-{{ service_instance_id }}
{%- if vlan_id and vlan_id_outer %}
encapsulation dot1q {{ vlan_id_outer }} second-dot1q {{ vlan_id }}
{%- endif %}
{%- if vlan_id and not vlan_id_outer%}
encapsulation dot1q {{ vlan_id }}
{%- endif %}
{%- if device_type != 'juniper_junos' and vlan_id and vlan_id_outer %}
rewrite ingress tag pop 2 symmetric
{%- endif %}
{%- if device_type != 'juniper_junos' and vlan_id and not vlan_id_outer %}
rewrite ingress tag pop 1 symmetric
{%- endif %}
service-policy input limita_{{ qos_input }}Mb
service-policy output limita_{{ qos_output }}Mb
l2vpn xconnect context {{ customer_name }}-{{ service_instance_id }}
description {{ customer_name }}-{{ service_instance_id }}
member {{ pe_loopback }} {{ pw_id }} encapsulation mpls
member {{ interface_name }} service-instance {{ service_instance_id }}
At present, the variables my Jinja2 templates ingest are hardcoded in accordance with the context and general logic of my Python code for specific use cases. However, this project will evolve to abstract all of that, enabling you to modify both templates and rendering logic without having to alter much, if any, Python code. For now, it's important to maintain the variables and context to avoid disrupting anything.
After generating the customer's configurations, you can proceed with their deployment. The script netprovisioncli_deploy.py is used for this purpose. Here's how it works. You can parse --password
in-line as well but, if you don't, it will prompt you to supply yours:
python netprovisioncli_deploy.py --customer LEONARDOFURTADO --access-device a-cisco-01 --pe-device pe-juniper-01 --username lfurtado
Password:
Deployment started by root from IP 192.168.0.108 at 2024-05-24 11:36:05.027430
Added configuration for a-cisco-01: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Added configuration for a-cisco-01: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Added configuration for pe-juniper-01: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Added configuration for pe-juniper-01: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deploying configuration for a-cisco-01 from generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Deploying configuration for a-cisco-01 from generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Deploying configuration for pe-juniper-01 from generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Deploying configuration for pe-juniper-01 from generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deployment completed successfully. Detailed audit log saved at: audit_logs/LEONARDOFURTADO_config_deploy_20240524_113642_audit.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
The deployment process is quite simple. It involves two sets of configurations: 'remove' and 'new'. For operational safety purposes, the deployment code initially removes the same type of configuration for a particular customer from the specified network devices. This is done in order to avoid remnants from previous service activations. Only after this removal does the system deploy the new configurations.
In the final step, the system eliminates the generated configurations from the generated_configs/
folder and stores the deployed configurations in the deployed_configs/
folder. Additionally, it provides the path to the audit log entry that pertains to the recent deployment.
Let's examine one of the audit logs, particularly from the deployment illustrated above. The "Activation status" should indicate whether it was a First-time activation, a Re-activation, or a Re-activation with service changes. It should also identify if configuration differences exist between the deployed configuration and the previous configuration for the customer, displaying these differences on the screen.
cat audit_logs/LEONARDOFURTADO_config_deploy_20240524_114524_audit.txt
Operator: root from IP 192.168.0.108
Completed at: 2024-05-24 11:45:08
--------------------------------------------------
Duration of execution: Deployment completed in 20.91 seconds
Device: a-cisco-01 (cisco_xe)
Generated config file: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Deployed config file: deployed_configs/LEONARDOFURTADO_a-cisco-01_20240524_114508.txt
Deployment result: Success
Activation status: Re-activation
Configuration differences:
No changes detected.
--------------------------------------------------
Duration of execution: Deployment completed in 37.48 seconds
Device: pe-juniper-01 (juniper_junos)
Generated config file: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deployed config file: deployed_configs/LEONARDOFURTADO_pe-juniper-01_20240524_114524.txt
Deployment result: Success
Activation status: Re-activation with service changes
Configuration differences:
--- old_config
+++ new_config
@@ -13,13 +13,13 @@
set interfaces irb unit 20 family inet address 172.16.1.1/30
set interfaces irb unit 20 family inet rpf-check
set interfaces irb unit 20 family inet sampling input
-set interfaces irb unit 20 family inet6 address 2001:db8:20:20::1/127
+set interfaces irb unit 20 family inet6 address 2001:db8:1234:1234::1/127
set interfaces irb unit 20 family inet6 rpf-check
set interfaces irb unit 20 family inet6 sampling input
-set routing-options static route 172.18.0.0/16 next-hop 172.16.1.2
-set routing-options static route 172.18.0.0/16 community 65000:10000
-set routing-options static route 172.18.0.0/16 community no-export
-set routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:20:20::2
+set routing-options static route 172.17.0.0/16 next-hop 172.16.1.2
+set routing-options static route 172.17.0.0/16 community 65000:10000
+set routing-options static route 172.17.0.0/16 community no-export
+set routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:1234:1234::2
set routing-options rib inet6.0 static route 2001:db8:2222::/48 community 65000:11000
set routing-options rib inet6.0 static route 2001:db8:2222::/48 community no-export
commit and-quit
--------------------------------------------------
Furthermore, when using a data-source set to YAML, the deployed configurations are always stored in the deployed_configs/
directory. Filenames include the customer name and appropriate timestamps, which are useful for diff check functions.
You can also deactivate a customer from the network, effectively removing their configurations from the network devices. The configuration generation code will produce the necessary configuration removal commands for deployment.
python netprovisioncli_generate.py --recipe recipes/customers/LEONARDOFURTADO.yaml --deactivate
Using template: cisco_xe_to_generic_deactivate.j2 to deactivate configuration for this customer from the Access device
Using template: juniper_junos_to_generic_deactivate.j2 to deactivate configuration for this customer from the PE device
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_deactivate.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_deactivate.txt
Configuration generation completed successfully.
Configuration generation completed in 0.02 seconds.
Then, deploy it:
python netprovisioncli_deploy.py --customer LEONARDOFURTADO --access-device a-cisco-01 --pe-device pe-juniper-01 --username lfurtado
Password:
Deployment started by root from IP 192.168.0.108 at 2024-05-24 12:00:14.674395
Added configuration for a-cisco-01: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_deactivate.txt
Added configuration for pe-juniper-01: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_deactivate.txt
Deploying configuration for a-cisco-01 from generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_deactivate.txt
Deploying configuration for pe-juniper-01 from generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_deactivate.txt
Deployment completed successfully. Detailed audit log saved at: audit_logs/LEONARDOFURTADO_config_deploy_20240524_120033_audit.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_deactivate.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_deactivate.txt
Things start to become more interesting when running NetProvisionCLI with the data source set to MongoDB.
python netprovisioncli_settings.py -h
usage: netprovisioncli_settings.py [-h] [--set-source SET_SOURCE] [--show] [--init] [--username USERNAME] [--password PASSWORD]
Manage settings for NetProvisionCLI.
options:
-h, --help show this help message and exit
--set-source SET_SOURCE
Set the data source (yaml, mongodb)
--show Show current settings
--init Initialize or reset settings to default
--username USERNAME Username for authentication
--password PASSWORD Password for authentication
To modify these settings, you must provide --username
and, optionally, --password
. Here's an example:
python netprovisioncli_settings.py --set-source mongodb --username lfurtado
Password:
Data source set to MongoDB.
python netprovisioncli_settings.py --show
Current Settings:
data_source: mongodb
influxdb:
bucket: netprovision
org: Leonardo Furtado
token: tWd9KFd8P5Urz1Ei6CesC2E5sWuQtiHygCfuwGOu2AIpLqCT_rEM46RCFhT7Ffyiv7w8g0EfDxSw2Inq7UP8Uw==
url: http://localhost:8086
mongodb_connection:
database_name: netprovision
uri: mongodb://localhost:27017/
I plan to enhance the code to accept more arguments. This will allow you to configure MongoDB (database_name and uri) and InfluxDB (bucket, org, token, and url), eliminating the need to manually edit the settings/settings.yaml
file. Until these changes are implemented, manual editing is required. However, you can switch from a data-source yaml to MongoDB using netprovisioncli_settings.py.
From now on, the following changes will occur in your work with this project:
- Deployed configurations will no longer be stored in the
deployed_configs/
folder. They will now be timestamped and stored in the netprovision InfluxDB bucket. - Similarly, audit logs will not be stored in the
audit_logs/
folder. They will be timestamped and stored in the netprovision InfluxDB bucket.
These changes will enhance audit logging, allow for more versatile configuration diff-checks, and improve rollback features.
Let's take a look at how we can work with these settings.
The main tool for doing that is netprovisioncli_admin.py.
python netprovisioncli_admin.py -h
usage: netprovisioncli_admin.py [-h] --username USERNAME [--password PASSWORD] [--recipe [RECIPE ...]] [--device DEVICE] [--remove] [--customer CUSTOMER]
Manage MongoDB entries for customers and devices.
options:
-h, --help show this help message and exit
--username USERNAME Username for authentication.
--password PASSWORD Password for authentication.
--recipe [RECIPE ...]
Paths to YAML or JSON files containing the customer recipes (can use globs).
--device DEVICE Path to a YAML or JSON file containing the device details (can specify multiple).
--remove Flag to remove a customer or device.
--customer CUSTOMER Customer name to query, add, or remove.
By using the --recipe
option, you can easily add customers to MongoDB's netprovision database:
python netprovisioncli_admin.py --recipe recipes/customers/LEONARDOFURTADO.json --username lfurtado
Password:
Authentication successful.
Customer LEONARDOFURTADO updated.
You can also remove a customer:
python netprovisioncli_admin.py --customer LEONARDOFURTADO --remove --username lfurtado
Password:
Authentication successful.
Customer LEONARDOFURTADO removed.
Additionally, you can add multiple customers by specifying a path to their YAML or JSON files:
python netprovisioncli_admin.py --recipe recipes/customers/*.json --username lfurtado
Password:
Authentication successful.
Customer LEONARDOFURTADO added.
Customer RUDSONCOSTA updated.
One of the major issues that could arise from this project is the possibility of conflicting or overlapping properties between different customers. For instance, two customers sharing the same IPv4 or IPv6 prefix or LAN subnet. While the configuration generation wouldn't be affected, deployment could fail, potentially impacting existing customers where these overlaps occur.
To prevent this, I've implemented some safety checks to netprovisioncli_admin.py, particularly when importing customer service configuration details from recipe files. These checks include:
- Circuit ID: Two customers cannot share the same circuit ID if they are defined on the same Access device and interface. However, the same circuit ID can be used for a different access interface. If you try to add a new customer in a conflicting way the code will return:
Conflict: Circuit ID 30 on a-cisco-01 GigabitEthernet1 is already in use by customer JOHN.
- (Inner) VLAN ID: Two customers cannot share the same VLAN ID if they are defined on the same Access device and interface. However, the same inner VLAN ID (or "C-VLAN") can be used on the same Access device as long as it's defined on a different access interface. An example of such a conflict check:
Conflict: VLAN ID 30 on a-cisco-01 GigabitEthernet1 is already in use by customer JOHN.
- PW ID: Two customers can't share the same pseudowire ID if they are defined on either the same Access or PE device. However, a PW ID can be reused for a different Access + PE device pair. An example of such a conflict check:
Conflict: Pseudowire ID 30 is already in use by customer JOHN on access device a-cisco-01 or PE device pe-juniper-01.
- IRB IPv4 and IPv6 addresses: This entails not only checking for direct conflict (the exact IPv4 or IPv6 address being used by another client) but also overlapping cases (a customer having a /24 that overlaps with another customer having a /23 or less). For example:
Conflict: IRB IPv4 address 172.16.40.1/30 is already in use by customer JOHN.
Conflict: IRB IPv4 prefix 172.16.40.99/24 overlaps with 172.16.40.1/30 owned by customer JOHN.
- IPv4 and IPv6 customer LAN routes: The same check as the IRB IPv4 and IPv6 cases. For example:
Conflict: IPv4 LAN route 172.17.0.0/16 is already in use by customer JOHN.
Conflict: IPv4 LAN prefix 172.17.0.0/24 overlaps with 172.17.0.0/16 owned by customer JOHN.
That being said, you cannot add new customer service provisioning configuration parameters if conflicts or overlaps occur. Consequently, you won't be able to generate deployment configurations, particularly when the project data source settings are MongoDB, as one issue leads to another.
You can use netprovisioncli_admin.py
to add network devices to MongoDB bucket by running the following operation:
python netprovisioncli_admin.py --device devices/network_devices.yaml --username lfurtado --password Lab
Authentication successful.
Device a-cisco-01 added.
Device a-huawei-02 added.
Device p-cisco-01 added.
Device p-cisco-02 added.
Device pe-juniper-01 added.
Device pe-cisco-03 added.
Device pe-cisco-04 added.
Device pe-huawei-02 added.
The netprovisioncli_query.py
tool enables you to query for users and devices from MongoDB. It supports the longest matching, eliminating the need to type the full customer name. You can combine --customer
and --device
in the same query.
python netprovisioncli_query.py -h
usage: netprovisioncli_query.py [-h] [--customer CUSTOMER] [--device DEVICE]
Query MongoDB for customer or device details.
options:
-h, --help show this help message and exit
--customer CUSTOMER Customer name to query (partial name allowed) or 'all' to list all customers.
--device DEVICE Device name to query (can specify multiple) or 'all' to list all devices.
Here's an example:
python netprovisioncli_query.py --customer LEO
Customer Details:
+------------------------------------------------+--------------------------+
| Field | Details |
+------------------------------------------------+--------------------------+
| _id | 66508bc09b5da0a02ccbd726 |
| name | LEONARDOFURTADO |
| customer_details.devices.access.name | a-cisco-01 |
| customer_details.devices.access.interface | GigabitEthernet1 |
| customer_details.devices.pe.name | pe-juniper-01 |
| customer_details.service_details.circuit_id | 20 |
| customer_details.service_details.qos_input | 500 |
| customer_details.service_details.qos_output | 500 |
| customer_details.service_details.vlan_id | 20 |
| customer_details.service_details.vlan_id_outer | 600 |
| customer_details.service_details.pw_id | 20 |
| customer_details.service_details.irb_ipaddr | 172.16.1.1/30 |
| customer_details.service_details.irb_ipv6addr | 2001:db8:20:20::1/127 |
| customer_details.service_details.ipv4_lan | 172.18.0.0/16 |
| customer_details.service_details.ipv4_nexthop | 172.16.1.2 |
| customer_details.service_details.ipv6_lan | 2001:db8:2222::/48 |
| customer_details.service_details.ipv6_nexthop | 2001:db8:20:20::2 |
| service_type | p2p |
+------------------------------------------------+--------------------------+
Another example:
python netprovisioncli_query.py --device a-cisco-01
Device Details:
+-----------------------+--------------------------------------------------+
| Field | Details |
+-----------------------+--------------------------------------------------+
| _id | 6649fd1a6583362b46f09882 |
| device_name | a-cisco-01 |
| device_type | cisco_xe |
| device_role | access |
| ip_address | 192.168.255.11 |
| loopback | 10.255.255.1 |
| customer_provisioning | True |
| forbidden_interfaces | ['^GigabitEthernet[2-4]$', '^Loopback[0-9999]$'] |
+-----------------------+--------------------------------------------------+
When working with databases, the generation and deployment codes are slightly modified to provide additional data about the deployment. First, you will no longer be able to generate configurations using recipes.
python netprovisioncli_generate.py --recipe recipes/customers/LEONARDOFURTADO.yaml
Error: Device information is missing or incorrect.
Error logged to generated_configs/LEONARDOFURTADO_error.json
Now, your only options are to provide all arguments in-line or, preferably, specify the customer name that already exists in MongoDB:
python netprovisioncli_generate.py --customer-name LEONARDOFURTADO
Using template: p2p_cisco_xe_to_generic_remove.j2 to remove any previous configuration for this customer from the Access device
Using template: p2p_juniper_junos_to_generic_remove.j2 to remove any previous configuration for this customer from the PE device
Using template: p2p_cisco_xe_to_generic.j2 for Access device new configuration
Using template: p2p_juniper_junos_to_generic.j2 for PE device new configuration
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Configuration generation completed successfully.
Configuration generation completed in 0.02 seconds.
Then, proceed with the deployment as you would normally do:
python netprovisioncli_deploy.py --customer LEONARDOFURTADO --access-device a-cisco-01 --pe-device pe-juniper-01 --username lfurtado --password Juniper
Loaded devices from MongoDB: {'a-cisco-01': {'_id': ObjectId('6649fd1a6583362b46f09882'), 'device_name': 'a-cisco-01', 'device_type': 'cisco_xe', 'device_role': 'access', 'ip_address': '192.168.255.11', 'loopback': '10.255.255.1', 'customer_provisioning': True, 'forbidden_interfaces': ['^GigabitEthernet[2-4]$', '^Loopback[0-9999]$']}, 'pe-juniper-01': {'_id': ObjectId('6649fd1a6583362b46f09883'), 'device_name': 'pe-juniper-01', 'device_type': 'juniper_junos', 'device_role': 'pe', 'ip_address': '192.168.255.15', 'loopback': '10.255.255.5', 'customer_provisioning': True}}
Added current deployment ID: 20240524_140206
This customer's most recent (previous) Deployment ID: 20240524_090130
Deployment started by root from IP 192.168.0.108 at 2024-05-24 14:02:06.916005
Added configuration for a-cisco-01: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Added configuration for a-cisco-01: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Added configuration for pe-juniper-01: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Added configuration for pe-juniper-01: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deploying configuration for a-cisco-01 from generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Deploying configuration for a-cisco-01 from generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Written deployed config for a-cisco-01 to InfluxDB.
Deploying configuration for pe-juniper-01 from generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Deploying configuration for pe-juniper-01 from generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Written deployed config for pe-juniper-01 to InfluxDB.
Deployment ID for audit log: 20240524_140206
To check the audit log entries for this deployment from InfluxDB, use: python netprovisioncli_commitdb.py --deployment-id 20240524_140206
To compare (diff check) the deployed configuration with the previously deployed one, use: python netprovisioncli_commitdb.py --diff-check 20240524_140206 20240524_090130
Deployment completed successfully. Detailed audit log saved in InfluxDB.
Deleted generated config file: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
The additional information provided by the deployment code when working with databases includes:
- Added current deployment ID: It will print the current deployment ID.
- This customer's most recent (previous) Deployment ID: It will print the previous deployment ID.
- To check the audit log entries for this deployment from InfluxDB, use: This will provide the full path to the audit log for the deployment. Please note that these logs are stored in InfluxDB, not MongoDB.
- To compare (diff check) the deployed configuration with the previously deployed one, use: This will provide the complete path for the difference check between the newly deployed configuration and the previous one for that customer.
Here's how you can utilize another tool, netprovisioncli_commitdb.py, to visualize this data:
python netprovisioncli_commitdb.py -h
usage: netprovisioncli_commitdb.py [-h] [--customer CUSTOMER] [--deployment-id DEPLOYMENT_ID] [--deployment-list] [--diff-check DEPLOYMENT_ID1 DEPLOYMENT_ID2] [--timeframe TIMEFRAME] [--last LAST]
Query InfluxDB for audit log entries.
options:
-h, --help show this help message and exit
--customer CUSTOMER Filter deployments by customer name.
--deployment-id DEPLOYMENT_ID
Deployment ID to query the audit log entry.
--deployment-list List the last 500 deployments.
--diff-check DEPLOYMENT_ID1 DEPLOYMENT_ID2
Display the differences between two deployment IDs.
--timeframe TIMEFRAME
Filter deployments by date (YYYY-MM-DD).
--last LAST List the last N deployments.
Check the list of the last 500 deployments stored in InfluxDB:
python netprovisioncli_commitdb.py --deployment-list
Deployment ID Timestamp Customer Name Operator
==========================================================================================
20240523_200513 2024-05-23 20:05:50 LEONARDOFURTADO root
20240523_200635 2024-05-23 20:07:13 LEONARDOFURTADO root
20240523_200802 2024-05-23 20:08:40 LEONARDOFURTADO root
20240523_202344 2024-05-23 20:24:22 LEONARDOFURTADO root
20240523_202520 2024-05-23 20:25:57 LEONARDOFURTADO root
20240523_202625 2024-05-23 20:27:02 LEONARDOFURTADO root
20240524_090130 2024-05-24 09:02:08 LEONARDOFURTADO root
20240524_140206 2024-05-24 14:02:44 LEONARDOFURTADO root
Check for a specific deployment ID:
python netprovisioncli_commitdb.py --deployment-id 20240524_140206
{
"operator": "root",
"operator_ip": "192.168.0.108",
"is_deactivate": false,
"device_name": "pe-juniper-01",
"device_type": "juniper_junos",
"configuration_type": "pe",
"configuration_path": "generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt",
"deployed_config_path": "\nset routing-instances LEONARDOFURTADO-20 instance-type vpls\nset routing-instances LEONARDOFURTADO-20 vlan-tags inner 20\nset routing-instances LEONARDOFURTADO-20 vlan-tags outer 600\nset routing-instances LEONARDOFURTADO-20 routing-interface irb.20\nset routing-instances LEONARDOFURTADO-20 protocols vpls encapsulation-type ethernet\nset routing-instances LEONARDOFURTADO-20 protocols vpls control-word\nset routing-instances LEONARDOFURTADO-20 protocols vpls no-tunnel-services\nset routing-instances LEONARDOFURTADO-20 protocols vpls vpls-id 20\nset routing-instances LEONARDOFURTADO-20 protocols vpls mtu 1500\nset routing-instances LEONARDOFURTADO-20 protocols vpls neighbor 10.255.255.1\nset routing-instances LEONARDOFURTADO-20 protocols vpls connectivity-type irb\nset interfaces irb unit 20 description LEONARDOFURTADO-20\nset interfaces irb unit 20 family inet address 172.16.1.1/30\nset interfaces irb unit 20 family inet rpf-check\nset interfaces irb unit 20 family inet sampling input\nset interfaces irb unit 20 family inet6 address 2001:db8:20:20::1/127\nset interfaces irb unit 20 family inet6 rpf-check\nset interfaces irb unit 20 family inet6 sampling input\nset routing-options static route 172.18.0.0/16 next-hop 172.16.1.2\nset routing-options static route 172.18.0.0/16 community 65000:10000\nset routing-options static route 172.18.0.0/16 community no-export\nset routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:20:20::2\nset routing-options rib inet6.0 static route 2001:db8:2222::/48 community 65000:11000\nset routing-options rib inet6.0 static route 2001:db8:2222::/48 community no-export\ncommit and-quit\n",
"diff_results": "--- old_config\n+++ new_config\n@@ -13,13 +13,13 @@\n set interfaces irb unit 20 family inet address 172.16.1.1/30\n set interfaces irb unit 20 family inet rpf-check\n set interfaces irb unit 20 family inet sampling input\n-set interfaces irb unit 20 family inet6 address 2001:db8:1234:1234::1/127\n+set interfaces irb unit 20 family inet6 address 2001:db8:20:20::1/127\n set interfaces irb unit 20 family inet6 rpf-check\n set interfaces irb unit 20 family inet6 sampling input\n-set routing-options static route 172.17.0.0/16 next-hop 172.16.1.2\n-set routing-options static route 172.17.0.0/16 community 65000:10000\n-set routing-options static route 172.17.0.0/16 community no-export\n-set routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:1234:1234::2\n+set routing-options static route 172.18.0.0/16 next-hop 172.16.1.2\n+set routing-options static route 172.18.0.0/16 community 65000:10000\n+set routing-options static route 172.18.0.0/16 community no-export\n+set routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:20:20::2\n set routing-options rib inet6.0 static route 2001:db8:2222::/48 community 65000:11000\n set routing-options rib inet6.0 static route 2001:db8:2222::/48 community no-export\n commit and-quit",
"timestamp": "2024-05-24 14:02:44",
"elapsed_time": "Deployment completed in 37.70 seconds"
}
Perform a diff check between the newly deployed config and the previous one:
python netprovisioncli_commitdb.py --diff-check 20240524_140206 20240524_090130
--- 20240524_140206
+++ 20240524_090130
@@ -14,13 +14,13 @@
set interfaces irb unit 20 family inet address 172.16.1.1/30
set interfaces irb unit 20 family inet rpf-check
set interfaces irb unit 20 family inet sampling input
-set interfaces irb unit 20 family inet6 address 2001:db8:20:20::1/127
+set interfaces irb unit 20 family inet6 address 2001:db8:1234:1234::1/127
set interfaces irb unit 20 family inet6 rpf-check
set interfaces irb unit 20 family inet6 sampling input
-set routing-options static route 172.18.0.0/16 next-hop 172.16.1.2
-set routing-options static route 172.18.0.0/16 community 65000:10000
-set routing-options static route 172.18.0.0/16 community no-export
-set routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:20:20::2
+set routing-options static route 172.17.0.0/16 next-hop 172.16.1.2
+set routing-options static route 172.17.0.0/16 community 65000:10000
+set routing-options static route 172.17.0.0/16 community no-export
+set routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:1234:1234::2
set routing-options rib inet6.0 static route 2001:db8:2222::/48 community 65000:11000
set routing-options rib inet6.0 static route 2001:db8:2222::/48 community no-export
commit and-quit
Check all deployments related to a specific customer:
python netprovisioncli_commitdb.py --customer LEONARDOFURTADO
Deployment ID Timestamp Customer Name Operator
==========================================================================================
20240523_200513 2024-05-23 20:05:50 LEONARDOFURTADO root
20240523_200635 2024-05-23 20:07:13 LEONARDOFURTADO root
20240523_200802 2024-05-23 20:08:40 LEONARDOFURTADO root
20240523_202344 2024-05-23 20:24:22 LEONARDOFURTADO root
20240523_202520 2024-05-23 20:25:57 LEONARDOFURTADO root
20240523_202625 2024-05-23 20:27:02 LEONARDOFURTADO root
20240524_090130 2024-05-24 09:02:08 LEONARDOFURTADO root
20240524_140206 2024-05-24 14:02:44 LEONARDOFURTADO root
20240524_141302 2024-05-24 14:13:40 LEONARDOFURTADO root
Refining the filter to match deployments for a specific customer within a certain timeframe:
python netprovisioncli_commitdb.py --customer LEONARDOFURTADO --timeframe 2024-05-24
Deployment ID Timestamp Customer Name Operator
==========================================================================================
20240524_090130 2024-05-24 09:02:08 LEONARDOFURTADO root
20240524_140206 2024-05-24 14:02:44 LEONARDOFURTADO root
20240524_141302 2024-05-24 14:13:40 LEONARDOFURTADO root
Check the last 'n' deployments. You can combine it with other flags, too:
netprovisioncli_commitdb.py --last 2
Deployment ID Timestamp Customer Name Operator
==========================================================================================
20240523_200513 2024-05-23 20:05:50 LEONARDOFURTADO root
20240523_200635 2024-05-23 20:07:13 LEONARDOFURTADO root
If you need to delete all entries from the netprovision bucket in InfluxDB for any reason, you can use this script. It will prompt you for your password twice. Please note that you must confirm the deletion by entering “YES”, as this action is irreversible. You don't need to worry about deleting device or customer service activation data, as they aren't stored in InfluxDB but in MongoDB. Still, make sure you know what you're doing if you decide to reset your netprovision InfluxDB bucket! Here's how it works:
python reset_influxdb.py --username lfurtado --password Lab
******************************************************
* WARNING: This operation will delete the entire *
* InfluxDB 'netprovision' bucket and recreate it. *
* This action is irreversible. *
******************************************************
Type 'YES' to confirm: YES
Please type your password again to confirm:
Bucket 'netprovision' has been reset.
If you proceed, you will lose:
- All audit log entries
- All deployment IDs, including their details and configurations deployed.
This section discusses three situations involving three different tools. Firstly, you can export data related to customer services, devices, and deployment to an Excel spreadsheet. This data will be saved in the reports/
folder.
python netprovisioncli_export.py
usage: netprovisioncli_export.py [-h] [--export]
Export customer, device, and deployment data to an XLS file. It requires MongoDB and InfluxDB settings in the settings.yaml file.
options:
-h, --help show this help message and exit
--export Export data to XLS file.
python netprovisioncli_export.py --export
Retrieved devices: [{'_id': ObjectId('6649fd1a6583362b46f09882'), 'device_name': 'a-cisco-01', 'device_type': 'cisco_xe', 'device_role': 'access', 'ip_address': '192.168.255.11', 'loopback': '10.255.255.1', 'customer_provisioning': True, 'forbidden_interfaces': ['^GigabitEthernet[2-4]$', '^Loopback[0-9999]$']}, {'_id': ObjectId('6649fd1a6583362b46f09883'), 'device_name': 'pe-juniper-01', 'device_type': 'juniper_junos', 'device_role': 'pe', 'ip_address': '192.168.255.15', 'loopback': '10.255.255.5', 'customer_provisioning': True}]
Data exported successfully to reports/netprovision_report.xlsx
The second tool enables you to backup the entire MongoDB netprovision database, as well as the InfluxDB netprovision bucket.
python netprovisioncli_backup.py -h
usage: netprovisioncli_backup.py [-h] [--export-mongo] [--export-influx] [--mongo-output MONGO_OUTPUT] [--influx-output INFLUX_OUTPUT]
Export MongoDB and InfluxDB data to JSON files. It requires MongoDB and InfluxDB settings in the settings.yaml file.
options:
-h, --help show this help message and exit
--export-mongo Export MongoDB data to JSON file.
--export-influx Export InfluxDB data to JSON file.
--mongo-output MONGO_OUTPUT
Output file for MongoDB data.
--influx-output INFLUX_OUTPUT
Output file for InfluxDB data.
python netprovisioncli_backup.py --export-influx --export-mongo
MongoDB data exported to export_data/mongodb_export.json
InfluxDB data exported to export_data/influxdb_export.json
Now, hypothetically speaking, consider a scenario where I intentionally deleted both the database and the bucket, or needed to reinstall NetProvisionCLI on another host, but wanted to restore all previous customer, device, and deployment data. It might look something like this:
influx delete --bucket netprovision \
--start 2024-05-01T00:00:00Z \
--stop 2024-12-31T00:00:00Z
mongo
MongoDB shell version v4.4.29
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("5b7b1e56-f812-4a9e-941c-bef8b298c88e") }
MongoDB server version: 4.4.29
---
> use netprovision
switched to db netprovision
>
> db.dropDatabase()
{ "dropped" : "netprovision", "ok" : 1 }
> quit()
To restore the data, simply use the netprovisioncli_restore.py script:
python netprovisioncli_restore.py --import-mongo --mongo-input export_data/mongodb_export.json
WARNING: This operation will overwrite existing data in MongoDB and InfluxDB.
Some data may be lost in the process as it will be a full restore.
Username: lfurtado
Password:
Are you sure you want to proceed with the restore? Type YES to confirm: YES
MongoDB data imported from export_data/mongodb_export.json
python netprovisioncli_restore.py --import-influx --influx-input export_data/influxdb_export.json
WARNING: This operation will overwrite existing data in MongoDB and InfluxDB.
Some data may be lost in the process as it will be a full restore.
Username: lfurtado
Password:
Are you sure you want to proceed with the restore? Type YES to confirm: YES
InfluxDB data imported from export_data/influxdb_export.json
An additional utility, netprovisioncli_deviceconfig.py, was added to enhance this project's features. It allows you to backup and visualize network device configurations stored in MongoDB's collection. Here's how it works. Bear in mind that while this utility can support the project when its data source is set to ‘yaml’, it is better suited for a 'mongodb' data source.
Use the --backup flag to back up the running configuration of a single device or all devices. The configuration for that device will be stored in MongoDB.
python netprovisioncli_deviceconfig.py --backup all --username USERNAME
You can visualize what backups you have in the database by using the following command:
python netprovisioncli_deviceconfig.py --audit-history
Diff-checks are also supported by specifying two different backup points:
python netprovisioncli_deviceconfig.py --diff-check a-cisco-01 2024-06-02_13:28:14 a-cisco-01 2024-06-17_15:27:10
You may also want to see the configuration of a device as kept in the MongoDB database:
python netprovisioncli_deviceconfig.py --display pe-juniper-01
To simplify work on this project, I've developed a shell feature named netprovisioncli_shell.py
. This feature imitates what network engineers would typically expect when interacting with network devices.
When you run it, you can use help -v
to view all the command options available in the current mode:
NetProvisionCLI> help -v
Documented commands (use 'help -v' for verbose/'help <topic>' for details):
======================================================================================================
admin Enter admin mode
alias Manage aliases
customer Enter customer mode
edit Run a text editor and optionally open a file with it
exit Exit the shell.
help List available commands or provide detailed help for a specific command
history View, run, edit, save, or clear previously entered commands
macro Manage macros
man Show detailed instructions and examples.
quit Exit this application
run_pyscript Run a Python script file inside the console
run_script Run commands in script file that is encoded as either ASCII or UTF-8 text
set Set a settable parameter or show current settings of parameters
shell Execute a command as if at the OS prompt
shortcuts List available shortcuts
There are two modes: admin and customer. Daily tasks related to customer service can be accomplished using operations such as generate, deploy, commitdb, query, and export in customer mode. Using help-v
will display all available options in this mode and using man
will provide you with examples and usage cases:
NetProvisionCLI/customer> help
Customer Service Provisioning commands:
commitdb - Query deployed configs and audit logs from the database. Usage: commitdb -h
customer - Add or modify customers to the service database. Usage: customer --recipe RECIPEFILE --username USERNAME
deploy - Deploy configuration. Usage: deploy --customer-name CUSTOMER_NAME --access-device ACCESS_DEVICE --pe-device PE_DEVICE --username USERNAME
export - Export customer, device, and deployment data to an XLS (Excel) file. Usage: export --export
generate - Generate configuration. Usage: generate --customer-name CUSTOMER_NAME
query - Query the database for customer or device details. Usage: query -h
General operations:
_relative_run_script - Run commands in script file that is encoded as either ASCII or UTF-8 text
alias - Manage aliases
edit - Run a text editor and optionally open a file with it
eof - Called when Ctrl-D is pressed
exit - Exit to the main shell.
help - Customized help command
history - View, run, edit, save, or clear previously entered commands
ipy - No description available.
macro - Manage macros
man - Show detailed instructions and examples.
py - No description available.
quit - Exit this application
run_pyscript - Run a Python script file inside the console
run_script - Run commands in script file that is encoded as either ASCII or UTF-8 text
set - Set a settable parameter or show current settings of parameters
shell - Execute a command as if at the OS prompt
shortcuts - List available shortcuts
Every other feature and the functionality of each part of the code remain the same, whether you choose to issue Python commands separately or use this shell feature. The choice is yours. To generate, deploy, and visualize a deployment, you could experiment with this:
Add/modify a customer to the database:
NetProvisionCLI> customer
NetProvisionCLI/customer> addcustomer --recipe recipes/customer/LEONARDOFURTADO.json --username lfurtado
Password:
Authentication successful.
Generate the service config:
NetProvisionCLI/customer> generate --customer-name LEONARDOFURTADO
Using template: p2p_cisco_xe_to_generic_remove.j2 to remove any previous configuration for this customer from the Access device
Using template: p2p_juniper_junos_to_generic_remove.j2 to remove any previous configuration for this customer from the PE device
Using template: p2p_cisco_xe_to_generic.j2 for Access device new configuration
Using template: p2p_juniper_junos_to_generic.j2 for PE device new configuration
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Configuration written to /root/Network-Automation/NetProvisionCLI/generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Configuration generation completed successfully.
Configuration generation completed in 0.02 seconds.
Deploy the service config:
NetProvisionCLI/customer> deploy --customer-name LEONARDOFURTADO --access-device a-cisco-01 --pe-device pe-juniper-01 --username lfurtado
Password:
Loaded devices from MongoDB: {'a-cisco-01': {'_id': ObjectId('665c587557fe951112b48cd4'), 'device_name': 'a-cisco-01', 'customer_provisioning': True, 'device_role': 'access', 'device_type': 'cisco_xe', 'forbidden_interfaces': ['^GigabitEthernet[2-4]$', '^Loopback[0-9999]$'], 'ip_address': '192.168.255.11', 'loopback': '10.255.255.1'}, 'pe-juniper-01': {'_id': ObjectId('665c587557fe951112b48cdc'), 'device_name': 'pe-juniper-01', 'customer_provisioning': True, 'device_role': 'pe', 'device_type': 'juniper_junos', 'ip_address': '192.168.255.15', 'loopback': '10.255.255.5'}}
Added current deployment ID: 20240617_113336
This customer's most recent (previous) Deployment ID: 20240617_111034
Deployment started by root from IP 192.168.0.108 at 2024-06-17 11:33:36.103912
Added configuration for a-cisco-01: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Added configuration for a-cisco-01: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Added configuration for pe-juniper-01: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Added configuration for pe-juniper-01: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deploying configuration for a-cisco-01 from generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
Deploying configuration for a-cisco-01 from generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Written deployed config for a-cisco-01 to InfluxDB.
Deploying configuration for pe-juniper-01 from generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Deploying configuration for pe-juniper-01 from generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Written deployed config for pe-juniper-01 to InfluxDB.
=== Cleanup of temporary generated configs ===
Deleted generated config file: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config_remove.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_pe-juniper-01_pe_config.txt
Deleted generated config file: generated_configs/LEONARDOFURTADO_a-cisco-01_access_config_remove.txt
=== End of cleanup ===
Deployment issued by user root for customer LEONARDOFURTADO completed successfully in 38.06 seconds.
Detailed audit log saved in InfluxDB under deployment ID 20240617_113336.
To check the audit log entries for this deployment, use: python netprovisioncli_commitdb.py --deployment-id 20240617_113336
To compare the new deployment with the previous configurations, use: python netprovisioncli_commitdb.py --diff-check 20240617_113336 20240617_111034
Query customer details:
NetProvisionCLI/customer> query --customer LEONARDOFURTADO
Customer Details:
+------------------------------------------------+---------------------------+
| Field | Details |
+------------------------------------------------+---------------------------+
| _id | 665c588d57fe951112b48cee |
| name | LEONARDOFURTADO |
| customer_details.devices.access.name | a-cisco-01 |
| customer_details.devices.access.interface | GigabitEthernet1 |
| customer_details.devices.pe.name | pe-juniper-01 |
| customer_details.service_details.circuit_id | 20 |
| customer_details.service_details.qos_input | 500 |
| customer_details.service_details.qos_output | 500 |
| customer_details.service_details.vlan_id | 20 |
| customer_details.service_details.vlan_id_outer | 600 |
| customer_details.service_details.pw_id | 20 |
| customer_details.service_details.irb_ipaddr | 172.16.1.1/30 |
| customer_details.service_details.irb_ipv6addr | 2001:db8:1234:1234::1/127 |
| customer_details.service_details.ipv4_lan | 172.17.0.0/16 |
| customer_details.service_details.ipv4_nexthop | 172.16.1.2 |
| customer_details.service_details.ipv6_lan | 2001:db8:2222::/48 |
| customer_details.service_details.ipv6_nexthop | 2001:db8:1234:1234::2 |
| service_type | p2p |
+------------------------------------------------+---------------------------+
Query the commit database (deployed configs):
NetProvisionCLI/customer> commitdb --deployment-list
Deployment ID Timestamp Customer Name Operator
==========================================================================================
20240612_144508 2024-06-12 14:45:48 LEONARDOFURTADO root
20240612_144728 2024-06-12 14:48:06 LEONARDOFURTADO root
20240612_145124 2024-06-12 14:52:02 LEONARDOFURTADO root
20240612_145420 2024-06-12 14:54:58 LEONARDOFURTADO root
20240617_100922 2024-06-17 10:10:02 LEONARDOFURTADO root
20240617_103350 2024-06-17 10:34:27 LEONARDOFURTADO root
20240617_111034 2024-06-17 11:11:12 LEONARDOFURTADO root
20240617_113336 2024-06-17 11:34:14 LEONARDOFURTADO root
Performing a diff check between a new config and a previous one:
NetProvisionCLI/customer> shell python netprovisioncli_commitdb.py --diff-check 20240617_113336 20240617_111034
--- 20240617_113336
+++ 20240617_111034
@@ -14,13 +14,13 @@
set interfaces irb unit 20 family inet address 172.16.1.1/30
set interfaces irb unit 20 family inet rpf-check
set interfaces irb unit 20 family inet sampling input
-set interfaces irb unit 20 family inet6 address 2001:db8:20:20::1/127
+set interfaces irb unit 20 family inet6 address 2001:db8:1234:1234::1/127
set interfaces irb unit 20 family inet6 rpf-check
set interfaces irb unit 20 family inet6 sampling input
-set routing-options static route 172.18.0.0/16 next-hop 172.16.1.2
-set routing-options static route 172.18.0.0/16 community 65000:10000
-set routing-options static route 172.18.0.0/16 community no-export
-set routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:20:20::2
+set routing-options static route 172.17.0.0/16 next-hop 172.16.1.2
+set routing-options static route 172.17.0.0/16 community 65000:10000
+set routing-options static route 172.17.0.0/16 community no-export
+set routing-options rib inet6.0 static route 2001:db8:2222::/48 next-hop 2001:db8:1234:1234::2
set routing-options rib inet6.0 static route 2001:db8:2222::/48 community 65000:11000
set routing-options rib inet6.0 static route 2001:db8:2222::/48 community no-export
commit and-quit
There are many supported operations. Use 'help -v' to access them.
Follow these instructions for a quick installation of MongoDB on a Linux Ubuntu host. I strongly advise you to check the official Install MongoDB page. For an easy step-by-step installation on Ubuntu, please check this official page.
Ensure that your MongoDB installation is secure by implementing strong authentication and other hardening measures. For guidelines, refer to MongoDB's official Security Checklist.
Next, ensure your NetProvisionCLI's settings/settings.yaml
file is configured properly:
mongodb_connection:
database_name: netprovision
uri: mongodb://localhost:27017/
Then, to experiment with MongoDB for our NetProvisionCLI project:
-
mongo
ormongo mongodb://localhost:27017/netprovision
: To connect to your MongoDB instance. -
use netprovision
: To switch to the netprovision database. -
show collections
: List all collections in the netprovision database to confirm that they exist. -
db.customers.stats()
: Get detailed statistics about thecustomers
collection, such as the number of documents, the size of the collection, and storage information. -
db.devices.find().pretty()
: To view the documents within thedevices
collection, you can use thefind
method. -
db.customers.find().pretty()
: To view the documents within thecustomers
collection, you can use thefind
method. -
db.customers.find({ name: "customer_name" }).pretty()
: If you want to query specific information or filter the documents, you can pass a query object to thefind
method. -
db.customers.getIndexes()
: To see the indexes on the netprovision collection. -
db.dropDatabase()
: It will drop the database! BE CAREFUL! -
quit()
: To quit MongoDB CLI.
Follow these instructions for a quick installation of InfluxDB on a Linux Ubuntu host.
wget -qO- https://repos.influxdata.com/influxdb.key | sudo apt-key add -
echo "deb https://repos.influxdata.com/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/influxdb.list
sudo apt-get update
sudo apt-get install influxdb2
sudo systemctl enable influxdb
sudo systemctl start influxdb
sudo systemctl status influxdb
Once you install and configure InfluxDB, ensure you edit your settings/settings.yaml
file to inform your org, token, and URL, as demonstrated below.
data_source: yaml
influxdb:
bucket: netprovision
org: YOUR_ORG_HERE
token: YOUR_TOKEN_HERE
url: http://localhost:8086
mongodb_connection:
database_name: netprovision
uri: mongodb://localhost:27017/
The influx command line interface (CLI) includes commands to manage many aspects of InfluxDB, including buckets, organizations, users, tasks, etc. You can also use the web GUI to manage many of its aspects. Let's take a look at the CLI.
To work with the CLI you'll have to download it first. Please follow the instructions here: https://docs.influxdata.com/influxdb/v2/reference/cli/influx/
Then, to use it:
export INFLUX_TOKEN=YOUR_TOKEN_HERE
export INFLUX_ORG="YOUR_ORG_HERE"
export INFLUX_BUCKET=netprovision
export INFLUX_HOST=http://localhost:8086
Listing your buckets:
influx bucket list
ID Name Retention Shard group duration Organization ID Schema Type
7a980ddf7f5fd22b _monitoring 168h0m0s 24h0m0s 3cd2c8329f67697a implicit
7724bac7304830fb _tasks 72h0m0s 24h0m0s 3cd2c8329f67697a implicit
75bb09cd24cb2633 netprovision 8760h0m0s 24h0m0s 3cd2c8329f67697a implicit
Useful operations:
-
influx query 'from(bucket:"netprovision") |> range(start: -1h)'
: Get data from the last hour. -
influx query 'from(bucket:"netprovision") |> range(start: -24h)'
: Get data from the last 24 hours. -
influx query 'import "influxdata/influxdb/schema" schema.measurements(bucket: "netprovision")'
: List all measurements in the bucket.
You can use several sample files to experiment with NetProvisionCLI.
-
sample_populate_mongodb.py
: If you plan to experiment with NetProvisionCLI using the same lab setup as utilized in these demonstrations, this code will help you populate data on MongoDB swiftly. -
recipes/
: The recipes folder contains samples of how to generate and customize your customer's service activation parameters. -
templates/
: The templates folder houses Jinja2 templates, which you can tailor to your requirements. Please note, though, you must adhere to the existing tags for variables as they are currently hardcoded in the configuration rendering and generation logic code. Plans to abstract this and make your job way easier are underway for the future.
The network topology in this document is used for demonstration purposes. Please note the actual nature of the service activation intent. The project's aim is to provide Internet access to corporate subscribers via a L2VPN over MPLS in an IRB or Routed-PW manner. The project will continue to evolve with the addition of various service offerings, and this wiki will be updated accordingly to reflect these new features.