PY-SIL Code Generation

The py-sil-gen.py plugin for pyang library can be used to generate PY-SIL code-stub files for the netconfd-pro server.

pyang is a YANG validator, code generator, and transformation tool written in python. It can be used to validate YANG modules for correctness, to transform YANG modules into other formats, and to write plugins to generate code from the modules.

Currently generation of EDIT3 ,GET2, RPC, ACTION and NOTIFICATION callbacks are supported.

Usage Requirements

  • Minimum python version is 3.8.10

  • Minimum pyang version 2.6.0

pyang python library should be installed, it can be done using following command:

> pip install pyang

When server is built with WITH_PY_SIL option, plugin will be installed into pyang library plugins directory.

Usage

pyang [--plugindir] [--libsavedir] [--bundle] [--deviation-module] [--path] [-f py-sil] file...

Only options required for PY-SIL code generation is provided above. Refer to pyang documentation for the full options reference.

Options

--plugindir plugindir

Specify the directory containing py-sil-gen.py plugin. By default, installation process will try to install it into pyang plugins directory.

--libsavedir directory

Specify the directory for saving generated PY-SIL code.

--bundle bundlename

Specify the bundle name to be generated if you would like to combine modules in bundle.

--deviation-module

This option is used to specify a deviation module. This option can be given multiple times.

--path path

Path is a colon (:) separated list of directories to search for imported modules. This option may be given multiple times.

-f py-sil

Specify the output format as PY-SIL code.

file...

One or more file parameters may be given on the command line. They denote either YANG modules to be processed.

Usage Examples

To generate stub PY-SIL code for a single YANG file:

> pyang -f py-sil mymod.yang

To generate stub PY-SIL code using bundles:

> pyang -f py-sil --bundle mybundle mymod1.yang mymod2.yang mymod3.yang

To generate PY-SIL code for a single YANG file and apply deviations from another file:

> pyang -f py-sil --deviation-module mymod-devs.yang mymod.yang

Example of PY-SIL Code Generated by Plugin

Example below is generated with following command for toaster.yang module:

> pyang -f py-sil toaster.yang

Following file structure will be generated:

toaster/
    |-- u_toaster.py
    |-- y_toaster.py

Refer to PY-SIL Tutorial for more details on how to use this PY-SIL code.

Module toaster.yang

module toaster {

    namespace "http://netconfcentral.org/ns/toaster";

    prefix "toast";

    organization
        "Netconf Central";

    contact
        "Andy Bierman <[email protected]>";

    description
        "YANG version of the TOASTER-MIB.

     Copyright (c) 2009 Andy Bierman and the persons identified as
     authors of the code.  All rights reserved.

     Redistribution and use in source and binary forms, with or
     without modification, is permitted pursuant to, and subject
     to the license terms contained in, the BSD 3-Clause License
     http://opensource.org/licenses/BSD-3-Clause";

    revision 2009-11-20 {
        description "Toaster module in progress.";
    }

    identity toast-type {
        description
          "Base for all bread types supported by the toaster.
           New bread types not listed here nay be added in the 
           future.";
    }

    identity white-bread {
        description
          "White bread.";
        base toast:toast-type;
    }

    identity wheat-bread {
        description
          "Wheat bread.";
        base toast-type;
    }

    identity wonder-bread {
        description
          "Wonder bread.";
        base toast-type;
    }

    identity frozen-waffle {
        description
          "Frozen waffle.";
        base toast-type;
    }

    identity frozen-bagel {
        description
          "Frozen bagel.";
        base toast-type;
    }

    identity hash-brown {
        description
          "Hash browned potatos.";
        base toast-type;
    }

    typedef DisplayString {
        description
          "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION.";
        reference "RFC 2579, section 2.";
        type string {
            length "0 .. 255";
        }
    }

