1"""RESTCONF plugin
2
3Verifies RESTCONF YANG statements as defined in RFC 8040.
4
5Verifies the grammar of the restconf extension statements.
6"""
7
8import pyang
9from pyang import plugin
10from pyang import grammar
11from pyang import statements
12from pyang import error
13from pyang.error import err_add
14
15restconf_module_name = 'ietf-restconf'
16
17class RESTCONFPlugin(plugin.PyangPlugin):
18    def __init__(self):
19        plugin.PyangPlugin.__init__(self, 'restconf')
20
21def pyang_plugin_init():
22    """Called by pyang plugin framework at to initialize the plugin."""
23
24    # Register the plugin
25    plugin.register_plugin(RESTCONFPlugin())
26
27    # Register that we handle extensions from the YANG module 'ietf-restconf'
28    grammar.register_extension_module(restconf_module_name)
29
30    yd = (restconf_module_name, 'yang-data')
31    statements.add_data_keyword(yd)
32    statements.add_keyword_with_children(yd)
33    statements.add_keywords_with_no_explicit_config(yd)
34
35    # Register the special grammar
36    for (stmt, occurance, (arg, rules), add_to_stmts) in restconf_stmts:
37        grammar.add_stmt((restconf_module_name, stmt), (arg, rules))
38        grammar.add_to_stmts_rules(add_to_stmts,
39                                   [((restconf_module_name, stmt), occurance)])
40
41    # Add validation functions
42    statements.add_validation_fun('expand_2',
43                                  [yd],
44                                  v_yang_data)
45
46    # Register special error codes
47    error.add_error_code('RESTCONF_YANG_DATA_CHILD', 1,
48                         "the 'yang-data' extension must have exactly one " +
49                         "child that is a container")
50
51restconf_stmts = [
52
53    # (<keyword>, <occurance when used>,
54    #  (<argument type name | None>, <substmts>),
55    #  <list of keywords where <keyword> can occur>)
56
57    ('yang-data', '*',
58     ('identifier', grammar.data_def_stmts),
59     ['module', 'submodule']),
60
61]
62
63def v_yang_data(ctx, stmt):
64
65    def ensure_container(s):
66        if len(s.i_children) != 1:
67            return False
68        ch = s.i_children[0]
69        if ch.keyword == 'choice':
70            for c in ch.i_children:
71                if not ensure_container(c):
72                    return False
73        elif ch.keyword != 'container':
74            return False
75        return True
76
77    if not ensure_container(stmt):
78        err_add(ctx.errors, stmt.pos, 'RESTCONF_YANG_DATA_CHILD', ())
79