********** Data Model ********** .. module:: yangson.datamodel :synopsis: Data model representation. .. testsetup:: import json import os os.chdir("examples/ex1") .. testcleanup:: os.chdir("../..") The *datamodel* module implements the following class: * :class:`DataModel`: basic user-level entry point to YANG data model information. Doctest__ snippets for this module use the data model and instance document from :ref:`sec-ex1`. __ http://www.sphinx-doc.org/en/stable/ext/doctest.html .. class:: DataModel(yltxt: str, mod_path: List[str], \ description: str = None) This class provides a basic user-level entry point to the *Yangson* library. The constructor argument *yltxt* is a string with JSON-encoded YANG library data [RFC7895]_, and *mod_path* is a list of filesystem directories in which *Yangson* searches for YANG modules (by default it is only the current directory). The *description* argument allows for adding a description text to the entire data model. If it is ``None``, then a default description is added which contains the ``module-set-id`` value from the YANG library data. The class constructor may raise the following exceptions: * :exc:`~.BadYangLibraryData` – if YANG library data is invalid. * :exc:`~.FeaturePrerequisiteError` – If a pre-requisite feature isn't supported. * :exc:`~.MultipleImplementedRevisions` – If multiple revisions of the same module are listed in YANG library with conformance type ``implement``. * :exc:`~.ModuleNotFound` – If a YANG module specified in YANG library cannot be found in any of the directories specified in *mod_path*. :class:`DataModel` is re-exported by the main package, so it can also be imported directly from there. .. doctest:: >>> from yangson import DataModel .. rubric:: Instance Attributes .. attribute:: schema Root node of the schema tree. .. attribute:: schema_data Object describing various properties extracted from the data model. .. attribute:: yang_library Python dictionary containing parsed YANG library data. .. rubric:: Public Methods .. classmethod:: from_file(name: str, mod_path: List[str] = ["."], \ description: str = None) -> DataModel Initialize the data model from a file containing JSON-encoded YANG library data and return the :class:`DataModel` instance. The *name* argument is the name of that file. The remaining two arguments are passed unchanged to the :class:`DataModel` class constructor. This method may raise the same exceptions as the class constructor. .. doctest:: >>> dm = DataModel.from_file("yang-library-ex1.json") >>> dm.yang_library['ietf-yang-library:modules-state']['module-set-id'] 'ae4bf1ddf85a67ab94a9ab71593cd1c78b7f231d' .. method:: module_set_id() -> str Return a unique identifier of the set of modules comprising the data model. This string, which consists of hexadecimal digits, is intended to be stored in the ``module-set-id`` leaf of YANG library data. The method computes the identifier as follows: - The list of module and sumodule names with revisions in the format ``name@revision`` is created. For (sub)modules that don't specify any revision, the empty string is used in place of ``revision``. - The list is alphabetically sorted, its entries joined back-to-back, and the result converted to a bytestring using the ASCII encoding. - The SHA-1 hash of the bytestring is computed, and its hexadecimal digest is the result. .. doctest:: >>> dm.module_set_id() 'ae4bf1ddf85a67ab94a9ab71593cd1c78b7f231d' .. method:: from_raw(robj: RawObject, subschema: PrefName) -> RootNode Create a root instance node from a raw data tree contained in the *robj* argument. The latter will typically be a Python dictionary directly parsed from JSON text with the library function :func:`json.load` or :func:`json.loads`. We call this data tree “raw” because it needs to be processed into the “cooked” form before it can be used in *Yangson*. For example, 64-bit numbers have to be encoded as strings in JSON text (see sec. `6.1`_ of [RFC7951]_), whereas the cooked form is a Python number. See the documentation of :mod:`instvalue` module for more details, and see also :term:`raw value`. The *subschema* argument may be used for creating instances of RPC input and/or output and notifications. The value of this argument is a :term:`prefixed name` (with an explicit prefix) that identifies the selected subschema (RPC or notification). .. doctest:: >>> with open("example-data.json") as infile: ... ri = json.load(infile) >>> inst = dm.from_raw(ri) >>> inst.value {'example-1:greeting': 'Hi!'} .. method:: get_schema_node(path: SchemaPath) -> Optional[SchemaNode] Return the schema node addressed by *path*, or ``None`` if no such schema node exists. The *path* argument is a :term:`schema path`. .. doctest:: >>> root = dm.get_schema_node("/") >>> root.parent is None True .. method:: get_data_node(path: DataPath) -> Optional[DataNode] Return the data node addressed by *path*, or ``None`` if such a data node doesn't exist. As opposed to the :meth:`get_schema_node` method, the *path* argument is a :term:`data path`, i.e. it contains only names of *data nodes*. .. doctest:: >>> leaf = dm.get_data_node("/example-1:greeting") >>> leaf.parent is root True .. method:: ascii_tree(no_types: bool = False, val_count: bool = False) -> str Generate ASCII art representation of the actual schema tree. If *no_types* is set to ``True``, the output of type information with *leaf* and *leaf-list* nodes is suppressed. If *val_count* is ``True``, each schema node is printed with the number of times it has been used for validating instances. The schema tree and its nodes are represented according to the conventions for tree diagrams described in [RFC8340]_, with the following additions and changes: * Lists and leaf-lists that are ordered by user (see section `7.7.7`_ in [RFC7950]_) are indicated by the hash symbol ``#`` rather than ``*``. * Types of leaf and leaf-list nodes are enclosed in chevrons ``<`` and ``>``. * Obsolete nodes that have been un-obsoleted in YANG library configuration (see :ref:`yang-library`) are labelled with uppercase ``O``. * Dependence on features is not indicated. .. NOTE:: ASCII trees generated by this method always depict a *complete* schema tree. In contrast, YANG tree diagrams defined in [RFC8340]_ are oriented more on partial trees of individual YANG modules. .. doctest:: >>> print(dm.ascii_tree(), end='') +--rw example-1:greeting? >>> print(dm.ascii_tree(True), end='') +--rw example-1:greeting? .. method:: clear_val_counters() -> None Reset validation counters to zero throughout the schema tree. .. method:: parse_instance_id(text: str) -> InstanceRoute Parse :term:`instance identifier` into an internal object of the :class:`~.instance.InstanceRoute` class that can be used as a parameter to the the :meth:`~.instance.InstanceNode.goto` and :meth:`~.instance.InstanceNode.peek` methods of the :class:`~.instance.InstanceNode` class. .. method:: parse_resource_id(text: str) -> InstanceRoute Parse :term:`resource identifier` into an :class:`~.instance.InstanceRoute` object. *Yangson* extends the syntax of resource identifiers defined in sec. `3.5.3`_ of [RFC8040]_ so as to support entire lists and leaf-lists as resources: the last component of a resource identifier can be the name of a list or leaf-list, with no keys or value specified. .. method:: schema_digest() -> str Generate a digest of the data model schema. The digest is a streamlined and compressed JSON representation of the data model schema intended for use in restricted environments without a full YANG processing stack such as client-side applications running in a web browser. The returned string contains a structure of JSON objects that follows the data model hierarchy. Every JSON object also contains members with information about the corresponding data node (including the anonymous root node), namely: * The following members are available for all nodes that have them: - ``kind`` – class of the node, with these possible values: ``schematree``, ``container``, ``leaf``, ``list``, ``leaf-list``, ``anydata`` and ``anyxml`` - ``config`` – ```false`` if the node (and its descendants) don't represent configuration - ``mandatory`` with the value of ``true`` if the node is mandatory - ``description`` – description string as defined in the data model * Internal nodes (the root node, containers, and lists) have the ``children`` member. Its value is an object with a name/value pair for every child data node that is defined in the data model. The name is the identifier of the child identical to the name of the node's instance – for example, it is ``foomod:bar`` for the ``bar`` data node defined in the ``foomod`` module. The value of each member of the ``children`` object is then another object containing the child's schema digest. * The following members are added for terminal nodes (leafs and leaf-lists): - ``type`` – specifies the base type of the terminal node such as ``uint8``, ``string``, the derived type name (if any), and possibly extra information specific for the type - ``default`` – the default value for the node, if defined - ``units`` - units for the node's values, if specified * Container nodes also have the ``presence`` member that is ``true`` for containers with presence (see sec. `7.5.1`_ of [RFC7950]_), and ``false`` otherwise. * List nodes also have the ``keys`` member whose value is an array with names of the list's keys. .. doctest:: >>> len(dm.schema_digest()) 222 .. _3.5.3: https://rfc-editor.org/rfc/rfc8040.html#section-3.5.3 .. _6.1: https://rfc-editor.org/rfc/rfc7951.html#section-6.1 .. _7.5.1: https://rfc-editor.org/rfc/rfc7950.html#section-7.5.1 .. _7.7.7: https://rfc-editor.org/rfc/rfc7950.html#section-7.7.7 .. _pyang: https://github.com/mbj4668/pyang