PY-SIL Callback Reference
Note
This section is preliminary.
More examples and callbacks will be added in the future.
Each callback type has its own callback API function prototype. To register callback it should be added to a specific y_[module_name].py file in "callbacks" section (see PY-SIL Callback Definition in the Config).
This code is usually generated automatically by pyang library plugin. Refer to PY-SIL Code Generation for examples on how auto generate stub PY-SIL code.
The following APIs are described in this section:
PY-SIL GET2 Callback : Callback for GET operations
PY-SIL All In One (AIO) GET2 Callback : Callback for GET (All In One) operations
PY-SIL EDIT3 Callback : Latest Generation EDIT Callbacks
PY-SIL RPC Callback : Callback for RPC operations
PY-SIL Action Callback : Callback for Action operations
PY-SIL Notifications : Notifications interface for PY-SIL
PY-SIL GET2 Callback
A GET2 callback method is usually mapped to a single data node. The server will issue “get” and “getnext” requests via this API, and the callback code will return value nodes for the data it is managing. These data nodes are not maintained in the server Data Tree. They are not cached in the server.
This API uses 1 callback per object template. The server will issue “get” and “getnext” requests via the API, and the callback code will return value nodes for the data is is managing. These data nodes are not maintained in the server data tree. They are not cached in the server.
Which Nodes Have PY-SIL GET2 Callbacks
This section is described in the YumaPro Developer Manual. For the information please see Which Nodes Have GET2 Callbacks.
PY-SIL GET2 Callback Method
- def get2_method_name(self, silcb: SILCbGet)
Callback method for server object handler get2 callback Used to provide main and/or subsystem retrieval of instances of a specific named object.
- Parameters:
silcb (SILCbGet) -- GET2 control block contains all the input, output, and state data used for GET2 transactions.
- Returns:
STATUS_T.NO_ERR if executed OK and found OK STATUS_T.ERR_NCX_NO_INSTANCE warning if no instance found
- Return type:
STATUS_T
PY-SIL GET2 Control Block
- class pysil.module.SILCbGet(scb: LP_STRUCT_SES_CB_T_, msg: LP_STRUCT_RPC_MSG_T_, get2cb: LP_STRUCT_GETCB_GET2_T_, module_name: str, callback_info: dict)
Bases:
SILCbBase
The
SILCbGet
class is a comprehensive control block used in GET2 callbacks. It encapsulates all necessary pointers and information required for the execution of the GET2 callback methods.- add_return_data(data: list | dict | str | int, has_more: bool = False) ENUM_STATUS_T_
Adds data to return
- Parameters:
data (dict, list, str or int) -- provide return data here list (for list of objects), dict (for single object), str or int (for simple leaf nodes)
has_more (bool) -- set to True if has more data to return
- Returns:
Status
- property ancestor_keys: dict
Ancestors keys for silcb
- property callback_info: dict
Property to get callback_info
- property cbmode: int
Reason for the callback (get or getnext), compare with GETCB_MODE_T values
- property choice_active_case: str
FOR CHOICES ONLY! Current active case
- property client_addr: str
This field contains the client address involved in the transaction. or None
- property get2cb: LP_STRUCT_GETCB_GET2_T_
Contains a pointer to the session control block that initiated the request, representing the current session's context.
- property local_keys: dict
Local keys for silcb
- property local_keys_fixed: list
Local keys fixed for silcb
- property local_keys_present: list
Local keys present for silcb
- log(msg: str, log_level: str = 'INFO') None
Adds message to log
- property module_name: str
- property msg: LP_STRUCT_RPC_MSG_T_
Pointer to incoming XML message header
- scb() LP_STRUCT_SES_CB_T_
Pointer to scb
- send_terminal_message(message: str) ENUM_STATUS_T_
Sends message to terminal
- set_choice_active_case(active_case: str) ENUM_STATUS_T_
FOR CHOICES ONLY! Sets active_case to specific value for choices.
- Parameters:
active_case (str) -- Provide active case string here
- Returns:
Status
- property username: str
user_id value or None
- property utils: PYSILUtils
returns utils for the silcb
PY-SIL GET2 Return Data
Each GET2 method returns a status code:
NO_ERR
: data is returnedERR_NCX_NO_INSTANCE
: the specified data instance does not existOther status code: some error occurred
If the node has child nodes the callback is expected to add data using add_return_data() method of SILCbGet. Depending on the object type and object nodes the data format will be different.
PY-SIL GET2 Return Data Examples
This section briefly illustrates examples of common nodes returning data within PY-SIL GET2 callback.
container - returns the child terminal nodes values.
# collect return_data
return_data = {
"B1": None, # remove or replace value with correct <int>
}
# add_return data
res = silcb.add_return_data(data=return_data)
list - Returns the keys values and perhaps the rest of the child terminal nodes values. Set “has_more” in add_return_data() to trigger “getnext” until no more instances found.
# collect return_data
return_data = {
"last-name": "str", # replace value with correct <str>
"first-name": "str", # replace value with correct <str>
"street": "str", # replace value with correct <str>
"city": "str", # replace value with correct <str>
"zipcode": "str", # replace value with correct <str>
}
# set has_more=True if more data available
# add return data
res = silcb.add_return_data(data=return_data, has_more=True)
choice - Returns the name of the active case and child terminal nodes values in the active case.
# set choice active case
silcb.set_choice_active_case("A")
# collect return_data
return_data = {
# Choose one of:
# * Case A
"A1": "str", # remove or replace value with correct <str>
"A2": "str", # remove or replace value with correct <str>
# * Case B
# "A3": "int", # remove or replace value with correct <int>
# # No data to return
# * Case C
}
# add return data
res = silcb.add_return_data(data=return_data)
leaf - Returns the leaf value.
# set return data
return_data = "str" # *required - replace value with
# add return data
res = silcb.add_return_data(data=return_data)
leaf-list - Returns all values for the leaf-list.
# collect return_data
return_data = [
53,
67,
92
]
# add return data
res = silcb.add_return_data(data=return_data)
PY-SIL GET2 Processing Procedure
The server will send a <get-request> server request message to each subsystem that has registered a get2 callback for the specific object.
For single-instance nodes (container, leaf, choice, anyxml) only a “get” request will be made.
For a leaf-list node, only “get” requests will be made, but all instances of the leaf-list are returned at once.
For a list node, a “get” request will be made if the keys are known.
A “get” request that contains no keys will cause the first instance to be returned.
Entries are ordered by their key values, exactly as specified in the YANG list. If there is no key-stmt defined, then the callback will still return the first entry, except the server will not attempt any sorting if multiple entries are returned (E.g 1 from each subsystem).
It is always up to the callback to know what is the “best” next entry, given the keys that are returned.
If there are more list entries, then the has_more” parameter in add_return_data() should be set to True.
The server will issue “getnext” (GETCB_GETNEXT_VALUE) requests as needed until the “has_more” parameter is set to False.
PY-SIL GET2 Callback Initialization and Cleanup
PY-SIL library provides functionality to register callback methods based on a configuration provided in a dictionary format. It register all callback specified in the configuration during init phase 1 and unregister them during cleanup phase.
config = {
callbacks: [
{
"path": "/address_get_2:addresses/address",
"cb_get2": "get_address",
}
]
}
"path": "/address_get_2:addresses/address" This specifies the path where the callback method get_address should be invoked.
"cb_get2": "get_address" This specifies the name of the callback method that should be invoked when a request is made to the specified path. In this case, the method named get_address will be invoked.
PY-SIL GET2 Callback Examples
PY-SIL GET2 Callback Container Example
Consider this simplified example, that represents GET2 callback for the “interface-state” container.
container interfaces-state {
config false;
leaf in-errors { type uint32; }
// rest of leafs removed
}
The following PY-SIL code shows a GET2 callback which returns the “interfaces-state” container terminal node value.
class PYSILModuleIetfInterfaces(PYSILModuleIetfInterfacesBase):
# ...
def get_interfaces_state(self, silcb: SILCbGet):
"""
Get database object callback for container interfaces-state
Path: container interfaces-state
Fill in 'get2cb' response fields.
Parameters
----------
silcb:SILCbGet
Control block
Returns
-------
STATUS_T
status of the callback.
"""
self.log(f"Enter 'get_interfaces_state' callback")
if silcb.cbmode == GETCB_MODE_T.GETCB_GET_VALUE:
pass
elif silcb.cbmode == GETCB_MODE_T.GETCB_GETNEXT_VALUE:
return STATUS_T.ERR_NCX_NO_INSTANCE
else:
# USE SET_ERROR FOR PROGRAMMING BUGS ONLY
return self.flag_internal_error()
res = STATUS_T.NO_ERR
# Item found. Create a python object (dict, list, etc) with item data
return_data = {
"in-errors": 8, # remove or replace value with correct <int>
}
# Save return data
res = silcb.add_return_data(data=return_data)
# Return status
return res
To ensure that the GET2 callbacks are working as expected an application may retrieve running configuration and device state information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<in-errors>8</in-errors>
</interfaces-state>
</rpc-reply>
PY-SIL GET2 Callback List Example
Consider this simplified example, that demonstrates a GET2 callback for the “interface” list.
container interfaces-state {
config false;
list interface {
key "ip";
leaf ip {
type inet:ip-prefix;
}
leaf name {
type string;
mandatory true;
}
}
}
The GET2 callbacks for “list” elements must fill in the return key values. All additional leafs are optional to fill in. Any other descendant complex elements, such other lists, containers, or choice nodes must have their own callbacks methods.
class PYSILModuleIetfInterfaces(PYSILModuleIetfInterfacesBase):
# ...
def get_list_interface(self, silcb: SILCbGet):
"""
Get database object callback
Path: list /interfaces-state/interface
Parameters
----------
silcb:SILCbGet
Control block
Returns
-------
STATUS_T
status of the callback.
Additional
----------
silcb.ancestor_keys: dict with ancestor keys
no ancestor keys
silcb.local_keys: dict with local keys
silcb.local_keys["ip"]: str
Local key leaf 'ip' in list 'interface' Path: /interfaces-state/interface/ip
silcb.local_keys_present: list of present and valid local keys
silcb.local_keys_fixed: list of local keys fixed in a getnext request
"""
data = ["10.10.10.1/16", "10.10.10.2/16", "10.10.10.3/16"]
ip, has_more = self.__get2_from_list(silcb, key_name="ip", data=data)
if ip is None:
return STATUS_T.ERR_NCX_NO_INSTANCE
# Item found. Create a python object (dict, list, etc) with item data
return_data = {
"ip": ip, # *required - replace value with correct <str>
"name": "interface-name", # remove or replace value with correct <str>
}
# Save return data and mark if next item exists
res = silcb.add_return_data(data=return_data, has_more=has_more)
# Return status
return res
def __get2_from_list(self, silcb: SILCbGet, key_name: str, data: list):
data_found = False
has_more = False
name = silcb.local_keys[key_name]
ret_name = None
if silcb.cbmode == GETCB_MODE_T.GETCB_GET_VALUE:
if name:
# /* get a specified instance */
if name in data:
ret_name = name
has_more = name != data[-1]
data_found = True
else:
return None, has_more
else:
# // else no keys == get-first
ret_name = data[0]
data_found = True
has_more = True, has_more
# If the callback mode is GETCB_GETNEXT_VALUE (get next value) Find current item first (by keys values from current node) and get next item to return
if silcb.cbmode == GETCB_MODE_T.GETCB_GETNEXT_VALUE:
# use keys silcb.local_keys to filter data and find next record
if name in silcb.local_keys_fixed:
return None, has_more
# /* adjust the key to find the next entry after
# * the specified value
# */
if not name:
ret_name = data[0] # return first entry [0]
has_more = True
data_found = True
else:
# /* find the correct entry to retrieve */
for i in range(len(data) - 1):
if name == data[i]:
ret_name = data[i + 1]
has_more = i < len(data) - 1
data_found = True
break
# If item doesn't exist, exit with an error
if not data_found:
return None, has_more
# Item found. Create a python object (dict, list, etc) with item data
return ret_name, has_more
To ensure that the GET2 callbacks are working as expected an application can retrieve running configuration and device state information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<interface>
<ip>10.10.10.1/16</ip>
</interface>
</interfaces-state>
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<interface>
<ip>10.10.10.1/16</ip>
<name>interface-name</name>
</interface>
</interfaces-state>
</rpc-reply>
If no key value is specified in the request or the request subtree filter target is “interface-state” parent container, then the server will try to output all the interfaces and its children:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces-state xmlns="http://yumaworks.com/ns/ietf-interfaces-example">
<interface>
<ip>10.10.10.1/16</ip>
<name>interface-name</name>
</interface>
<interface>
<ip>10.10.10.2/16</ip>
<name>interface-name2</name>
</interface>
<interface>
<ip>10.10.10.3/16</ip>
<name>interface-name3</name>
</interface>
</interfaces-state>
</rpc-reply>
PY-SIL GET2 Callback Choice Example
A GET2 callback method for a YANG Choice is expected to set the name of the
active choice (silcb.set_choice_active_case(...)), and any terminal nodes values
from the active case. It can also return ERR_NCX_NO_INSTANCE
if no active
case is set.
Consider this simplified example, that represents GET callback for the node type “choice”.
choice type {
case interface { // active case
leaf interface {
type if:interface-ref;
}
}
case case-network {
leaf next-hop-host {
type inet:ip-address;
}
}
}
The following python code shows a simple GET2 callback method for the data model, which returns the name of the active case and child terminal nodes in the active case when it is requested.
class PYSILModuleIetfInterfaces(PYSILModuleIetfInterfacesBase):
# ...
def get_type(self, silcb: SILCbGet):
"""
Get database object callback
Path: choice /type
Parameters
----------
silcb:SILCbGet
Control block
Returns
-------
STATUS_T
status of the callback.
"""
self.log(f"Enter 'get_type' callback get2-test@2022-03-02")
res = STATUS_T.NO_ERR
data_found = False
# get the active case
# It may be NULL if no active case and choice is optional
active_case = "interface"
# set the active case return values
if active_case:
res = silcb.set_choice_active_case(active_case)
if res != STATUS_T.NO_ERR:
return res
return_data = {
# Choose one of:
# * Case interface
"interface": "if:interface-ref", # remove or replace value with correct <if:interface-ref>
# * Case case-network
# "next-hop-host": "inet:ip-address", # remove or replace value with correct <inet:ip-address>
}
# Save return data
res = silcb.add_return_data(data=return_data)
# Return status
return res
Note that any leaf or leaf-list nodes that are in nested within other “choice” statements within the active case are not returned. These are considered complex objects and the caller will ask for nested choice, list, and container nodes in a separate call.
container top {
choice complex-type {
case interface { // active case
choice new-choice {
leaf interface {
type if:interface-ref;
}
leaf test-interface {
type if:interface-ref;
}
}
}
case case-network {
leaf next-hop-host {
type inet:ip-address;
}
}
}
}
In the example above, the GET2 callback for 'complex-choice'
would be called first and the active case name interface
would
be returned without a terminal node.
Then the GET2 callback for 'new-choice' would be invoked and
this callback will return the name of the active case
(interface
or test-interface
), and the child leaf for that case.
GET2 Callback Example for To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<top xmlns="http://yumaworks.com/ns/interfaces" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<top xmlns="http://yumaworks.com/ns/interfaces">
<interface>ethernet1/1/1</interface>
</top>
</rpc-reply>
PY-SIL GET2 Callback Leaf Example
Consider this simplified example, that represents GET callback for a “leaf” node.
container top {
leaf get2-leaf {
config false;
type int32;
}
}
Note that only leaf or leaf-list nodes that are top level nodes or the parent node is a config=true node will require a GET2 callback.
The following python code shows a simple GET2 callback method for the data model, which returns the leaf value when it is requested.
class PYSILModuleIetfInterfaces(PYSILModuleIetfInterfacesBase):
leaf_val = 0
# ...
def get_get2_leaf(self, silcb: SILCbGet):
"""
Get database object callback
Path: leaf /get2test:get2-leaf
Parameters
----------
silcb:SILCbGet
Control block
Returns
-------
STATUS_T
status of the callback.
"""
self.log(
f"Enter 'get_get2_leaf' callback get2-test@2022-03-02 ancestor_keys={silcb.ancestor_keys} local_keys={silcb.local_keys} silcb.local_keys_present={silcb.local_keys_present} silcb.local_keys_fixed={silcb.local_keys_fixed}"
)
has_more = False
# Check callback mode (silcb.cbmode) to find data required to return
# If the callback mode is GETCB_GET_VALUE (get first value), use first record from your filtered data
if silcb.cbmode == GETCB_MODE_T.GETCB_GET_VALUE:
# get the real value from the system here.
# But in this example just increment the value by 1
self.leaf_val += 1
# If the callback mode is GETCB_GETNEXT_VALUE (get next value) Find current item first (by keys values from current node) and get next item to return
if silcb.cbmode == GETCB_MODE_T.GETCB_GETNEXT_VALUE:
# use keys silcb.local_keys to filter data and find next record
return STATUS_T.ERR_NCX_NO_INSTANCE
# Item found. Set specific return_data value
return_data = self.leaf_val # remove or replace value with correct <int>
# Save return data and mark if next item exists
res = silcb.add_return_data(data=return_data, has_more=has_more)
# Return status
return res
In the above example, the code generates the value based on the static value that is incremented after each retrieval. An agent can generate the value based on the real device statistical data or based on the system's statistical data, etc.
To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<get2-leaf xmlns="http://yumaworks.com/ns/interfaces" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get2-leaf xmlns="http://yumaworks.com/ns/interfaces">43</get2-leaf>
</rpc-reply>
PY-SIL GET2 Callback Leaf-List Example
A GET2 callback for a leaf-list is similar to the callback for a leaf, except multiple values can be returned, instead of one value. The server will return the data in the same order as returned by the callback method.
Consider this simplified example, that represents a GET2 callback for a “leaf-list” node.
leaf-list get2-leaf-list {
config false;
type int32;
}
The following python code shows a simple GET2 callback method for the same data model, which returns the leaf-list values when requested.
class PYSILModuleIetfInterfaces(PYSILModuleIetfInterfacesBase):
# ...
def get_get2_leaf_list(self, silcb: SILCbGet):
"""
Get database object callback
Path: leaf-list /get2test:get2-leaf-list
Parameters
----------
silcb:SILCbGet
Control block
Returns
-------
STATUS_T
status of the callback.
"""
self.log(
f"Enter 'get_get2_leaf_list' callback get2-test@2022-03-02 ancestor_keys={silcb.ancestor_keys} local_keys={silcb.local_keys} silcb.local_keys_present={silcb.local_keys_present} silcb.local_keys_fixed={silcb.local_keys_fixed}"
)
data_found = False
has_more = False
# Check callback mode (silcb.cbmode) to find data required to return
# If the callback mode is GETCB_GET_VALUE (get first value), use first record from your filtered data
if silcb.cbmode == GETCB_MODE_T.GETCB_GET_VALUE:
data_found = True
# 3 entries [53, 67, 92]
return_data = [53, 67, 92]
pass
# If the callback mode is GETCB_GETNEXT_VALUE (get next value) Find current item first (by keys values from current node) and get next item to return
if silcb.cbmode == GETCB_MODE_T.GETCB_GETNEXT_VALUE:
return STATUS_T.ERR_NCX_NO_INSTANCE
# If item doesn't exist, exit with an error
# check if the leaf-list exists;
# determined by the SIL or SIL-SA
# callback based on instances in the system
data_found = True
if not data_found:
return STATUS_T.ERR_NCX_NO_INSTANCE
# Save return data and mark if next item exists
res = silcb.add_return_data(data=return_data, has_more=has_more)
# Return status
return res
In the above example, the code generates the value based on the static value that does not fluctuate after each retrieval. An agent can generate the value based on the real device statistical data or based on the system's statistical data, etc.
To ensure that the GET2 callback is working as expected an application can retrieve running configuration and device state information as follows:
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get>
<filter type="subtree">
<get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces" />
</filter>
</get>
</rpc>
The server may reply with:
<rpc-reply message-id="3" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">53</get2-leaf-list>
<get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">67</get2-leaf-list>
<get2-leaf-list xmlns="http://yumaworks.com/ns/interfaces">92</get2-leaf-list>
</rpc-reply>
PY-SIL All In One (AIO) GET2 Callback
This section describes All In One (AIO) GET2 mechanism that allows to call a single GET2 callback for the whole Subtree. Common information about All In One GET2 Callback are available in All In One GET2 Callback.
PY-SIL supports All In One GET2 Callback returns in JSON format only.
PY-SIL GET2 AIO Callback Control Block
Control Block for PY-SIL GET2 AIO Callback is the sames as PY-SIL GET2 Control Block.
PY-SIL AIO List Example
In this example the server will invoke GET2 callbacks only for "nonconfig-list" first and then for AIO "nonconfig-list2" list, and will not invoke any deeper level callbacks.
The server will expect that the AIO "nonconfig-list2" callback will fill in the GET2 control block with the sufficient information about "nonconfig-list2" node children, complex children as well as terminal nodes.
Example Python file:
/usr/share/yumapro/src/address_get_aio/u_address_get_aio.py
This section illustrates how to use All In One GET2 callbacks for list nodes. Assume we have the same data model as described above then the GET2 callback may look as follow:
class PYSILModuleAddressGetAio(PYSILModuleAddressGetAioBase):
# ...
def get_address(self, silcb: SILCbGet):
"""
Get database object callback for list "address"
Path: list /address_get_aio:addresses/address
Parameters
----------
silcb: SILCbGet
Control block
Returns
-------
STATUS_T
status of the callback.
Additional
----------
silcb.local_keys: dict with local keys
silcb.local_keys["last-name"]: str
Local key leaf 'last-name' in list 'address' Path: /address_get_aio:addresses/address/last-name
silcb.local_keys["first-name"]: str
Local key leaf 'first-name' in list 'address' Path: /address_get_aio:addresses/address/first-name
silcb.local_keys_present: list of present and valid local keys
silcb.local_keys_fixed: list of local keys fixed in a getnext request
"""
self.log(
f"Enter 'get_address' callback address_get_aio@2024-01-22 local_keys={silcb.local_keys} silcb.local_keys_present={silcb.local_keys_present} silcb.local_keys_fixed={silcb.local_keys_fixed}"
)
# Load data from YAML file
test_data = load_test_data()
res = STATUS_T.NO_ERR
data_found = False
# Apply filters by local and ancestor keys if needed
# Filter data by silcb.local_keys_present and silcb.local_keys_fixed keys from silcb.local_keys
if "last-name" in silcb.local_keys_fixed:
# Local key leaf "last-name" in list "address" Path: /address_get_aio:addresses/address/last-name
# apply filter for the key using silcb.local_keys["last-name"] value
test_data = [
r
for r in test_data
if r.get("last-name") == silcb.local_keys["last-name"]
]
if "first-name" in silcb.local_keys_fixed:
# Local key leaf "first-name" in list "address" Path: /address_get_aio:addresses/address/first-name
# apply filter for the key using silcb.local_keys["first-name"] value
test_data = [
r
for r in test_data
if r.get("first-name") == silcb.local_keys["first-name"]
]
# Check callback mode (silcb.cbmode) to find data required to return
# If the callback mode is GETCB_GET_VALUE (get first value), use first record from your filtered data
if silcb.cbmode == GETCB_MODE_T.GETCB_GET_VALUE:
pass
# If the callback mode is GETCB_GETNEXT_VALUE (get next value) Find current item first (by keys values from current node) and get next item to return
elif silcb.cbmode == GETCB_MODE_T.GETCB_GETNEXT_VALUE:
return STATUS_T.ERR_NCX_NO_INSTANCE
else:
return self.flag_internal_error()
# If item doesn't exist, exit with an error
data_found = len(test_data) > 0
if not data_found:
return STATUS_T.ERR_NCX_NO_INSTANCE
# Items found. Create a python list of objects (dicts) with items data
# Example for data with one item in the list
# return_data = [
# # One item example
# {
# "last-name": "str", # *required - replace value with correct <str>
# "first-name": "str", # *required - replace value with correct <str>
# "street": "str", # remove or replace value with correct <str>
# "city": "str", # remove or replace value with correct <str>
# "zipcode": "str", # remove or replace value with correct <str>
# "phone": {
# "phone-type": "str", # *required - replace value with correct <str>
# "phone-number": "str", # *required - replace value with correct <str>
# },
# },
# ]
return_data = test_data
# Save return data
res = silcb.add_return_data(data=return_data)
# Return status
return res
PY-SIL EDIT3 Callback
The PY-SIL supports only EDIT3 callback for edit operations (EDIT1 and EDIT2 are not supported in PY-SIL).
The EDIT3 callback provides a more streamlined and efficient approach to handling YANG model-driven configurations. The key feature of EDIT3 in PY-SIL is its utilization of the 'update_json' value, which combines new user inputs with existing data, simplifying the process of modifying configuration data nodes.
Key Features of EDIT3 in PY-SIL:
Enhanced Data Management: EDIT3 callbacks facilitate more effective management of configuration data by consolidating new and existing values into a single 'update_json' structure. This approach reduces complexity and potential errors in processing configuration changes.
Efficiency in Processing: The integration of the 'update_json' value in EDIT3 callbacks minimizes the need for separate handling of new and current values, enabling more efficient processing of configuration changes.
Streamlined Callback Implementation: By eliminating the need for 'child_edit' and reducing redundancy, EDIT3 in PY-SIL provides a cleaner and more concise callback implementation compared to previous callback generations.
Common information about EDIT3 related to the server is available in EDIT3 Callback.
PY-SIL EDIT3 Callback Method
- def edit_method_name(self, silcb: SILCbEdit)
Used to provide a callback sub-mode for a specific named complex object
- Parameters:
silcb (SILCbEdit) -- Edit control block that contains full EDIT3 callback information
- Returns:
status
- Return type:
STATUS_T
PY-SIL EDIT3 Control Block
- class pysil.module.SILCbEdit(editcb: LP_STRUCT_AGT_EDITCB_T_, module_name: str, callback_info: dict)
Bases:
SILCbBase
The
SILCbEdit
class is a comprehensive control block used in EDIT3 callbacks. It encapsulates all necessary pointers and information required for the execution of the EDIT3 callback methods.- property ancestor_keys: dict
Ancestors keys for silcb
- property callback_info: dict
Property to get callback_info
- property cbtyp: c_uint
Indicates the reason for the callback, which could be one of several types from AGT_CBTYP_T, such as:
AGT_CB_VALIDATE
AGT_CB_APPLY
AGT_CB_COMMIT
AGT_CB_ROLLBACK
- property client_addr: str
This field contains the client address involved in the transaction. or None
- property curval: LP_STRUCT_VAL_VALUE_T_
This field refers to the current container/list values from either the <running> or <candidate> configuration, which could be None for certain operations like create.
- property curval_json: dict
This field refers to the current container/list values from either the <running> or <candidate> configuration, which could be None for certain operations like create.
- property editop: c_ubyte
This enumerator represents the parent edit-config operation type, affecting how the callback operates on objects. Possible types from OP_EDITOP_T include:
OP_EDITOP_LOAD
OP_EDITOP_MERGE
OP_EDITOP_REPLACE
OP_EDITOP_CREATE
OP_EDITOP_DELETE
- property local_keys: dict
Local keys for silcb
- log(msg: str, log_level: str = 'INFO') None
Adds message to log
- property module_name: str
- property msg: LP_STRUCT_RPC_MSG_T_
This pointer refers to the incoming RPC message in progress, detailing the specific request being processed.
- property msg_id: str
txid_str value or None
- property newval: LP_STRUCT_VAL_VALUE_T_
This field contains object that holds the proposed changes to be applied to the current configuration, varying based on the editop value.
- property newval_json: dict
This field contains object that holds the proposed changes to be applied to the current configuration, varying based on the editop value.
- property scb: LP_STRUCT_SES_CB_T_
This field contains a pointer to the session control block that initiated the request, representing the current session's context.
- send_terminal_message(message: str) ENUM_STATUS_T_
Sends message to terminal
- property update: LP_STRUCT_VAL_VALUE_T_
This field contains a merged value of all new and current values from the datastore, streamlining the update process.
- property update_json: dict
This field contains a merged value of all new and current values from the datastore, streamlining the update process.
- property username: str
user_id value or None
- property utils: PYSILUtils
returns utils for the silcb
This control block is pivotal in managing the intricacies of EDIT3 callbacks, ensuring that all necessary data and context are readily available for efficient processing.
PY-SIL EDIT3 Callback Initialization and Cleanup
PY-SIL library provides functionality to register callback methods based on a configuration provided in a dictionary format. It register all callback specified in configuration during init phase 1 and unregister them during cleanup phase.
Example 1
config = {
# ...
callbacks: [
# ...
{"path": "/if:interfaces", "cb_edit3": "edit_interfaces"},
]
}
"path": "/if:interfaces" This specifies the path where the callback method "edit_interfaces" should be invoked.
"cb_edit3": "edit_interfaces" This specifies the name of the callback method that should be invoked when a request is made to the specified path. In this case, the method named "edit_interfaces" will be invoked.
Example 2
config = {
# ...
callbacks: [
# ...
{
"path": "/if:interfaces/interface",
"cb_edit3": "edit_interface",
"lvl": 3,
"keys": [
{"name": "name", "pname": "name", "type": "string", "lvl": 3}
],
},
]
}
"path": "/if:interfaces/interface" This specifies the path where the callback method "edit_interface" should be invoked.
"cb_edit3": "edit_interface" This specifies the name of the callback method that should be invoked when a request is made to the specified path. In this case, the method named "edit_interface" will be invoked.
"lvl": 3 This specifies the level in the node's tree for the callback.
"keys": [] This specifies list of keys (ancestor keys and local keys) for the callback. Each key has:
name - yang name for a node.
pname - python parameter name in keys list.
type - type of the key.
lvl - level in the node's tree for the key node.
PY-SIL EDIT3 Callback Example
An example implementation of an EDIT3 callback method demonstrates how the 'update_json' value is utilized in various editing operations.
The following code snippet provides an example of how to implement the EDIT3 callback in a practical scenario, specifically focusing on managing interface entries within a network device configuration context.
The edit3_interface callback serves as the main EDIT3 callback handler for interface edits. It is structured to manage different phases of the callback process, aligning actions with the specified operation type (editop).
Key functionalities of this callback include:
Callback Information Handling: The callback handling begins by extracting relevant information from the editcb control block
Handling Different Callback Types: Based on the cbtyp, the callback executes different actions. For example, in the
AGT_CB_COMMIT
phase, it determines whether to invoke delete_interface or update_interface based on whether the edit operation is a delete or an update.Logging and Error Handling: The callback includes provisions for logging and error handling, ensuring that each action taken is properly recorded and any issues are promptly addressed.
This example illustrates the practical application of the EDIT3 callback in a network configuration context. It showcases how the update value can be used to efficiently manage changes to the device’s configuration, either by updating existing entries or deleting them as required.
class PYSILModuleIetfInterfaces(PYSILModuleIetfInterfacesBase):
# ...
def edit_interface(self, silcb: SILCbEdit):
"""
Edit database object callback
Path: list /if:interfaces/interface
Parameters
----------
silcb:SILCbEdit
Control block
Returns
-------
STATUS_T
Return status for the phase.
Additional
----------
silcb.local_keys: dict with local keys
silcb.local_keys["name"]: str
Local key leaf 'name' in list 'interface' Path: /if:interfaces/interface/name
"""
self.log(f"Enter 'edit3_interface' callback ietf-interfaces@2018-02-20 for {AGT_CBTYP_T(silcb.cbtyp).name} phase silcb.editop={OP_EDITOP_T(silcb.editop).name} local_keys={silcb.local_keys})
res = STATUS_T.NO_ERR
if silcb.cbtyp == AGT_CBTYP_T.AGT_CB_VALIDATE:
# description-stmt validation here
pass
elif silcb.cbtyp == AGT_CBTYP_T.AGT_CB_APPLY:
# database manipulation done here
pass
elif silcb.cbtyp == AGT_CBTYP_T.AGT_CB_COMMIT:
# device instrumentation done here
if silcb.editop == OP_EDITOP_T.OP_EDITOP_DELETE:
# Delete all data from the device
self.log(f"Delete entry")
res = delete_interface(silcb.local_keys["name"])
else:
# Use silcb.update_json to update your device data
#
# The technique here is to replace the existing data
# with this composite Update value.
self.log(f"UPDATE entry with {silcb.update_json}")
res = update_interface(silcb.update_json, silcb.local_keys["name"])
elif silcb.cbtyp == AGT_CBTYP_T.AGT_CB_ROLLBACK:
# undo device instrumentation here
pass
else:
return self.flag_internal_error()
return res
PY-SIL EDIT3 Update Example
The update_interface function modifies or creates an interface in the interfaces dictionary based on the provided key and data. It takes two parameters:
key: A string representing the key of the interface to update or create.
data: A dictionary containing the updated data for the interface.
If the interface associated with the given key already exists in the interfaces dictionary, its data is updated with the provided data. If the interface does not exist (i.e., the key is not found in the dictionary), a new entry is created in the interfaces dictionary with the provided data.
The function returns STATUS_T.NO_ERR to indicate a successful update or creation of the interface. Since the function always performs either an update or a creation, it always returns STATUS_T.NO_ERR. This function allows for the management of interface data by updating existing interfaces or creating new ones if they do not already exist.
# Example dictionary-like structure
interfaces = {
"interface1": {"name": "interface1", "type": "ethernet", "enabled": 1},
"interface2": {"name": "interface2", "type": "wifi", "enabled": 2},
"interface3": {"name": "interface3", "type": "ethernet", "enabled": 1},
}
def update_interface(key, data):
"""
Update interface data based on the given key.
Parameters
----------
key : str
The key of the interface to update.
data : dict
A dictionary containing the updated data for the interface.
Returns
-------
STATUS_T
True if the update is successful.
"""
if key in interfaces:
interfaces[key].update(data)
else:
interfaces[key] = data
return STATUS_T.NO_ERR
PY-SIL EDIT3 Delete Example
The delete_interface function removes an interface from the interfaces dictionary based on the provided key. It takes one parameter:
key: A string representing the key of the interface to delete. If the interface associated with the given key exists in the interfaces dictionary, it is removed, and the function returns STATUS_T.NO_ERR to indicate a successful deletion. If the interface does not exist (i.e., the key is not found in the dictionary), the function returns STATUS_T.ERR_NCX_NOT_FOUND.
The function provides the flexibility to manage the interface data by allowing the deletion of specific interfaces when they are no longer needed.
# Example dictionary-like structure
interfaces = {
"interface1": {"name": "interface1", "type": "ethernet", "enabled": 1},
"interface2": {"name": "interface2", "type": "wifi", "enabled": 2},
"interface3": {"name": "interface3", "type": "ethernet", "enabled": 1},
}
def delete_interface(key):
"""
Delete an interface based on the given key.
Parameters
----------
key : str
The key of the interface to delete.
Returns
-------
STATUS_T
NO_ERR if deletion is successful, ERR_NCX_NOT_FOUND otherwise (e.g., key not found).
"""
if key in interfaces:
del interfaces[key]
return STATUS_T.NO_ERR
else:
return STATUS_T.ERR_NCX_NOT_FOUND
PY-SIL RPC Callback
Any new operation can be added by defining a new YANG RPC statement in a module, and providing the proper PY-SIL code.
Common information about RPC callbacks related to the server is available in RPC Callbacks.
PY-SIL RPC Validate Callback
- def validate_method_name(self, silcb: SILCbValidate)
The RPC validate callback method is optional to use. Its purpose is to validate any aspects of an RPC operation, beyond the constraints checked by the server engine. Only 1 validate method can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. There is usually zero or one of these callback method for every 'rpc' statement in the YANG module associated with the SIL code.
The PY-SIL Code Generation will generate validate methods with comments to simplify implementation.
- Parameters:
silcb (SILCbValidate) -- RPC Validate control block contains all the input, output, and state data used for RPC transactions.
- Returns:
STATUS_T.NO_ERR if executed OK
- Return type:
STATUS_T
PY-SIL RPC Invoke Callback
- def invoke_method_name(self, silcb: SILCbInvoke)
The RPC invoke callback method is used to perform the operation requested by the client session. Only 1 invoke method can register for each YANG rpc statement. The standard NETCONF operations are reserved by the server engine. There is usually one of these callback methods for every 'rpc' statement in the YANG module associated with the SIL code.
The RPC invoke callback method is optional to use, although if no invoke callback is provided, then the operation will have no affect. Normally, this is only the case if the module is be tested by an application developer, using netconfd-pro as a server simulator.
The PY-SIL Code Generation will generate validate methods with comments to simplify implementation.
- Parameters:
silcb (SILCbInvoke) -- RPC Invoke control block contains all the input, output, and state data used for RPC transactions. It's basically the same as RPC Validate control block with added add_return_data() method to make it possible to add return data if any.
- Returns:
STATUS_T.NO_ERR if executed OK
- Return type:
STATUS_T
PY-SIL RPC Validate Callback Control Block
- class pysil.module.SILCbValidate(scb: LP_STRUCT_SES_CB_T_, msg: LP_STRUCT_RPC_MSG_T_, methnode: LP_STRUCT_XML_NODE_T_, module_name: str, callback_info: dict, nsid: int)
Bases:
SILCbValidateInvokeBase
The
SILCbValidate
class is a comprehensive control block used in RPC Validate callbacks. It encapsulates all necessary pointers and information required for the execution of the RPC Validate callback methods.- property callback_info: dict
Property to get callback_info
- property client_addr: str
This field contains the client address involved in the transaction. or None
- property errorval
Property errorval recorded by set_errorval method
- property input_params: dict
Returns dict with all parameters for callback object
- log(msg: str, log_level: str = 'INFO') None
Adds message to log
- property methnode: LP_STRUCT_XML_NODE_T_
This pointer refers to the XML-specific node being parsed if available. This can be used for error reporting if no 'val' or 'obj' parameters are available (from the request message).
- property module_name: str
- property msg: LP_STRUCT_RPC_MSG_T_
This pointer refers to the incoming RPC message in progress, detailing the specific request being processed.
- property nsid: int
namespace ID for silcb
- property scb: LP_STRUCT_SES_CB_T_
This field contains a pointer to the session control block that initiated the request, representing the current session's context.
- send_terminal_message(message: str) ENUM_STATUS_T_
Sends message to terminal
- set_errorval(data)
Use method if you would like to set an errorval when error returned. The errorval will be recorded to the server.
- property username: str
user_id value or None
- property utils: PYSILUtils
returns utils for the silcb
This control block is pivotal in managing the intricacies of RPC Validate callbacks, ensuring that all necessary data and context are readily available for efficient processing.
PY-SIL RPC Invoke Callback Control Block
- class pysil.module.SILCbInvoke(scb: LP_STRUCT_SES_CB_T_, msg: LP_STRUCT_RPC_MSG_T_, methnode: LP_STRUCT_XML_NODE_T_, module_name: str, callback_info: dict, nsid: int)
Bases:
SILCbValidate
The
SILCbInvoke
class is a comprehensive control block used in RPC Invoke callbacks. It encapsulates all necessary pointers and information required for the execution of the RPC Invoke callback methods.- add_return_data(data: dict) ENUM_STATUS_T_
Adds data to return
- Parameters:
data (dict) -- provide return data dict here
- Returns:
Status
- property callback_info: dict
Property to get callback_info
- property client_addr: str
This field contains the client address involved in the transaction. or None
- property errorval
Property errorval recorded by set_errorval method
- property input_params: dict
Returns dict with all parameters for callback object
- log(msg: str, log_level: str = 'INFO') None
Adds message to log
- property methnode: LP_STRUCT_XML_NODE_T_
This pointer refers to the XML-specific node being parsed if available. This can be used for error reporting if no 'val' or 'obj' parameters are available (from the request message).
- property module_name: str
- property msg: LP_STRUCT_RPC_MSG_T_
This pointer refers to the incoming RPC message in progress, detailing the specific request being processed.
- property nsid: int
namespace ID for silcb
- property scb: LP_STRUCT_SES_CB_T_
This field contains a pointer to the session control block that initiated the request, representing the current session's context.
- send_terminal_message(message: str) ENUM_STATUS_T_
Sends message to terminal
- set_errorval(data)
Use method if you would like to set an errorval when error returned. The errorval will be recorded to the server.
- property username: str
user_id value or None
- property utils: PYSILUtils
returns utils for the silcb
This control block is pivotal in managing the intricacies of RPC Invoke callbacks, ensuring that all necessary data and context are readily available for efficient processing. It's basically the same as SILCbValidate with added method add_return_data().
PY-SIL RPC Callback Initialization and Cleanup
PY-SIL library provides functionality to register callback based on a configuration provided in a dictionary format. It register all callback specified in configuration during init phase 1 and unregister them during cleanup phase.
config = {
# ...
callbacks: [
# ...
{
"path": "/toast:make-toast",
"cb_validate": "validate_make_toast",
"cb_invoke": "invoke_make_toast",
"input_params": [
{
"name": "toasterDoneness",
"type": "uint32",
},
{
"name": "toasterToastType",
"type": "identityref",
},
],
},
]
}
"path": "/toast:make-toast" This specifies the path where the callback "validate_make_toast" or "invoke_make_toast" should be called.
"cb_validate": "validate_make_toast" This specifies the name of the validate callback method that should be called when a request is made to the specified path. In this case, the callback named "validate_make_toast" will be called.
"cb_invoke": "invoke_make_toast" This specifies the name of the invoke callback method that should be called when a request is made to the specified path. In this case, the callback named "invoke_make_toast" will be called.
"input_params": [] This specifies list of input parameters for the callback. Each parameter has:
name - yang name for a node.
type - type of the parameter.
PY-SIL RPC Callback Examples
Example RPC YANG definition:
rpc make-toast {
description
"Make some toast.
The toastDone notification will be sent when
the toast is finished.
An 'in-use' error will be returned if toast
is already being made.
A 'resource-denied' error will be returned
if the toaster service is disabled.";
input {
leaf toasterDoneness {
type uint32 {
range "1 .. 10";
}
default 5;
description
"This variable controls how well-done is the
ensuing toast. It should be on a scale of 1 to 10.
Toast made at 10 generally is considered unfit
for human consumption; toast made at 1 is warmed
lightly.";
}
leaf toasterToastType {
type identityref {
base toast:toast-type;
}
default toast:wheat-bread;
description
"This variable informs the toaster of the type of
material that is being toasted. The toaster
uses this information, combined with
toasterDoneness, to compute for how
long the material must be toasted to achieve
the required doneness.";
}
}
}
Example PY-SIL RPC Validate Method:
class PYSILModuleToaster(PYSILModuleToasterBase):
# ...
def validate_make_toast(self, silcb: SILCbValidate):
"""
Validation phase callback for "<validate_make_toast>" operation.
All YANG constraints have passed at this point.
Add description-stmt checks in this function.
Path: rpc /toast:make-toast
Description:
Make some toast.
The toastDone notification will be sent when
the toast is finished.
An 'in-use' error will be returned if toast
is already being made.
A 'resource-denied' error will be returned
if the toaster service is disabled.
Parameters
----------
silcb: SILCbValidate
Control block
Returns
-------
STATUS_T
Return status for the phase.
An error in validate phase will cancel invoke phase
An rpc-error will be added if an error is returned and the msg error Q is empty
Additional
----------
silcb.input_params: dict with input parameters
silcb.input_params["toasterDoneness"]: int
This variable controls how well-done is the
ensuing toast. It should be on a scale of 1 to 10.
Toast made at 10 generally is considered unfit
for human consumption; toast made at 1 is warmed
lightly.
silcb.input_params["toasterToastType"]: identityref
This variable informs the toaster of the type of
material that is being toasted. The toaster
uses this information, combined with
toasterDoneness, to compute for how
long the material must be toasted to achieve
the required doneness.
"""
self.log(
f"Enter 'validate_make_toast' callback toaster@2009-11-20 input_params={silcb.input_params}"
)
res = STATUS_T.NO_ERR
if self.toaster_state.enabled:
# toaster service enabled, check if in use
if self.toaster_state.toasting:
res = STATUS_T.ERR_NCX_IN_USE
else:
# this is where a check on bread inventory would go
# this is where a check on toaster HW ready would go
pass
else:
# toaster service disabled
res = STATUS_T.ERR_NCX_RESOURCE_DENIED
# if you have an error you also could set errorval
# silcb.set_errorval("testerrorval")
return res
Example PY-SIL RPC Invoke Method:
class PYSILModuleToaster(PYSILModuleToasterBase):
# ...
def invoke_make_toast(self, silcb: SILCbInvoke):
"""
Invocation phase callback for "<invoke_make_toast>" operation.
Validation callback has passed at this point.
Call device instrumentation code in this function.
Path: rpc /toast:make-toast
Description:
Make some toast.
The toastDone notification will be sent when
the toast is finished.
An 'in-use' error will be returned if toast
is already being made.
A 'resource-denied' error will be returned
if the toaster service is disabled.
Parameters
----------
silcb: SILCbInvoke
Control block
Returns
-------
STATUS_T
Return status for the phase.
An error in validate phase will cancel invoke phase
An rpc-error will be added if an error is returned and the msg error Q is empty
Additional
----------
silcb.input_params: dict with input parameters
silcb.input_params["toasterDoneness"]: int
This variable controls how well-done is the
ensuing toast. It should be on a scale of 1 to 10.
Toast made at 10 generally is considered unfit
for human consumption; toast made at 1 is warmed
lightly.
silcb.input_params["toasterToastType"]: identityref
This variable informs the toaster of the type of
material that is being toasted. The toaster
uses this information, combined with
toasterDoneness, to compute for how
long the material must be toasted to achieve
the required doneness.
"""
self.log(
f"Enter 'invoke_make_toast' callback toaster@2009-11-20 input_params={silcb.input_params}"
)
res = STATUS_T.NO_ERR
toaster_doneness = silcb.input_params["toasterDoneness"]
# make sure the toaster_doneness value is set
if toaster_doneness is None:
toaster_doneness = 5 # set the default
# arbitrary formula to convert toaster doneness to the
# number of seconds the toaster should be on
# In the example each doneness is 12 seconds
toaster_duration = toaster_doneness * 12
# this is where the code would go to adjust the duration
# based on the bread type
self.log(f"toaster: starting toaster for {toaster_duration} seconds")
# this is where the code would go to start the toaster
# heater element
# start a timer to toast for the specified time interval
res, timer_id = self.timer_create(
self.toaster_timer_fn, seconds=toaster_duration
)
if res == STATUS_T.NO_ERR:
self.toaster_state.toasting = True
self.toaster_state.timer_id = timer_id
return res
PY-SIL RPC Data Output Handling Example
Example YANG Module:
module addrpc {
namespace "http://www.yumaworks.com/ns/addrpc";
prefix add;
revision "2020-02-25";
rpc add {
description "Get the sum of two numbers";
input {
leaf num1 {
type int32;
mandatory true;
description "First number to add";
}
leaf num2 {
type int32;
mandatory true;
description "Second number to add";
}
}
output {
leaf sum {
type int32;
mandatory true;
description "The sum of the 2 numbers";
}
}
}
}
Example PY-SIL RPC Invoke Method Returning Output Data:
class PYSILModuleAddrpc(PYSILModuleAddrpcBase):
# ...
def invoke_add(self, silcb: SILCbInvoke):
"""
Invocation phase callback for "<invoke_add>" operation.
Validation callback has passed at this point.
Call device instrumentation code in this function.
Path: rpc /add:add
Description: Get the sum of two numbers
Parameters
----------
silcb:SILCbInvoke
Control block
Returns
-------
STATUS_T
Return status for the phase.
An error in validate phase will cancel invoke phase
An rpc-error will be added if an error is returned and the msg error Q is empty
Additional
----------
silcb.input_params: dict with input parameters
silcb.input_params["num1"]: int - First number to add
silcb.input_params["num2"]: int - Second number to add
"""
self.log(
f"Enter 'invoke_add' callback addrpc@2020-02-25 input_params={silcb.input_params}"
)
res = STATUS_T.NO_ERR
# Calculate sum of num1 and num2 and return
result = silcb.input_params["num1"] + silcb.input_params["num2"]
return_data = {
"sum": result, # *required - replace value with correct <int>
}
# Save return data
res = silcb.add_return_data(data=return_data)
return res
PY-SIL Action Callback
A YANG Action is like an RPC operation in many ways except it is associated with the data node where the Action is defined. The PY-SIL code for an Action callback is a combination of the RPC callbacks and the EDIT callbacks. The call flow is the same as for RPC operations. The parameters are similar as well, except the ancestor keys for the instance are provided, so the PY-SIL callback knows which instance to apply the Action.
All YANG Actions are data-driven within the server, using the YANG Action statement for the operation and PY-SIL callbacks.
Any new data-specific Actions can be added by defining a new YANG Action statement within a container or list, and providing the proper PY-SIL code.
Common information about Action Callbacks related to the server is available in Action Callbacks.
PY-SIL Action Validate Callback
- def action_validate_method_name(self, silcb: SILCbActionValidate)
The action validate callback method is optional to use. Its purpose is to validate any aspects of an Action request, beyond the constraints checked by the server engine.
The validate callback is optional to use.
Validation could be done in the validate or invoke phase with the same result.
The PY-SIL Code Generation will generate Action validate methods with comments to simplify implementation.
- Parameters:
silcb (SILCbActionValidate) -- Action Validate control block contains all the input, output, and state data used for the transaction.
- Returns:
STATUS_T.NO_ERR if executed OK
- Return type:
STATUS_T
PY-SIL Action Invoke Callback
- def action_invoke_method_name(self, silcb: SILCbInvoke)
The action invoke callback method is used to perform the data-specific action requested by the client session.
The action invoke callback method is optional to use, although if no invoke callback is provided, then the action will have no affect.
The PY-SIL Code Generation will generate validate methods with comments to simplify implementation.
- param silcb:
Action Invoke control block contains all the input, output, and state data used for the transaction. It's basically the same as Action Validate control block with added add_return_data() callback to make it possible to add return data if any.
- type silcb:
- return:
STATUS_T.NO_ERR if executed OK
- rtype:
STATUS_T
PY-SIL Action Validate Callback Control Block
- class pysil.module.SILCbActionValidate(scb: LP_STRUCT_SES_CB_T_, msg: LP_STRUCT_RPC_MSG_T_, methnode: LP_STRUCT_XML_NODE_T_, actionval: LP_STRUCT_VAL_VALUE_T_, module_name: str, callback_info: dict, nsid: int)
Bases:
SILCbValidateInvokeBase
The
SILCbActionValidate
class is a comprehensive control block used in Action Validate callbacks. It encapsulates all necessary pointers and information required for the execution of the Action Validate callback methods.- property actionval: LP_STRUCT_VAL_VALUE_T_
Pointer to the input node represents the start of the specific action being invoked. The auto-generated SIL code can derive all the ancestor keys from this data structure.
- property ancestor_keys: dict
Ancestors keys for silcb
- property callback_info: dict
Property to get callback_info
- property client_addr: str
This field contains the client address involved in the transaction. or None
- property errorval
Property errorval recorded by set_errorval method
- property input_params: dict
Returns dict with all parameters for callback object
- log(msg: str, log_level: str = 'INFO') None
Adds message to log
- property methnode: LP_STRUCT_XML_NODE_T_
This pointer refers to the XML-specific node being parsed if available. This can be used for error reporting if no 'val' or 'obj' parameters are available (from the request message).
- property module_name: str
- property msg: LP_STRUCT_RPC_MSG_T_
This pointer refers to the incoming RPC message in progress, detailing the specific request being processed.
- property nsid: int
namespace ID for silcb
- property scb: LP_STRUCT_SES_CB_T_
This field contains a pointer to the session control block that initiated the request, representing the current session's context.
- send_terminal_message(message: str) ENUM_STATUS_T_
Sends message to terminal
- set_errorval(data)
Use method if you would like to set an errorval when error returned. The errorval will be recorded to the server.
- property username: str
user_id value or None
- property utils: PYSILUtils
returns utils for the silcb
This control block is pivotal in managing the intricacies of Action Validate callbacks, ensuring that all necessary data and context are readily available for efficient processing.
PY-SIL Action Invoke Callback Control Block
- class pysil.module.SILCbActionInvoke(scb: LP_STRUCT_SES_CB_T_, msg: LP_STRUCT_RPC_MSG_T_, methnode: LP_STRUCT_XML_NODE_T_, actionval: LP_STRUCT_VAL_VALUE_T_, module_name: str, callback_info: dict, nsid: int)
Bases:
SILCbActionValidate
The
SILCbActionInvoke
class is a comprehensive control block used in Action Invoke callbacks. It encapsulates all necessary pointers and information required for the execution of the Action Invoke callback methods.- property actionval: LP_STRUCT_VAL_VALUE_T_
Pointer to the input node represents the start of the specific action being invoked. The auto-generated SIL code can derive all the ancestor keys from this data structure.
- add_return_data(data)
Adds data to return
- Parameters:
data (dict) -- provide return data dict here
- Returns:
Status
- property ancestor_keys: dict
Ancestors keys for silcb
- property callback_info: dict
Property to get callback_info
- property client_addr: str
This field contains the client address involved in the transaction. or None
- property errorval
Property errorval recorded by set_errorval method
- property input_params: dict
Returns dict with all parameters for callback object
- log(msg: str, log_level: str = 'INFO') None
Adds message to log
- property methnode: LP_STRUCT_XML_NODE_T_
This pointer refers to the XML-specific node being parsed if available. This can be used for error reporting if no 'val' or 'obj' parameters are available (from the request message).
- property module_name: str
- property msg: LP_STRUCT_RPC_MSG_T_
This pointer refers to the incoming RPC message in progress, detailing the specific request being processed.
- property nsid: int
namespace ID for silcb
- property scb: LP_STRUCT_SES_CB_T_
This field contains a pointer to the session control block that initiated the request, representing the current session's context.
- send_terminal_message(message: str) ENUM_STATUS_T_
Sends message to terminal
- set_errorval(data)
Use method if you would like to set an errorval when error returned. The errorval will be recorded to the server.
- property username: str
user_id value or None
- property utils: PYSILUtils
returns utils for the silcb
This control block is pivotal in managing the intricacies of Action Invoke callbacks, ensuring that all necessary data and context are readily available for efficient processing. It's basically the same as SILCbActionValidate with added method add_return_data().
PY-SIL Action Callback Initialization and Cleanup
PY-SIL library provides functionality to register callbacks based on a configuration provided in a dictionary format. It register all callback specified in configuration during Init phase 1 and unregister them during cleanup phase.
config = {
# ...
callbacks: [
# ...
{
"path": "/exa:server/reset",
"cb_action_validate": "action_validate_reset",
"cb_action_invoke": "action_invoke_reset",
"lvl": 3,
"keys": [
{
"name": "name",
"pname": "server__name",
"type": "string",
"lvl": 2,
}
],
"input_params": [
{
"name": "reset-msg",
"type": "string",
}
],
},
]
}
"path": "/exa:server/reset" This specifies the path where the callback "action_validate_reset" or "action_invoke_reset" should be called.
"cb_action_validate": "action_validate_reset" This specifies the name of the validate callback method that should be called when a request is made to the specified path. In this case, the callback named "action_validate_reset" will be called.
"cb_action_invoke": "action_invoke_reset" This specifies the name of the invoke callback method that should be called when a request is made to the specified path. In this case, the callback named "action_invoke_reset" will be called.
"keys": [] This specifies list of keys for the callback. Each key has:
name - yang name for a node.
pname - python parameter name in keys list.
type - type of the key.
lvl - level in the node's tree for the key node.
"input_params": [] This specifies list of input parameters for the callback. Each parameter has:
name - yang name for a node.
type - type of the parameter.
PY-SIL Action Callback Examples
Example Action YANG definition:
module ex-action {
yang-version 1.1;
namespace "http://netconfcentral.org/ns/ex-action";
prefix exa;
import ietf-yang-types { prefix yang; }
revision 2020-03-06;
list server {
key name;
leaf name {
type string;
description "Server name";
}
action reset {
input {
leaf reset-msg {
type string;
description "Log message to print before server reset";
}
}
output {
leaf reset-finished-at {
type yang:date-and-time;
description "Time the reset was done on the server";
}
}
}
}
}
Example PY-SIL Action Validate Method:
class PYSILModuleExAction(PYSILModuleExActionBase):
# some methods here
def action_validate_reset(self, silcb: SILCbActionValidate):
"""
YANG 1.1 action validate callback.
Path: action /exa:server/reset
Parameters
----------
silcb: SILCbActionValidate
Control block
Returns
-------
STATUS_T
Return status for the phase.
An error in validate phase will cancel invoke phase
An rpc-error will be added if an error is returned and the msg error Q is empty
Additional
----------
silcb.ancestor_keys: dict with ancestor keys
silcb.ancestor_keys["server__name"]: str
Ancestor key leaf 'name' in list 'server' Path: /exa:server/name
silcb.input_params: dict with input parameters
silcb.input_params["reset-msg"]: str
Log message to print before server reset
"""
self.log(
f"Enter 'action_validate_reset' callback ex-action@2020-03-06 ancestor_keys={silcb.ancestor_keys} input_params={silcb.input_params}"
)
res = STATUS_T.NO_ERR
"""
Ancestor keys are available in silcb.ancestor_keys dict
silcb.ancestor_keys["server__name"]: str # Path: /exa:server/name
"""
# the validate function would check here if it is OK to
# reset the server right now; if not an error would be
# returned by setting res to the correct error status.
# If the input parameter is the problem then set errorval
# silcb.set_errorval("testerrorval")
# otherwise leave errorval NULL
return res
Example PY-SIL Action Invoke Method:
class PYSILModuleExAction(PYSILModuleExActionBase):
# some methods here
def action_invoke_reset(self, silcb: SILCbActionInvoke):
"""
YANG 1.1 action invoke callback.
Path: action /exa:server/reset
Parameters
----------
silcb: SILCbActionInvoke
Control block
Returns
-------
STATUS_T
Return status for the phase.
An error in validate phase will cancel invoke phase
An rpc-error will be added if an error is returned and the msg error Q is empty
Additional
----------
silcb.ancestor_keys: dict with ancestor keys
silcb.ancestor_keys["server__name"]: str
Ancestor key leaf 'name' in list 'server' Path: /exa:server/name
silcb.input_params: dict with input parameters
silcb.input_params["reset-msg"]: str
Log message to print before server reset
"""
self.log(
f"Enter 'action_invoke_reset' callback ex-action@2020-03-06 ancestor_keys={silcb.ancestor_keys} input_params={silcb.input_params}"
)
# display the reset logging message
self.log(
f'ex-action: Resetting server {silcb.ancestor_keys["server__name"]}. MSG: {silcb.input_params["reset-msg"]}'
)
res = STATUS_T.NO_ERR
# the invoke function would schedule or execute the server reset here
"""
Ancestor keys are available in silcb.ancestor_keys dict
silcb.ancestor_keys["server__name"]: str # Path: /exa:server/name
"""
# Item found. Create a python object (dict, list, etc) with item data
from datetime import datetime, timezone
return_data = {
"reset-finished-at": datetime.now(
timezone.utc
).isoformat(), # remove or replace value with correct <yang:date-and-time>
}
# Save return data
res = silcb.add_return_data(data=return_data)
return res
PY-SIL Action Data Output Handling
YANG Actions can return data to the client if the operation succeeds. The YANG “output” statement defines the return data for each YANG Action. Constructing YANG data is covered in detail elsewhere. The previous example from PY-SIL Action Invoke Callback shows a simple example PY-SIL Action invoke callback method that returns data.
This procedure is the same for PY-SIL RPC output handling. Refer to the PY-SIL RPC Data Output Handling Example section and follow these procedures.
PY-SIL Notifications
The PY-SIL Code Generation will automatically generate methods to queue a specific notification type for processing. It is up to the PY-SIL callback code to invoke this callback when the notification event needs to be generated. The PY-SIL code is expected to provide the values that are needed for any notification data dictionary.
The send_notification(...) method is not a callback. Its purpose is to generate a specific event, but the same method could be used for multiple event types. If the notification-stmt for the event type defines child nodes then the send method is expected to receive this data (in dict) when the method is called.
PY-SIL can include an Event Stream Callback function to know if any clients are listening on the relevant event stream. If no subscriptions are active, then the notification send processing logic can decide to suppress generation of certain events.
PY-SIL Send Notification
This method is not called by the server directly. It can be changed or not used at all to generate a notification event.
- def send_notification(self, name:str, data:dict=None, stream_name:str=None) -> None
Send a notification-event to the main server.
- Parameters:
name (str) -- Notification name.
data (dict) -- Dictionary with notification nodes data if any.
stream_name (str) -- A stream name to send notification to. "None" means default (netconf).
- Returns:
None
- Return type:
None
Send notification for PY-SIL
Send to the default or configured event stream for PY-SIL (PYSILModuleBase).
class PYSILModuleToaster(PYSILModuleToasterBase):
#...
def send_toastDone(self, data: dict):
#...
self.send_notification(name="toastDone", data=data)
Send notification for PY-SIL to a Specific Event Stream
class PYSILModuleToaster(PYSILModuleToasterBase):
#...
def send_toastDone(self, data: dict):
#...
self.send_notification(name="toastDone", data=data, stream_name="stream1")
PY-SIL Notification Send Example
In the example below, the 'toastDone' notification event contains just one leaf, called the 'toastStatus'. There is PY-SIL timer callback code which calls this method, and provides the final toast status, after the <make-toast> operation has been completed or canceled.
class PYSILModuleToaster(PYSILModuleToasterBase):
# ...
def invoke_make_toast(self, silcb: SILCbInvoke):
"""Method to start make a toast (start timer)"""
# ...
res = STATUS_T.NO_ERR
# ...
# start a timer to toast for the specified time interval (20 seconds here)
# call self.toaster_timer_fn(...) in 20 seconds
res, timer_id = self.timer_create(self.toaster_timer_fn, seconds=20)
if res == STATUS_T.NO_ERR:
self.toaster_state.toasting = True
self.toaster_state.timer_id = timer_id
return res
def toaster_timer_fn(self, timer_id: int, cookie: dict):
"""Custom added method to process timer events"""
if self.toaster_state.timer_id != timer_id:
self.log("Got wrong timer_id for toaster")
return 0
# toast is finished
self.toaster_state.toasting = False
self.toaster_state.timer_id = 0
self.log(f"toaster[{self.toaster_state.id}] is finished")
# send notification with toastStatus is "done"
self.send_toastDone({"toastStatus": "done"})
return 0
def send_toastDone(self, data: dict):
"""
Send a "<toastDone>" notification.
Path: /toast:toastDone
Called by your code when notification event occurs.
This API is optional. It can be replaced with
any function of your choice that provides the same functionality.
Create an internal notification message (agt_not_msg_t) and queue
it for delivery to client sessions.
The parameters depend in the data definitions within the
notification-stmt for this event type.
Parameters
----------
data: dict
Notification data in format:
data = {
"toastStatus": None, # remove or replace value with correct <int>
}
example: self.send_toastDone({"toastStatus": "done"})
Returns
-------
None
"""
self.log(f"Enter 'send_toastDone' notification toaster@2009-11-20 data={data}")
self.send_notification(name="toastDone", data=data)