Parser

class yangify.parser.Parser(schema: yangson.schemanode.DataNode, model_filter: yangify.model_filter.ModelFilter, native: Any, root_native: Any, keys: Dict[str, str])

Parsing classes used to parse groupings need to inherit from this class. In addition to the relevant functions and classes to parse children nodes, they may include a Yangify class that inherits from ParserData to implement some parsing logic. This is mandatory when parsing YANG Lists.

yy

This attribute will be instantiated using the nested class Parser.Yangify. Everything you implement inside your class Yangify will be available through this attribute.

Type:ParserData

Example

Interface class to parse openconfig-interfaces:interfaces/interface model:

class Interface(Parser):
    class Yangify(ParserData):
        def extract_elements(self) -> Iterator[Tuple[str, Dict[str, Any]]]:
            for k, v in interfaces_magic:
                yield k, v
Yangify

alias of ParserData

class yangify.parser.ParserData(schema: yangson.schemanode.DataNode, native: Any, root_native: Any, keys: Dict[str, str])

This is the base class for the Parser.Yangify object.

schema

Schema of the current grouping being processed

Type:yangson.schemanode.DataNode
native

Exact details will depend on the parser implementation. As a rule of thumb points to the native configuration/data relevant for the current element being processed. On start it’s set the same as ParserData.root_native, however, it can be changed by the parser implementation. When processing lists, it’s set automatically to the second object returned by ParserData.extract_elements

Type:Any
root_native

Exact type and details will depend on the parser implementation. However, it points to the original object passed to RootParser in the native argument.

Type:Any
keys

This dictionary keeps track of all the keys relevant to the current object being processed. Keys are equal to the path where the key was extracted while the value is the actual value. For instance, if you were parsing the interface FastEthernet1.1 using the openconfig:interfaces model, you’d have the following keys:

{
    "openconfig-interfaces:interfaces/interface": "FastEthernet1",
    "openconfig-interfaces:interfaces/interface/subinterfaces/subinterface": 1,
}
Type:Dict[str, Any]
extract_elements() → Iterator[Tuple[str, Any]]

This function is called when processing a YANG list. It’s mandatory to implement and needs to return an iterator of <key, value> pairs where:

  • the key is the identifier of en element of the list
  • the value is a relevant block of native data for the current object

The key will be added to ParserData.keys (referenced as the path to the node) and the value will be set in the ParserData.native attribute.

init() → None

Called only by the RootParser in the very beginning of the processing. This function may be used to preprocess the native objects or for anything else that may be needed to do before the actual processing starts.

key

Last key extracted in the current object being processed.

metadata = {}
path = ''
post() → None

Called only by the RootParser in the very end of the processing.

post_process() → None

This is called after processing either a container or a list element.

pre_process() → None

This is called before processing either a container or a list element.

class yangify.parser.RootParser(dm: yangson.datamodel.DataModel, native: Any, config: bool = True, state: bool = False, include: Optional[List[str]] = None, exclude: Optional[List[str]] = None)

This object represents the root of the parser and allows the user to choose which parts of the model to parse and with which parsers. To do so assign the parser classes to class attributes named after the nodes you want to parse.

Parameters:
  • dm – DataModel we are going to be parsing
  • native – Native data to parse and map into the model
  • config – Parse config leaves
  • state – Parse state leaves

Examples

Parsing both openconfig-interfaces and openconfig-vlan models:

from yangify import parser

class Interfaces(parser.Parser):
    ...

class Vlans(parser.Parser):
    ...

class Parser(parser.RootParser):
    interfaces = Interfaces
    vlans = Vlans

Parsing only openconfig-interfaces but using a different parser:

from yangify import parser

class InterfacesAlt(parser.Parser):
    ...

class Parser(parser.RootParser):
    interfaces = InterfacesAlt
process(validate: bool = True) → yangson.instance.RootNode

Process the native data and map it into the model.

Parameters:validate – Validates the object before returning it
yangify.parser.unneeded(*args, **kwargs) → None

You can use this dummy function to flag that a particular leaf doesn’t need to be processed. Otherwise, the system will flag it as not implemented.

Example

we don’t need to vlan_id as it’s the same as the key:

class VlanConfig(Parser):
    def name(self):
        ...

    vlan_id = unneeded
yangify.parser.text_tree.parse_indented_config(config: List[str], current_indent: int = 0, previous_indent: int = 0, nested: bool = False) → Dict[str, Any]

This method reads a configuration that conforms to a very poor industry standard CLI (aka IOS-style) and returns a nested structure that behaves like a dict.

Example

The following block of configuration:

interface FastEthernet1
    descrption this is a description
    switchport mode access
    switchport access vlan 1
    shutdown
interface FastEthernet2
    descrption this is a description
    switchport mode trunk
    switchport trunk allowed vlan 10,20

Will yield the following dict:

interface:
    #text: FastEthernet1
    FastEthernet1:
        #standalone: true
        descrption:
            #text: this is a description
            this:
                #text: is a description
                is:
                    #text: a description
                    a:
                        #text: description
                        description:
                            #standalone
        switchport:
            #text: access vlan 1
            mode:
                #text: access
                access:
                    #standalone: true
            access:
                #text: vlan 1
                vlan:
                    #text: 1
                    1:
                        #standalone: true
        shutdown:
            #standalone: true
    FastEthernet2:
        #standalone: true
        descrption:
            #text: this is another description
            this:
                #text: is another description
                is:
                    #text: another description
                    a:
                        #text: description
                        description:
                            #standalone
        switchport:
            #text: trunk allowed vlan 10,20
            mode:
                #text: trunk
                trunk:
                    #standalone: true
            trunk:
                #text: allowed vlan 10,20
                allowed:
                    #text: vlan 10,20
                    vlan:
                        #text: 10,20
                        10,20:
                            #standalone: true

Basically, the function does the following:

  1. It uses the indentation of the configuration to build a hierarchy, that’s for instance how it knows the commands that are nested inside the interfaces.
  2. For commands with multiple words in the same line it builds a data structure that follows the following rules:
    1. each word goes into a nested dictionary inside the previous word.
    2. if a word is followed by more words, those following words go into a subkey #text. For instance, parsed["interface"]["FastEthernet1"]["description"]["#text"] will give you the entire description
    3. if a word is not followed by any word, a #standalone key equal to True is added. For instance, parsed["interface"]["FastEthernet1"]["shutdown"]["#standalone"] will indicate the interface is shutdown.