    container toaster {
        presence
          "Indicates the toaster service is available";

        description
          "Top-level container for all toaster database objects.";

        leaf toasterManufacturer {
            type DisplayString;
            config false;
            mandatory true;
            description 
              "The name of the toaster's manufacturer. For instance, 
                Microsoft Toaster.";
        }
 
        leaf toasterModelNumber {
            type DisplayString;
            config false;
            mandatory true;
            description
              "The name of the toaster's model. For instance,
               Radiant Automatic.";
        }

        leaf toasterStatus {
            type enumeration {
                enum up {
                  value 1;
                  description
                    "The toaster knob position is up.
                      No toast is being made now.";
                }
                enum down {
                  value 2;
                  description
                    "The toaster knob position is down.
                      Toast is being made now.";

                }
            }
            config false;
            mandatory true;
            description
              "This variable indicates the current state of 
               the toaster.";
        }
    }

    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.";
            }
        }
    }

    rpc cancel-toast {
        description
          "Stop making toast, if any is being made.
           A 'resource-denied' error will be returned 
           if the toaster service is disabled.";
    }

    notification toastDone {
        description
          "Indicates that the toast in progress has completed.";

        leaf toastStatus {
           description
             "Indicates the final toast status";
           type enumeration {
               enum done {
                  description
                    "The toast is done.";
               }
               enum cancelled {
                  description
                    "The toast was cancelled.";
               }
               enum error {
                  description
                    "The toaster service was disabled or
                     the toaster is broken.";
               }
            }
        }
    }
               
                       
/*************************************************************

   Original TOASTER-MIB

TOASTER-MIB DEFINITIONS ::= BEGIN

IMPORTS
        enterprises
                FROM RFC1155-SMI
        OBJECT-TYPE
                FROM RFC-1212
        DisplayString
                FROM RFC-1213;

epilogue        OBJECT IDENTIFIER ::= {enterprises 12}
toaster         OBJECT IDENTIFIER ::= {epilogue 2}


toasterManufacturer OBJECT-TYPE
  SYNTAX  DisplayString
  ACCESS  read-only
  STATUS  mandatory
  DESCRIPTION
          "The name of the toaster's manufacturer. For  instance, 
          Microsoft Toaster."
  ::= {toaster 1}

toasterModelNumber OBJECT-TYPE
  SYNTAX  DisplayString
  ACCESS  read-only
  STATUS  mandatory
  DESCRIPTION
          "The name of the toaster's model. For instance,
          Radiant Automatic."
  ::= {toaster 2}

toasterControl OBJECT-TYPE
  SYNTAX  INTEGER  {up (1), down (2)}
  ACCESS  read-write
  STATUS  mandatory
  DESCRIPTION
          "This variable controls the current state of the toaster.
           To begin toasting, set it to down (2). To abort toasting 
          (perhaps in the event of an emergency), set it to up (2)."
  ::= {toaster 3}

toasterDoneness OBJECT-TYPE
  SYNTAX  INTEGER (1..10)
  ACCESS  read-write
  STATUS  mandatory
  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."
  ::= {toaster 4}

toasterToastType OBJECT-TYPE
  SYNTAX  INTEGER {
                    white-bread (1),
                    wheat-bread (2),
                    wonder-bread (3),
                    frozen-waffle (4),
                    frozen-bagel (5),
                    hash-brown (6),
                    other (7)
                  }
  ACCESS  read-write
  STATUS  mandatory
  DESCRIPTION
          "This variable informs the toaster of the type of 
           material that is being toasted. The toaster 
           uses this information, combined with 
           toasterToastDoneness, to compute for how 
           long the material must be toasted to achieve 
           the required doneness."
  ::= {toaster 5}

END

*************************************************************/
        

}

u_toaster.py

"""
 * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved.
 * Copyright (c) 2012 - 2024, YumaWorks, Inc., All Rights Reserved.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *

*** Generated by py-sil plugin (pyang)
    
    User PY-SIL module
    module toaster
    revision 2009-11-20
    prefix toast
    namespace http://netconfcentral.org/ns/toaster
    Created: 2024-05-01T00:28:53Z

    Online Developer Manual:
    https://docs.yumaworks.com/en/latest/dev/index.html

    CLI parameters: -f py-sil toaster.yang
"""

