Core collection#
The Core collection provides modules for the EDA's core API, such as user management, transaction handling, alarm monitoring, query execution, and more.
Transactions#
Carrying out atomic transactions across the whole infrastructure is one of the key features Nokia EDA offers.
While individual Ansible collections contain modules to perform one-shot operations on resources, the Transaction API allows for more complex workflows that involve combining multiple resource operations into a single atomic transaction, running dry runs, inspecting diffs and rolling back changes if needed.
The nokia.eda_core_v1.transaction.v2.transaction
module runs the transactions and has the following structure:
- name: Run a transaction
nokia.eda_core_v1.transaction.v2.transaction:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
description: "interface deletion"
dryRun: false #(1)!
resultType: "normal" #(2)!
retain: true #(3)!
crs:
- type:
create:
value: # resource definition
- type:
modify:
value: # resource definition
- type:
replace:
value: # resource definition
- type:
patch:
patchOps: # patch operations
target:
gvk:
group:
version:
kind:
name:
namespace:
- type:
delete:
gvk:
group:
version:
kind:
name:
namespace:
dryRun
: If set to true, the dry run will be performed by EDA and no changes will be made to the actual resource/nodes.resultType
: Defines the type of result to be returned by the transaction. Set to "normal".retain
: When set to true, the transaction results will be retained in memory of the CE pod.
In its crs
field, a user provides a list of resources to include in the transaction and associated, easy to understand operation:
- create - creates a resource. Fails if it already exists.
- modify - modifies an existing resource. Fails if it does not exist.
- replace - replaces the existing resource, creates if it does not exist.
- patch (JSON patch RFC 6902)
- delete - deletes a resource. Fails if it does not exist.
Using these operations a user can arbitrary number of resources and operations with them to a single transaction and run them together in an atomic, all-or-nothing, fashion.
Create#
Let's explore how to use transaction module for creating resources. We will create two Banner resources in a single transaction. First Banner will target leaf nodes and second Banner will target spine nodes.
The transaction module expects us to provide the resources definitions in the crs
field:
- name: Create two banners
nokia.eda_core_v1.transaction.v2.transaction:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
description: "interface deletion"
dryRun: false
resultType: "normal"
retain: true
crs:
- type:
create:
value: # first Banner resource
- type:
create:
value: # second Banner resource
You should use the resource module from the application collection to register the value of a resource in a variable. Using the Siteinfo collection and its banner
resource we can create the two Banner resources:
Note, both of our resources don't have
state: present/query/absent
defined, which means the resource will be just created in memory.
Let's break down the tx-create-banners.yaml
playbook into its individual tasks. We start first with registering two banner resources in their distinct variables, without specifying the state
attribute:
- name: Create Two Banners
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Create banner for leafs
nokia.eda_siteinfo_v1alpha1.banner:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
resource:
metadata:
labels:
some-label: ansible-demo
namespace: eda
name: demo-leaf-banner
spec:
loginBanner: "This is a demo banner provisioned by Ansible on leafs"
nodeSelector:
- eda.nokia.com/role=leaf
# the `state` attribute should not be present
# to register the resource value as it is defined in a variable banner_leafs
# state: present
register: banner_leafs
- name: Create banner for spines
nokia.eda_siteinfo_v1alpha1.banner:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
resource:
metadata:
labels:
some-label: ansible-demo
namespace: eda
name: demo-spine-banner
spec:
loginBanner: "This is a demo banner provisioned by Ansible on leafs"
nodeSelector:
- eda.nokia.com/role=spine
register: banner_spines
Next we use the transaction module and add the registered objects with the corresponding create
type:
- name: Run transaction
nokia.eda_core_v1.transaction.v2.transaction:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
description: "Banners creation"
dryRun: false
resultType: "normal"
retain: true
crs:
- type:
create:
value: "{{ banner_leafs.result }}"
- type:
create:
value: "{{ banner_spines.result }}"
register: tx
- name: print response
ansible.builtin.debug:
var: tx
When the transaction API is used, the EDA API server immediately responds to the transaction run request with the transaction ID and starts to work on the transaction in the background, allowing for asynchronous processing of the requested changes.
Now we want to check the transaction status and do that only when the transaction is complete. To achieve this we will use the nokia.eda_core_v1.transaction.v2.result.execution
module, that returns execution results of the transaction and can be instructed to wait for the transaction to complete.
- name: Get TX execution result
nokia.eda_core_v1.transaction.v2.result.execution:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx.result.id }}"
waitForComplete: true
failOnErrors: true
register: result_execution
- name: print response
ansible.builtin.debug:
var: result_execution
result_execution:
changed: false
failed: false
result:
changedCrs:
- gvk:
group: core.eda.nokia.com
kind: NodeConfig
version: v1
names:
- banner-demo-leaf-banner-leaf1
- banner-demo-leaf-banner-leaf2
- banner-demo-spine-banner-spine1
namespace: eda
- gvk:
group: siteinfo.eda.nokia.com
kind: Banner
version: v1alpha1
names:
- demo-leaf-banner
- demo-spine-banner
namespace: eda
- gvk:
group: siteinfo.eda.nokia.com
kind: BannerState
version: v1alpha1
names:
- demo-leaf-banner
- demo-spine-banner
namespace: eda
executionSummary:
"intents-run: 2, nodes-changed: 3, engine-time=22.387839ms,
push-to-node=290.785763ms, publish-cr=14.968423ms, git-save=682.16071ms"
intentsRun:
- errors: []
intentName:
gvk:
group: siteinfo.eda.nokia.com
kind: Banner
version: v1alpha1
name: demo-leaf-banner
namespace: eda
outputCrs:
- gvk:
group: core.eda.nokia.com
kind: NodeConfig
version: v1
name: banner-demo-leaf-banner-leaf1
namespace: eda
- gvk:
group: core.eda.nokia.com
kind: NodeConfig
version: v1
name: banner-demo-leaf-banner-leaf2
namespace: eda
- gvk:
group: siteinfo.eda.nokia.com
kind: BannerState
version: v1alpha1
name: demo-leaf-banner
namespace: eda
script:
executionTime: 21842
- errors: []
intentName:
gvk:
group: siteinfo.eda.nokia.com
kind: Banner
version: v1alpha1
name: demo-spine-banner
namespace: eda
outputCrs:
- gvk:
group: core.eda.nokia.com
kind: NodeConfig
version: v1
name: banner-demo-spine-banner-spine1
namespace: eda
- gvk:
group: siteinfo.eda.nokia.com
kind: BannerState
version: v1alpha1
name: demo-spine-banner
namespace: eda
script:
executionTime: 20573
nodesWithConfigChanges:
- name: leaf1
namespace: eda
- name: leaf2
namespace: eda
- name: spine1
namespace: eda
topologySupported: true
status: 200
The transaction execution results will provide you with lots of information about the transaction such as the intent scripts that were run, the changed and output resources and the nodes that were affected by the transaction.
You can also get the transaction summary results by using the following module:
- name: Get TX summary
nokia.eda_core_v1.transaction.v2.result.summary:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx.result.id }}"
register: result_summary
- name: print response
ansible.builtin.debug:
var: result_summary
The transaction summary provides you with the details pertaining to the transaction and its status. Including the commit hash it was recorded with, completion status, etc.
Dry run#
Being able to transact many resources in an atomic and idempotent way is great, but being able to dry run the transaction gives the users confidence in the changes being made and adds this sweet reliability to Nokia EDA automation platform.
To demonstrate how dry runs work we will stage a change of removing the demo-leaf-banner
resource created in the previous step, but instead of executing the transaction as usual, we will switch the dryRun
flag to true
.
When deleting resources via transaction, all you need to provide is the resource identification:
- the
group
(API group) the resource belongs to. - the
version
of the API this resource is using. - the
kind
- the resource kind as seen in the UI - the
name
of the resource to delete - the
namespace
the resource is in
The first three values can be easily obtained from the UI or using the app metadata modules.
Here is how the whole playbook would look like:
- name: Dry run Banner deletion
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Run transaction
nokia.eda_core_v1.transaction.v2.transaction:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
description: "Banners creation"
dryRun: true
resultType: "normal"
retain: true
crs:
- type:
delete:
gvk:
group: siteinfo.eda.nokia.com
version: v1alpha1
kind: Banner
name: demo-leaf-banner
namespace: eda
register: tx
- name: print response
ansible.builtin.debug:
var: tx
- name: Get TX execution result
nokia.eda_core_v1.transaction.v2.result.execution:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx.result.id }}"
waitForComplete: true
failOnErrors: true
register: result_execution
- name: print response
ansible.builtin.debug:
var: result_execution
- name: Get TX summary
nokia.eda_core_v1.transaction.v2.result.summary:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx.result.id }}"
register: result_summary
- name: print response
ansible.builtin.debug:
var: result_summary
result_execution:
changed: false
failed: false
result:
changedCrs:
- gvk:
group: core.eda.nokia.com
kind: NodeConfig
version: v1
names:
- banner-demo-leaf-banner-leaf1
- banner-demo-leaf-banner-leaf2
namespace: eda
- gvk:
group: siteinfo.eda.nokia.com
kind: Banner
version: v1alpha1
names:
- demo-leaf-banner
namespace: eda
- gvk:
group: siteinfo.eda.nokia.com
kind: BannerState
version: v1alpha1
names:
- demo-leaf-banner
namespace: eda
executionSummary: "intents-run: 1, nodes-changed: 2, engine-time=551.762µs,
push-to-node=14.527371ms"
intentsRun:
- errors: []
intentName:
gvk:
group: siteinfo.eda.nokia.com
kind: Banner
version: v1alpha1
name: demo-leaf-banner
namespace: eda
outputCrs: []
script:
executionTime: 9
nodesWithConfigChanges:
- name: leaf1
namespace: eda
- name: leaf2
namespace: eda
topologySupported: true
status: 200
By running a transaction in dry run mode you get the visibility into the following questions every operator seeks answers to:
- Are your changes valid?
- Will the transaction succeed if you run it?
- What nodes will be affected by the change?
- What diffs will be produced by the change from the nodes perspective?
The first two questions are answered by the fact that the transaction returns no errors and the result summary output says success: true
. This gives operators confidence that their changes are valid and have great chance of succeeding when pushed to the nodes.
In the execution results the output contains .result.nodesWithConfigChanges
list that shows which nodes would be affected by the change. As we removed the banner resource that was targeting the leaf nodes we can see that only the leaf nodes are included in this list:
Now, we mentioned the diffs, where are they?
Diffs#
Every transaction will keep the diff for the changed resources and the diffs for the node configurations that were made as a result of this transaction. Both transactions running with dry run and without it, will have the diffs available to the user.
Nokia EDA does not store the diffs themselves, instead the full configuration before and after the change is kept. A separate module is available to fetch the node configs that were generated as part of the transaction. For example, in the dry run that we ran above the transaction number that was recorded was 84
. Using this transaction number and knowing that leaf1
and leaf2
were targeted by the dry run, we can fetch the "expected" diffs if the transaction was executed.
We run this playbook with overriding the static tx id like this:
- name: Get Node Diff
hosts: all
gather_facts: false
vars:
tx_id: 60
node: leaf1
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Get node diff
nokia.eda_core_v1.transaction.v2.result.diffs.nodecfg:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx_id }}"
namespace: "eda"
node: "{{ node }}"
register: response
- name: print var
ansible.builtin.debug:
var: response
Note, that the result is structured as:
Where each data
block contains the config in the format that is used by EDA to push changes to the devices (native CLI or Openconfig).
response:
changed: false
failed: false
result:
after:
data: |-
interface ethernet-1/1 {
admin-state enable
}
interface ethernet-1/2 {
admin-state enable
}
interface ethernet-1/3 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/4 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/5 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/6 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/7 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/8 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/9 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/10 {
description lag-leaf1-e1011-local
admin-state enable
ethernet {
aggregate-id lag2
lacp-port-priority 32768
}
}
interface ethernet-1/11 {
description lag-leaf1-e1011-local
admin-state enable
ethernet {
aggregate-id lag2
lacp-port-priority 32768
}
}
interface ethernet-1/12 {
description lag-leaf1-2-e1212-local
admin-state enable
ethernet {
aggregate-id lag1
lacp-port-priority 32768
}
}
interface ethernet-1/14 {
admin-state enable
}
interface lag1 {
description lag-leaf1-2-e1212-local
admin-state enable
vlan-tagging true
lag {
lag-type lacp
min-links 1
lacp-fallback-mode static
lacp-fallback-timeout 60
lacp {
interval FAST
lacp-mode ACTIVE
admin-key 1
system-id-mac FE:2F:AA:00:00:01
system-priority 32768
}
}
}
interface lag2 {
description lag-leaf1-e1011-local
admin-state enable
vlan-tagging true
lag {
lag-type lacp
min-links 1
lacp-fallback-mode static
lacp-fallback-timeout 60
lacp {
interval FAST
lacp-mode ACTIVE
admin-key 3
system-id-mac FE:2F:AA:00:00:03
system-priority 32768
}
}
}
interface mgmt0 {
admin-state enable
subinterface 0 {
admin-state enable
ipv4 {
admin-state enable
dhcp-client {
trace-options {
trace [
messages
]
}
}
}
ipv6 {
admin-state enable
dhcp-client {
trace-options {
trace [
messages
]
}
}
}
}
}
network-instance mgmt {
type ip-vrf
admin-state enable
description "Management network instance"
interface mgmt0.0 {
}
protocols {
linux {
import-routes true
export-routes true
}
}
}
system {
aaa {
authentication {
authentication-method [
local
]
admin-user {
password $aes1$ATfFb4Am95W8pW8=$eOQ5tWPh1Whx7GovYXvUUA==
}
}
authorization {
role sudo {
superuser true
services [
cli
gnmi
gnoi
gnsi
netconf
]
}
}
server-group local {
type local
}
}
ssh-server mgmt {
admin-state enable
network-instance mgmt
}
boot {
autoboot {
admin-state enable
}
}
configuration {
role sudo {
}
}
grpc-server mgmt {
admin-state enable
rate-limit 65535
session-limit 1024
metadata-authentication true
tls-profile EDA
network-instance mgmt
port 57400
services [
gnmi
gnoi
gnsi
]
gnmi {
commit-save false
}
}
lldp {
interface ethernet-1/1 {
admin-state enable
}
interface ethernet-1/2 {
admin-state enable
}
interface ethernet-1/3 {
admin-state enable
}
interface ethernet-1/4 {
admin-state enable
}
interface ethernet-1/5 {
admin-state enable
}
interface ethernet-1/6 {
admin-state enable
}
interface ethernet-1/7 {
admin-state enable
}
interface ethernet-1/8 {
admin-state enable
}
interface ethernet-1/9 {
admin-state enable
}
interface ethernet-1/10 {
admin-state enable
}
interface ethernet-1/11 {
admin-state enable
}
interface ethernet-1/12 {
admin-state enable
}
interface ethernet-1/14 {
admin-state enable
}
}
name {
host-name leaf1
}
network-instance {
protocols {
evpn {
ethernet-segments {
bgp-instance 1 {
ethernet-segment lag-leaf1-2-e1212-local {
admin-state enable
esi 00:FE:2F:AA:00:00:01:00:00:00
multi-homing-mode all-active
interface lag1 {
}
df-election {
timers {
activation-timer 0
}
algorithm {
type default
}
}
}
}
}
}
bgp-vpn {
bgp-instance 1 {
}
}
}
}
}
before:
data: |-
interface ethernet-1/1 {
admin-state enable
}
interface ethernet-1/2 {
admin-state enable
}
interface ethernet-1/3 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/4 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/5 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/6 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/7 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/8 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/9 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/10 {
description lag-leaf1-e1011-local
admin-state enable
ethernet {
aggregate-id lag2
lacp-port-priority 32768
}
}
interface ethernet-1/11 {
description lag-leaf1-e1011-local
admin-state enable
ethernet {
aggregate-id lag2
lacp-port-priority 32768
}
}
interface ethernet-1/12 {
description lag-leaf1-2-e1212-local
admin-state enable
ethernet {
aggregate-id lag1
lacp-port-priority 32768
}
}
interface ethernet-1/14 {
admin-state enable
}
interface lag1 {
description lag-leaf1-2-e1212-local
admin-state enable
vlan-tagging true
lag {
lag-type lacp
min-links 1
lacp-fallback-mode static
lacp-fallback-timeout 60
lacp {
interval FAST
lacp-mode ACTIVE
admin-key 1
system-id-mac FE:2F:AA:00:00:01
system-priority 32768
}
}
}
interface lag2 {
description lag-leaf1-e1011-local
admin-state enable
vlan-tagging true
lag {
lag-type lacp
min-links 1
lacp-fallback-mode static
lacp-fallback-timeout 60
lacp {
interval FAST
lacp-mode ACTIVE
admin-key 3
system-id-mac FE:2F:AA:00:00:03
system-priority 32768
}
}
}
interface mgmt0 {
admin-state enable
subinterface 0 {
admin-state enable
ipv4 {
admin-state enable
dhcp-client {
trace-options {
trace [
messages
]
}
}
}
ipv6 {
admin-state enable
dhcp-client {
trace-options {
trace [
messages
]
}
}
}
}
}
network-instance mgmt {
type ip-vrf
admin-state enable
description "Management network instance"
interface mgmt0.0 {
}
protocols {
linux {
import-routes true
export-routes true
}
}
}
system {
aaa {
authentication {
authentication-method [
local
]
admin-user {
password $aes1$ATfFb4Am95W8pW8=$eOQ5tWPh1Whx7GovYXvUUA==
}
}
authorization {
role sudo {
superuser true
services [
cli
gnmi
gnoi
gnsi
netconf
]
}
}
server-group local {
type local
}
}
ssh-server mgmt {
admin-state enable
network-instance mgmt
}
boot {
autoboot {
admin-state enable
}
}
configuration {
role sudo {
}
}
grpc-server mgmt {
admin-state enable
rate-limit 65535
session-limit 1024
metadata-authentication true
tls-profile EDA
network-instance mgmt
port 57400
services [
gnmi
gnoi
gnsi
]
gnmi {
commit-save false
}
}
lldp {
interface ethernet-1/1 {
admin-state enable
}
interface ethernet-1/2 {
admin-state enable
}
interface ethernet-1/3 {
admin-state enable
}
interface ethernet-1/4 {
admin-state enable
}
interface ethernet-1/5 {
admin-state enable
}
interface ethernet-1/6 {
admin-state enable
}
interface ethernet-1/7 {
admin-state enable
}
interface ethernet-1/8 {
admin-state enable
}
interface ethernet-1/9 {
admin-state enable
}
interface ethernet-1/10 {
admin-state enable
}
interface ethernet-1/11 {
admin-state enable
}
interface ethernet-1/12 {
admin-state enable
}
interface ethernet-1/14 {
admin-state enable
}
}
banner {
login-banner "This is a demo banner provisioned by Ansible on leafs"
}
name {
host-name leaf1
}
network-instance {
protocols {
evpn {
ethernet-segments {
bgp-instance 1 {
ethernet-segment lag-leaf1-2-e1212-local {
admin-state enable
esi 00:FE:2F:AA:00:00:01:00:00:00
multi-homing-mode all-active
interface lag1 {
}
df-election {
timers {
activation-timer 0
}
algorithm {
type default
}
}
}
}
}
}
bgp-vpn {
bgp-instance 1 {
}
}
}
}
}
dataUnavailable: false
format: text
status: 200
Most likely you would want to see the diff result in a way like the NOS display it; with added and removed line. You can achieve this currently with doing the diff outside of the platform1, for example, you can create a set of ansible tasks that would do the diff on your control node and use it like this:
- name: Get Node Diff with Tasks
hosts: all
gather_facts: false
vars:
tx_id: 60
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Get transaction execution result
nokia.eda_core_v1.transaction.v2.result.execution:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx_id }}"
waitForComplete: true
failOnErrors: true
register: result_execution
- name: Process node configuration diffs
include_tasks: process-node-diff.yaml
loop: "{{ result_execution.result.nodesWithConfigChanges }}"
loop_control:
loop_var: node_item
- name: Fetch node diffs
nokia.eda_core_v1.transaction.v2.result.diffs.nodecfg:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx_id }}"
namespace: "{{ node_item.namespace }}"
node: "{{ node_item.name }}"
register: diff_result
- name: Write before config to temp file
ansible.builtin.copy:
content: "{{ diff_result.result.before.data | default('') }}"
dest: "/tmp/before_config_{{ node_item.name }}.txt"
changed_when: false
- name: Write after config to temp file
ansible.builtin.copy:
content: "{{ diff_result.result.after.data | default('') }}"
dest: "/tmp/after_config_{{ node_item.name }}.txt"
changed_when: false
- name: Generate unified diff
ansible.builtin.shell: diff -u "/tmp/before_config_{{ node_item.name }}.txt" "/tmp/after_config_{{ node_item.name }}.txt"
register: config_diff
failed_when: false
changed_when: false
- name: Display configuration differences
ansible.builtin.debug:
msg: |
Configuration Diff for {{ node_item.name }} (- removed, + added):
{{ config_diff.stdout }}
- name: Clean up temp files
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- "/tmp/before_config_{{ node_item.name }}.txt"
- "/tmp/after_config_{{ node_item.name }}.txt"
changed_when: false
The playbook leverages the transaction execution result that contains the list of nodes with changes and loops over these nodes to get the raw configs for them and computing the diff on disk. The output then becomes much more familiar to the network operators:
TASK [Display configuration differences] ************************************************************************
ok: [localhost] =>
msg: |-
Configuration Diff for leaf2 (- removed, + added):
--- /tmp/before_config_leaf2.txt 2025-08-26 15:16:32.000217924 +0200
+++ /tmp/after_config_leaf2.txt 2025-08-26 15:16:32.150402060 +0200
@@ -225,9 +225,6 @@
admin-state enable
}
}
- banner {
- login-banner "This is a demo banner provisioned by Ansible on leafs"
- }
name {
host-name leaf2
}
As we can see, the diff rightfully suggests that the login banner is about to be removed if we were to proceed with the transaction.
Revert#
Should you find yourself in a situation where a rolled out changed brought more bad than good, you can revert the entire transaction. Let's revert the transaction that added two banner resources, this was done in transaction with ID 83
.
- name: Revert Transaction
hosts: all
gather_facts: false
vars:
tx_id: 60
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Revert transaction
nokia.eda_core_v1.transaction.v2.revert:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx_id }}"
register: response
- name: print var
ansible.builtin.debug:
var: response
The revert of a transaction results in a new transaction that negates the actions that were taken in the transaction referred by its ID.
Restore#
If thing were great 2, 20 or 200 transactions ago you should have a chance to restore the system to that state. The Git-powered time machine that EDA provides allows you to easily revert to a previous state no matter where you are.
Just a moment ago we recorded a transaction 85 where we reverted things, but let's say we want to go back to transaction 77. Easy:
Run by providing the desired transaction ID:
- name: Revert Transaction
hosts: all
gather_facts: false
vars:
tx_id: 60
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Revert transaction
nokia.eda_core_v1.transaction.v2.revert:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
transactionId: "{{ tx_id }}"
register: response
- name: print var
ansible.builtin.debug:
var: response
System information#
EDA version#
- name: Get EDA version
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Get EDA version
nokia.eda_core_v1.about.version:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
state: query
register: response
- name: print var
ansible.builtin.debug:
var: response
EDA health#
- name: Get EDA health
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Get EDA health
nokia.eda_core_v1.about.health:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
state: query
register: response
- name: print var
ansible.builtin.debug:
var: response
Users#
Get users#
The examples show how to retrieve all users of the platform as well as a named user.
- name: Get all and a specific user
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Get all users
nokia.eda_core_v1.admin.users.users:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
state: query
register: users
- name: print var
ansible.builtin.debug:
var: users
- name: Get admin user
nokia.eda_core_v1.admin.users.users:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
username: admin
state: query
register: admin_user
- name: print var
ansible.builtin.debug:
var: admin_user
users:
changed: false
failed: false
result:
- enabled: true
firstName: EDA
groups:
- 5aefa9bb-6459-491e-994a-794c9a04a6e3
lastName: admin user
status:
lastSuccessfulLogin: "2025-08-24T14:56:15Z"
username: admin
uuid: f2a75035-56a5-4ba0-be9b-53e1351259be
- email: [email protected]
enabled: false
firstName: John
lastName: Wick
status: {}
username: new-user
uuid: bbd43cb1-4688-4952-ac22-f67c05a7fff2
status: 200
admin_user:
changed: false
failed: false
result:
- enabled: true
firstName: EDA
groups:
- 5aefa9bb-6459-491e-994a-794c9a04a6e3
lastName: admin user
status:
lastSuccessfulLogin: "2025-08-24T14:56:15Z"
username: admin
uuid: f2a75035-56a5-4ba0-be9b-53e1351259be
status: 200
Create user#
When creating the user, its group and password should be set separately after the user creation.
- name: Create user
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Create a new user
nokia.eda_core_v1.admin.users.users:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
username: new-user
firstName: John
lastName: Wick
email: [email protected]
state: present
register: response
- name: print var
ansible.builtin.debug:
var: response
response:
changed: true
failed: false
result:
email: [email protected]
enabled: false
firstName: John
lastName: Wick
status: {}
username: new-user
uuid: bbd43cb1-4688-4952-ac22-f67c05a7fff2
status: 201
Reset password#
To set the password of a newly created user or resetting a password of an existing user.
- name: Reset password for the user
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Get user
nokia.eda_core_v1.admin.users.users:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
username: new-user
state: query
register: new_user
- name: print var
ansible.builtin.debug:
var: new_user
- name: Reset password
nokia.eda_core_v1.admin.users.resetpassword:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
uuid: "{{ new_user.result[0].uuid }}"
temporary: false
value: pass123
register: reset_request
- name: print var
ansible.builtin.debug:
var: reset_request
Node configs#
Get node configuration in the format that EDA uses to provision the nodes. Currently either native CLI or Openconfig.
- name: Get node config
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Get node config
nokia.eda_core_v1.nodeconfig.v2.namespaces.nodes:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
nsName: eda
nodeName: leaf1
state: query
register: response
- name: print var
ansible.builtin.debug:
var: response
The output contains the config annotations in the .result.annotations
list and the running config in the .result.running
block.
response:
changed: false
failed: false
result:
annotations:
- cr:
gvk:
group: bootstrap.eda.nokia.com
kind: Init
version: v1alpha1
name: init-base
lines:
- endLine: 141
startLine: 97
- endLine: 145
startLine: 145
- endLine: 170
startLine: 158
- endLine: 192
startLine: 175
- endLine: 235
startLine: 232
- endLine: 265
startLine: 265
- cr:
gvk:
group: bootstrap.eda.nokia.com
kind: ManagementRouter
version: v1alpha1
name: mgmt-router
lines:
- endLine: 125
startLine: 123
- endLine: 135
startLine: 135
- cr:
gvk:
group: core.eda.nokia.com
kind: Manifest
version: v1
name: aaa.eda.nokia.com
lines:
- endLine: 144
startLine: 142
- endLine: 156
startLine: 147
- endLine: 174
startLine: 171
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: lag-leaf1-2-e1212-local
lines:
- endLine: 57
startLine: 50
- endLine: 78
startLine: 61
- endLine: 136
startLine: 136
- endLine: 228
startLine: 226
- endLine: 265
startLine: 236
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: lag-leaf1-e1011-local
lines:
- endLine: 49
startLine: 34
- endLine: 96
startLine: 79
- endLine: 225
startLine: 220
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-1
lines:
- endLine: 2
- endLine: 195
startLine: 193
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-14
lines:
- endLine: 60
startLine: 58
- endLine: 231
startLine: 229
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-2
lines:
- endLine: 5
startLine: 3
- endLine: 198
startLine: 196
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-3
lines:
- endLine: 9
startLine: 6
- endLine: 201
startLine: 199
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-4
lines:
- endLine: 13
startLine: 10
- endLine: 204
startLine: 202
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-5
lines:
- endLine: 17
startLine: 14
- endLine: 207
startLine: 205
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-6
lines:
- endLine: 21
startLine: 18
- endLine: 210
startLine: 208
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-7
lines:
- endLine: 25
startLine: 22
- endLine: 213
startLine: 211
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-8
lines:
- endLine: 29
startLine: 26
- endLine: 216
startLine: 214
- cr:
gvk:
group: interfaces.eda.nokia.com
kind: Interface
version: v1alpha1
name: leaf1-ethernet-1-9
lines:
- endLine: 33
startLine: 30
- endLine: 219
startLine: 217
running: |-
interface ethernet-1/1 {
admin-state enable
}
interface ethernet-1/2 {
admin-state enable
}
interface ethernet-1/3 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/4 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/5 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/6 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/7 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/8 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/9 {
admin-state enable
vlan-tagging true
}
interface ethernet-1/10 {
description lag-leaf1-e1011-local
admin-state enable
ethernet {
aggregate-id lag2
lacp-port-priority 32768
}
}
interface ethernet-1/11 {
description lag-leaf1-e1011-local
admin-state enable
ethernet {
aggregate-id lag2
lacp-port-priority 32768
}
}
interface ethernet-1/12 {
description lag-leaf1-2-e1212-local
admin-state enable
ethernet {
aggregate-id lag1
lacp-port-priority 32768
}
}
interface ethernet-1/14 {
admin-state enable
}
interface lag1 {
description lag-leaf1-2-e1212-local
admin-state enable
vlan-tagging true
lag {
lag-type lacp
min-links 1
lacp-fallback-mode static
lacp-fallback-timeout 60
lacp {
interval FAST
lacp-mode ACTIVE
admin-key 1
system-id-mac FE:2F:AA:00:00:01
system-priority 32768
}
}
}
interface lag2 {
description lag-leaf1-e1011-local
admin-state enable
vlan-tagging true
lag {
lag-type lacp
min-links 1
lacp-fallback-mode static
lacp-fallback-timeout 60
lacp {
interval FAST
lacp-mode ACTIVE
admin-key 3
system-id-mac FE:2F:AA:00:00:03
system-priority 32768
}
}
}
interface mgmt0 {
admin-state enable
subinterface 0 {
admin-state enable
ipv4 {
admin-state enable
dhcp-client {
trace-options {
trace [
messages
]
}
}
}
ipv6 {
admin-state enable
dhcp-client {
trace-options {
trace [
messages
]
}
}
}
}
}
network-instance mgmt {
type ip-vrf
admin-state enable
description "Management network instance"
interface mgmt0.0 {
}
protocols {
linux {
import-routes true
export-routes true
}
}
}
system {
aaa {
authentication {
authentication-method [
local
]
admin-user {
password $aes1$ATfFb4Am95W8pW8=$eOQ5tWPh1Whx7GovYXvUUA==
}
}
authorization {
role sudo {
superuser true
services [
cli
gnmi
gnoi
gnsi
netconf
]
}
}
server-group local {
type local
}
}
ssh-server mgmt {
admin-state enable
network-instance mgmt
}
boot {
autoboot {
admin-state enable
}
}
configuration {
role sudo {
}
}
grpc-server mgmt {
admin-state enable
rate-limit 65535
session-limit 1024
metadata-authentication true
tls-profile EDA
network-instance mgmt
port 57400
services [
gnmi
gnoi
gnsi
]
gnmi {
commit-save false
}
}
lldp {
interface ethernet-1/1 {
admin-state enable
}
interface ethernet-1/2 {
admin-state enable
}
interface ethernet-1/3 {
admin-state enable
}
interface ethernet-1/4 {
admin-state enable
}
interface ethernet-1/5 {
admin-state enable
}
interface ethernet-1/6 {
admin-state enable
}
interface ethernet-1/7 {
admin-state enable
}
interface ethernet-1/8 {
admin-state enable
}
interface ethernet-1/9 {
admin-state enable
}
interface ethernet-1/10 {
admin-state enable
}
interface ethernet-1/11 {
admin-state enable
}
interface ethernet-1/12 {
admin-state enable
}
interface ethernet-1/14 {
admin-state enable
}
}
name {
host-name leaf1
}
network-instance {
protocols {
evpn {
ethernet-segments {
bgp-instance 1 {
ethernet-segment lag-leaf1-2-e1212-local {
admin-state enable
esi 00:FE:2F:AA:00:00:01:00:00:00
multi-homing-mode all-active
interface lag1 {
}
df-election {
timers {
activation-timer 0
}
algorithm {
type default
}
}
}
}
}
}
bgp-vpn {
bgp-instance 1 {
}
}
}
}
}
status: 200
Queries#
To execute an EQL query in a non-streaming mode, use the nokia.eda_core_v1.query.v1.eql.eql
module:
- name: Run EQL Query
hosts: all
gather_facts: false
tasks:
- name: Fetch EDA access token
nokia.eda_utils_v1.get_token:
base_url: "{{ eda_api_url }}"
client_secret: "{{ client_secret }}"
username: admin
password: "{{ eda_password }}"
register: token
- name: Run a query
nokia.eda_core_v1.query.v1.eql.eql:
base_url: "{{ eda_api_url }}"
auth_token: "Bearer {{ token.result.access_token }}"
query: .namespace.node.srl
namespaces: eda
register: response
- name: print var
ansible.builtin.debug:
var: response
response:
changed: false
failed: false
result:
data:
- .namespace.name: eda
.namespace.node.name: leaf1
version: 25.3.2
- .namespace.name: eda
.namespace.node.name: leaf2
version: 25.3.2
- .namespace.name: eda
.namespace.node.name: spine1
version: 25.3.2
jsonSchema:
properties:
.namespace.name:
type: string
x-eda-nokia-com:
ui-order-priority: 1
.namespace.node.name:
type: string
x-eda-nokia-com:
ui-order-priority: 2
version:
type: string
type: object
x-eda-nokia-com:
properties:
keys:
properties:
.namespace.name:
type: string
.namespace.node.name:
type: string
type: object
type: object
schema:
fields:
- annotations:
- end_char: 8
start_char: 0
- end_char: 13
start_char: 10
display_name: namespace.name
name: .namespace.name
type: string
- annotations:
- end_char: 3
start_char: 0
- end_char: 8
start_char: 5
display_name: node.name
name: .namespace.node.name
type: string
- annotations:
- end_char: 6
start_char: 0
display_name: version
name: version
type: string
status: 200
In the response, you will find the list of returned objects under the .result.data
key. The elements of this list would match what you would see in the table when running EQL in EDA UI.
In addition to the actual values, the response will include the schema for the returned objects, such is their types and structure.
Changelog v1#
0.2.0#
- Add documentation for the top level collection fields
- Update modules for workflows and resource lists
0.1.0#
Initial Beta release.
-
Later we will have a module in the util collection that will allow you performing the diff easily. ↩