from pysil.module import (
    STATUS_T,
    GETCB_MODE_T,
    SILCbGet,
    SILCbInvoke,
    SILCbValidate,
    OP_EDITOP_T,
    AGT_CBTYP_T,
    SILCbEdit,
)
from .y_toaster import PYSILModuleToasterBase


class PYSILModuleToaster(PYSILModuleToasterBase):

    def edit_toaster(self, silcb: SILCbEdit):
        """
        Edit database object callback
        Path: container /toast:toaster

        Parameters
        ----------
            silcb: SILCbEdit
                Control block

        Returns
        -------
            STATUS_T
                Return status for the phase.
        """

        self.log(
            f"Enter 'edit3_toaster' callback toaster@2009-11-20 for {AGT_CBTYP_T(silcb.cbtyp).name} phase silcb.editop={OP_EDITOP_T(silcb.editop).name}"
        )

        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")
            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}")
        elif silcb.cbtyp == AGT_CBTYP_T.AGT_CB_ROLLBACK:
            # undo device instrumentation here
            pass
        else:
            return self.flag_internal_error()

        return STATUS_T.NO_ERR

    def get_toasterManufacturer(self, silcb: SILCbGet):
        """
        Get database object callback
        Path: leaf /toast:toaster/toasterManufacturer

        Parameters
        ----------
            silcb: SILCbGet
                Control block

        Returns
        -------
            STATUS_T
                status of the callback.
        """

        self.log(f"Enter 'get_toasterManufacturer' callback toaster@2009-11-20")

        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:
            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:
            # use keys silcb.local_keys to filter data and find next record
            pass

        # If item doesn't exist, exit with an error
        if not data_found:
            return STATUS_T.ERR_NCX_NO_INSTANCE

        # Item found. Set specific return_data value
        return_data = (
            "DisplayString"  # *required - replace value with correct <DisplayString>
        )

        # 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 get_toasterModelNumber(self, silcb: SILCbGet):
        """
        Get database object callback
        Path: leaf /toast:toaster/toasterModelNumber

        Parameters
        ----------
            silcb: SILCbGet
                Control block

        Returns
        -------
            STATUS_T
                status of the callback.
        """

        self.log(f"Enter 'get_toasterModelNumber' callback toaster@2009-11-20")

        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:
            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:
            # use keys silcb.local_keys to filter data and find next record
            pass

        # If item doesn't exist, exit with an error
        if not data_found:
            return STATUS_T.ERR_NCX_NO_INSTANCE

        # Item found. Set specific return_data value
        return_data = (
            "DisplayString"  # *required - replace value with correct <DisplayString>
        )

        # 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 get_toasterStatus(self, silcb: SILCbGet):
        """
        Get database object callback
        Path: leaf /toast:toaster/toasterStatus

        Parameters
        ----------
            silcb: SILCbGet
                Control block

        Returns
        -------
            STATUS_T
                status of the callback.
        """

        self.log(f"Enter 'get_toasterStatus' callback toaster@2009-11-20")

        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:
            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:
            # use keys silcb.local_keys to filter data and find next record
            pass

        # If item doesn't exist, exit with an error
        if not data_found:
            return STATUS_T.ERR_NCX_NO_INSTANCE

        # Item found. Set specific return_data value
        return_data = "str"  # *required - 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 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 you have an error you also could set errorval
        # silcb.set_errorval("testerrorval")

        return res

    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

        # if you have an error you also could set errorval
        # silcb.set_errorval("testerrorval")

        # Item found. Create a python object (dict, list, etc) with item data
        return_data = {}

        # Save return data
        res = silcb.add_return_data(data=return_data)

        return res

    def validate_cancel_toast(self, silcb: SILCbValidate):
        """
        Validation phase callback for "<validate_cancel_toast>" operation.
        All YANG constraints have passed at this point.
        Add description-stmt checks in this function.
        Path: rpc /toast:cancel-toast
        Description:
            Stop making toast, if any is 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
        """

        self.log(f"Enter 'validate_cancel_toast' callback toaster@2009-11-20")

        res = STATUS_T.NO_ERR

        # if you have an error you also could set errorval
        # silcb.set_errorval("testerrorval")

        return res

    def invoke_cancel_toast(self, silcb: SILCbInvoke):
        """
        Invocation phase callback for "<invoke_cancel_toast>" operation.
        Validation callback has passed at this point.
        Call device instrumentation code in this function.
        Path: rpc /toast:cancel-toast
        Description:
            Stop making toast, if any is 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
        """

        self.log(f"Enter 'invoke_cancel_toast' callback toaster@2009-11-20")

        res = STATUS_T.NO_ERR

        # if you have an error you also could set errorval
        # silcb.set_errorval("testerrorval")

        # Item found. Create a python object (dict, list, etc) with item data
        return_data = {}

        # Save return data
        res = silcb.add_return_data(data=return_data)

        return res

    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": "str",  # remove or replace value with correct <str>
                }

        Returns
        -------
            None
        """
        self.log(f"Enter 'send_toastDone' notification toaster@2009-11-20 data={data}")

        self.send_notification(name="toastDone", data=data)

    def init(self):
        """
        Phase 1: Initialize the toaster server instrumentation library.
        Called by server when module is loaded.

        Returns
        -------
            STATUS_T
                Return status.
                An error will cause the module load to fail.
        """
        self.log(f"Enter 'init' toaster@2009-11-20")
        return STATUS_T.NO_ERR

    def init2(self):
        """
        Phase 2: Initialize the toaster server instrumentation library.
        SIL init phase 2: non-config data structures.
        Called after running config is loaded.

        Returns
        -------
            STATUS_T
                Return status for the cleanup.
                An error will cause the server initialization to fail.
        """
        self.log(f"Enter 'init2' toasters@2009-11-20")
        return STATUS_T.NO_ERR

    def cleanup(self):
        """
        Cleanup the toaster server instrumentation library.
        Called by server when module is unloaded.

        Returns
        -------
            STATUS_T
                Return status for the cleanup.
        """
        self.log(f"Enter 'cleanup' toaster@2009-11-20")
        return STATUS_T.NO_ERR


module = PYSILModuleToaster()

y_toaster.py

"""
 * Copyright (c) 2008 - 2012, Andy Bierman, All Rights Reserved.
 * Copyright (c) 2012 - 2024, YumaWorks, Inc., All Rights Reserved.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *

*** Generated by py-sil plugin (pyang)
    
    YumaPro PY-SIL module
    module toaster
    revision 2009-11-20
    prefix toast
    namespace http://netconfcentral.org/ns/toaster
    Created: 2024-05-01T00:28:53Z

    Online Developer Manual:
    https://docs.yumaworks.com/en/latest/dev/index.html

    CLI parameters: -f py-sil toaster.yang
"""

from pysil.module import PYSILModuleBase


class PYSILModuleToasterBase(PYSILModuleBase):
    """Base Python module with configuration for PY-SIL-APP toaster"""

    def __init__(self):
        config = {
            "name": "toaster",
            "revision": "2009-11-20",
            "container_name": "toaster",
            "callbacks": [
                {"path": "/toast:toaster", "cb_edit3": "edit_toaster"},
                {
                    "path": "/toast:toaster/toasterManufacturer",
                    "cb_get2": "get_toasterManufacturer",
                    "lvl": 3,
                },
                {
                    "path": "/toast:toaster/toasterModelNumber",
                    "cb_get2": "get_toasterModelNumber",
                    "lvl": 3,
                },
                {
                    "path": "/toast:toaster/toasterStatus",
                    "cb_get2": "get_toasterStatus",
                    "lvl": 3,
                },
                {
                    "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:cancel-toast",
                    "cb_validate": "validate_cancel_toast",
                    "cb_invoke": "invoke_cancel_toast",
                },
            ],
        }

        super().__init__(config=config)