def v_chk_path_refs(ctx, statement): """ Check path references for absolute / relative paths as appropriate. This function is called with the following statements: * path * augment """ path = statement.arg if path[0] == '/': abspath = True else: abspath = False components = yangpath.split_paths(path) # consider the namespace in the first component # assumes that if the namespace matches the module namespace, then # relative path should be used (intra-module ) if ":" in components[0]: (namespace, barepath) = components[0].split(':') else: namespace = statement.i_module.i_prefix barepath = components[0] mod_prefix = statement.i_module.i_prefix if namespace == mod_prefix and abspath: err_add(ctx.errors, statement.pos, 'OC_RELATIVE_PATH', (statement.keyword, statement.arg))
def v_chk_required_substmt(ctx, stmt): if stmt.keyword in _required_substatements: (required, s) = _required_substatements[stmt.keyword] for r in required: if stmt.search_one(r) is None: err_add(ctx.errors, stmt.pos, 'IETF_MISSING_REQUIRED_SUBSTMT', (s, stmt.keyword, r))
def v_chk_namespace(ctx, stmt, namespace_prefixes): if namespace_prefixes != []: for prefix in namespace_prefixes: if stmt.arg == prefix + stmt.i_module.arg: return err_add(ctx.errors, stmt.pos, 'LINT_BAD_NAMESPACE_VALUE', namespace_prefixes[0] + stmt.i_module.arg)
def chk_revision(oldmod, newmod, ctx): oldrev = get_latest_revision(oldmod) newrev = get_latest_revision(newmod) if newrev is None: err_add(ctx.errors, newmod.pos, 'CHK_NO_REVISION', ()) elif (oldrev is not None) and (oldrev >= newrev): err_add(ctx.errors, newmod.pos, 'CHK_BAD_REVISION', (newrev, oldrev))
def chk_children(oldch, newchs, newp, ctx): newch = None for ch in newchs: if ch.arg == oldch.arg: newch = ch break if newch is None: err_def_removed(oldch, newp, ctx) return if newch.keyword != oldch.keyword: err_add(ctx.errors, newch.pos, 'CHK_CHILD_KEYWORD_CHANGED', (oldch.keyword, newch.arg, newch.keyword)) return chk_status(oldch, newch, ctx) chk_if_feature(oldch, newch, ctx) chk_config(oldch, newch, ctx) chk_must(oldch, newch, ctx) chk_when(oldch, newch, ctx) if newch.keyword == 'leaf': chk_leaf(oldch, newch, ctx) elif newch.keyword == 'leaf-list': chk_leaf_list(oldch, newch, ctx) elif newch.keyword == 'container': chk_container(oldch, newch, ctx) elif newch.keyword == 'list': chk_list(oldch, newch, ctx) elif newch.keyword == 'choice': chk_choice(oldch, newch, ctx) elif newch.keyword == 'case': chk_case(oldch, newch, ctx) elif newch.keyword == 'input': chk_input_output(oldch, newch, ctx) elif newch.keyword == 'output': chk_input_output(oldch, newch, ctx)
def chk_i_children(old, new, ctx): for oldch in old.i_children: chk_child(oldch, new, ctx) # chk_child removes all old children for newch in new.i_children: if statements.is_mandatory_node(newch): err_add(ctx.errors, newch.pos, 'CHK_NEW_MANDATORY', newch.arg)
def chk_children(oldch, newchs, newp, ctx): newch = None for ch in newchs: if ch.arg == oldch.arg: newch = ch break if newch is None: err_def_removed(oldch, newp, ctx) return newchs.remove(newch) if newch.keyword != oldch.keyword: err_add(ctx.errors, newch.pos, 'CHK_CHILD_KEYWORD_CHANGED', (oldch.keyword, newch.arg, newch.keyword)) return chk_status(oldch, newch, ctx) chk_if_feature(oldch, newch, ctx) chk_config(oldch, newch, ctx) chk_must(oldch, newch, ctx) chk_when(oldch, newch, ctx) if newch.keyword == 'leaf': chk_leaf(oldch, newch, ctx) elif newch.keyword == 'leaf-list': chk_leaf_list(oldch, newch, ctx) elif newch.keyword == 'container': chk_container(oldch, newch, ctx) elif newch.keyword == 'list': chk_list(oldch, newch, ctx) elif newch.keyword == 'choice': chk_choice(oldch, newch, ctx) elif newch.keyword == 'case': chk_case(oldch, newch, ctx) elif newch.keyword == 'input': chk_input_output(oldch, newch, ctx) elif newch.keyword == 'output': chk_input_output(oldch, newch, ctx)
def v_chk_recommended_substmt(ctx, stmt): if stmt.keyword in _recommended_substatements: for r in _recommended_substatements[stmt.keyword]: if stmt.search_one(r) is None: err_add(ctx.errors, stmt.pos, 'IETF_MISSING_RECOMMENDED_SUBSTMT', (stmt.keyword, r))
def v_chk_leaf_mirroring(ctx, statement): """ Check that all config leaves are included in the state container """ # Skip the check if the container is not a parent of other containers if statement.search_one('container') is None: return containers = statement.search('container') # Only perform this check if this is a container that has both a config # and state container c_config, c_state = None, None for c in containers: if c.arg == 'config': c_config = c elif c.arg == 'state': c_state = c if not None in [c_config, c_state]: break if None in [c_config, c_state]: return config_elem_names = [i.arg for i in c_config.substmts if not i.arg == 'config' and i.keyword in INSTANTIATED_DATA_KEYWORDS] state_elem_names = [i.arg for i in c_state.substmts if not i.arg == 'state' and i.keyword in INSTANTIATED_DATA_KEYWORDS] for elem in config_elem_names: if not elem in state_elem_names: err_add(ctx.errors, statement.parent.pos, 'OC_OPSTATE_APPLIED_CONFIG', (elem, statements.mk_path_str(statement, False)))
def post_validate_ctx(self, ctx, modules): if not ctx.opts.ietf: return for mod in modules: if (self.mmap[mod.arg]['found_2119_keywords'] and not self.mmap[mod.arg]['found_8174']): pos = self.mmap[mod.arg]['description_pos'] err_add(ctx.errors, pos, 'IETF_MISSING_RFC8174', ())
def v_chk_recommended_substmt(ctx, stmt): if stmt.keyword in _recommended_substatements: (recommended, s) = _recommended_substatements[stmt.keyword] for r in recommended: if stmt.search_one(r) is None: err_add(ctx.errors, stmt.pos, 'LINT_MISSING_RECOMMENDED_SUBSTMT', (s, stmt.keyword, r))
def cmp_node(optr, nptr): if optr.parent is None: return if (optr.i_module.i_modulename == nptr.i_module.i_modulename and optr.arg == nptr.arg): return cmp_node(optr.parent, nptr.parent) else: err_add(ctx.errors, new.pos, 'CHK_LEAFREF_PATH_CHANGED', ())
def v_chk_prefix(self, ctx, stmt): if stmt.parent.keyword != 'module': return r = '.+3gpp$' if re.match(r, stmt.arg) is None: err_add(ctx.errors, stmt.pos, '3GPP_BAD_PREFIX_VALUE', ()) if len(stmt.arg) > 13: err_add(ctx.errors, stmt.pos, '3GPP_TOO_LONG_PREFIX', ())
def chk_range(old, new, oldts, newts, ctx): ots = old.i_type_spec nts = new.i_type_spec if (type(ots) == types.RangeTypeSpec and type(nts) == types.RangeTypeSpec): tmperrors = [] types.validate_ranges(tmperrors, new.pos, ots.ranges, new) if tmperrors != []: err_add(ctx.errors, new.pos, 'CHK_RESTRICTION_CHANGED', 'range')
def chk_i_children(old, new, ctx): for oldch in old.i_children: chk_child(oldch, new, ctx) old_child_args = [oldch.arg for oldch in old.i_children] added_new_children = [new_child for new_child in new.i_children if new_child.arg not in old_child_args] for newch in added_new_children: if statements.is_mandatory_node(newch): err_add(ctx.errors, newch.pos, 'CHK_NEW_MANDATORY', newch.arg)
def chk_decimal64(old, new, oldts, newts, ctx): oldbasets = get_base_type(oldts) newbasets = get_base_type(newts) if newbasets.fraction_digits != oldbasets.fraction_digits: err_add(ctx.errors, new.pos, 'CHK_DEF_CHANGED', ('fraction-digits', newts.fraction_digits, oldts.fraction_digits)) # a decimal64 can only be restricted with range chk_range(old, new, oldts, newts, ctx)
def v_unused_complex_type(ctx, stmt): """Adds warnings for complex types which are defined not at the top-level of the module and unused """ if stmt.parent.parent is not None: # this is a locally scoped complex type if stmt.i_is_unused == True: err_add(ctx.errors, stmt.pos, 'UNUSED_COMPLEX_TYPE', stmt.arg)
def chk_enumeration(old, new, oldts, newts, ctx): # verify that all old enums are still in new, with the same values for (name, val) in oldts.enums: n = util.keysearch(name, 0, newts.enums) if n is None: err_add(ctx.errors, new.pos, 'CHK_DEF_REMOVED', ('enum', name, old.pos)) elif n[1] != val: err_add(ctx.errors, new.pos, 'CHK_ENUM_VALUE_CHANGED', (name, val, n[1]))
def chk_bits(old, new, oldts, newts, ctx): # verify that all old bits are still in new, with the same positions for (name, pos) in oldts.bits: n = util.keysearch(name, 0, newts.bits) if n is None: err_add(ctx.errors, new.pos, 'CHK_DEF_REMOVED', ('bit', name, old.pos)) elif n[1] != pos: err_add(ctx.errors, new.pos, 'CHK_BIT_POSITION_CHANGED', (name, pos, n[1]))
def v_chk_module_name(ctx, stmt, modulename_prefixes): if modulename_prefixes != []: for prefix in modulename_prefixes: if stmt.arg.find(prefix + '-') == 0: return err_add(ctx.errors, stmt.pos, 'LINT_BAD_MODULENAME_PREFIX', (modulename_prefixes[0], stmt.arg)) elif stmt.arg.find('-') == -1: # can't check much, but we can check that a prefix is used err_add(ctx.errors, stmt.pos, 'LINT_NO_MODULENAME_PREFIX', ())
def chk_presence(old, new, ctx): oldpresence = old.search_one('presence') newpresence = new.search_one('presence') if oldpresence is None and newpresence is None: pass elif oldpresence is None and newpresence is not None: err_def_added(newpresence, ctx) elif oldpresence is not None and newpresence is None: err_def_removed(oldpresence, new, ctx) elif oldpresence.arg != newpresence.arg: err_add(ctx.errors, newpresence.pos, 'CHK_UNDECIDED_PRESENCE', ())
def v_type_abstract(ctx, stmt): """ Check the base complex type, if present, is also abstract.""" if (stmt.arg == 'true'): stmt.parent.i_is_abstract = True # check whether the complex type extends another one if stmt.parent.i_base_type is not None: if not stmt.parent.i_base_type.i_is_abstract: err_add(ctx.errors, stmt.pos, 'ABSTRACT_NOT_ALLOWED', (stmt.parent.i_base_type.arg, stmt.parent.arg)) else: stmt.parent.i_is_abstract = False
def validate_complex_type(ctx, stmt): """Validates a reffered complex type.""" if stmt.i_complex_type is None: err_add(ctx.errors, stmt.pos, 'COMPLEX_TYPE_NOT_FOUND', (stmt.arg, stmt.i_module.arg)) return else: # ensure the complex type is validated if stmt.i_complex_type.is_grammatically_valid == True: v_type_complex_type(ctx, stmt.i_complex_type) stmt.i_complex_type.i_is_unused = False
def chk_identityref(old, new, oldts, newts, ctx): # verify that the bases are the same extra = [n for n in newts.idbases] for oidbase in oldts.idbases: for nidbase in newts.idbases: if (nidbase.i_module.i_modulename == oidbase.i_module.i_modulename and nidbase.arg.split(':')[-1] == oidbase.arg.split(':')[-1]): extra.remove(nidbase) for n in extra: err_add(ctx.errors, n.pos, 'CHK_DEF_ADDED', ('base', n.arg))
def check_top_level_data_definitions(ctx, stmt): """Check that the module has no data elements at the root. Args: ctx: pyang.Context for the validation stmt: pyang.Statement for the matching module """ data_definitions = [i.arg for i in stmt.substmts if i.keyword in INSTANTIATED_DATA_KEYWORDS] if data_definitions: err_add(ctx.errors, stmt.pos, "OC_MODULE_DATA_DEFINITIONS", (stmt.arg, ", ".join(data_definitions)))
def chk_status(old, new, ctx): oldstatus = old.search_one('status') newstatus = new.search_one('status') if oldstatus is None or oldstatus.arg == 'current': # any new status is ok return if newstatus is None: err_add(ctx.errors, new.pos, 'CHK_INVALID_STATUS', ("(implicit) current", oldstatus.arg)) elif ((newstatus.arg == 'current') or (oldstatus.arg == 'obsolete' and newstatus.arg != 'obsolete')): err_add(ctx.errors, newstatus.pos, 'CHK_INVALID_STATUS', (newstatus.arg, oldstatus.arg))
def v_chk_include(ctx, stmt): latest = stmt.i_orig_module.i_latest_revision if latest is None: return submodulename = stmt.arg r = stmt.search_one("revision-date") if r is not None: rev = r.arg else: rev = None subm = ctx.get_module(submodulename, rev) if subm is not None and subm.i_latest_revision is not None and subm.i_latest_revision > latest: err_add(ctx.errors, stmt.pos, "LINT_BAD_REVISION", (latest, submodulename, subm.i_latest_revision))
def check_bad_types(ctx, stmt): """Check validation rules for bad types that should not be used in OpenConfig models. Args: ctx: pyang.Context for validation stmt: pyang.Statement representing a leaf or leaf-list """ elemtype = stmt.search_one("type") if elemtype is None or elemtype.arg not in BAD_TYPES: return err_add(ctx.errors, stmt.pos, "OC_BAD_TYPE", (elemtype.arg))
def chk_range(old, new, oldts, newts, ctx): ots = old.i_type_spec nts = new.i_type_spec if not isinstance(nts, types.RangeTypeSpec): return if isinstance(ots, types.RangeTypeSpec): tmperrors = [] types.validate_ranges(tmperrors, new.pos, ots.ranges, new) if tmperrors: err_add(ctx.errors, new.pos, 'CHK_RESTRICTION_CHANGED', 'range') else: err_add(ctx.errors, nts.ranges_pos, 'CHK_DEF_ADDED', ('range', str(nts.ranges)))
def v_chk_opstate_paths(ctx, statement): """ Check elements for compliance with the opstate structure. Called for leaves and leaf-lists. Particularly: * Skip checks for YANG list keys * Check that there is a single 'config' and 'state' container in the path * Check that elements in a 'config' container are r/w, and 'state' are ro """ pathstr = statements.mk_path_str(statement, False) # print "examining path to %s: %s" % (statement.arg, pathstr) # leaves that are list keys are exempt from this check. YANG # requires them at the top level of the list, i.e., not allowed # in a descendent container if statement.keyword == 'leaf' and hasattr(statement, 'i_is_key'): keytype = statement.search_one ('type') if keytype.arg != 'leafref': err_add(ctx.errors, statement.pos, 'OC_OPSTATE_KEY_LEAFREF', statement.arg) # print "leaf %s is a key, skipping" % statement.arg return #path_elements = [c.encode('ascii', 'ignore') for c in yangpath.split_paths(pathstr)] path_elements = yangpath.split_paths(pathstr) # count number of 'config' and 'state' elements in the path confignum = path_elements.count('config') statenum = path_elements.count('state') if confignum != 1 and statenum != 1: err_add(ctx.errors, statement.pos, 'OC_OPSTATE_CONTAINER_COUNT', (pathstr)) # for elements in a config or state container, make sure they have the # correct config property if statement.parent.keyword == 'container': # print "%s %s in container: %s (%s)" % (statement.keyword, pathstr, # str(statement.parent.arg), statement.i_config) if statement.parent.arg == 'config': if statement.i_config is False: err_add(ctx.errors, statement.pos, 'OC_OPSTATE_CONFIG_PROPERTY', (statement.arg, 'config', 'true')) elif statement.parent.arg == 'state' or statement.parent.arg == 'counters': if statement.i_config is True: err_add(ctx.errors, statement.parent.pos, 'OC_OPSTATE_CONFIG_PROPERTY', (statement.arg, 'state', 'false')) else: err_add(ctx.errors, statement.pos, 'OC_OPSTATE_CONTAINER_NAME', (statement.arg, pathstr))
def check_module_rawtext(ctx, stmt): """Perform validation of a module"s raw text. Args: ctx: The pyang.Context for the current validation. stmt: The pyang.Statement for a module that we are parsing Function is called once per module to reduce the number of iterations through the module. """ try: mod_filename = stmt.pos.ref.split("/")[-1] mod_filename = mod_filename.split(".")[0] except IndexError: err_add(ctx.errors, stmt.pos, "OC_LINTER_ERROR", "Can't determine a module name for %s" % stmt.pos) handle = None for mod in ctx.repository.get_modules_and_revisions(ctx): # stmt.pos.ref gives the reference to the file that this # key statement was within if mod[0] == mod_filename: handle = mod break if handle is not None: try: module = ctx.repository.get_module_from_handle(handle[2]) except (AttributeError, IndexError) as e: err_add(ctx.errors, stmt.pos, "OC_LINTER_ERROR", "Can't find module %s: %s" % (stmt.pos.ref, e)) return else: err_add(ctx.errors, stmt.pos, "OC_LINTER_ERROR", "Couldn't open module %s" % stmt.pos.ref) return key_re = re.compile( r"^([ ]+)?key([ ]+)(?P<arg>[^\"][a-zA-Z0-9\-_]+);$") quoted_re = re.compile(r"^\".*\"$") ln_count = 0 for ln in module[2].split("\n"): ln_count += 1 # Remove the newline from the module ln = ln.rstrip("\n") if key_re.match(ln): key_arg = key_re.sub(r"\g<arg>", ln) if not quoted_re.match(key_arg): # Need to create a fake position object for the # key statement because of this pre-initialisation # module parse. pos = error.Position(stmt.pos.ref) pos.line = ln_count # Generate an error as the key argument is not # quoted. err_add(ctx.errors, pos, "OC_KEY_ARGUMENT_UNQUOTED", key_arg) ln_count += 1
def _add_i_deviation(self, ctx, stmt): if not hasattr(stmt.i_module, 'is_deviation_module'): stmt.i_module.is_deviation_module = True t = stmt.i_target_node if t is None: return stmt = stmt.search_one('deviate') if stmt.arg == 'not-supported': if ((t.parent.keyword == 'list') and (t in t.parent.i_key)): err_add(self.ctx.errors, stmt.pos, 'BAD_DEVIATE_KEY', (t.i_module.arg, t.arg)) return if not hasattr(t.parent, 'i_not_supported'): t.parent.i_not_supported = [] t.parent.i_not_supported.append(t) self._add_deviation(t, 'not_supported', stmt.i_module, stmt) elif stmt.arg == 'add': for c in stmt.substmts: if c.keyword in statements._singleton_keywords: if t.search_one(c.keyword) != None: err_add(self.ctx.errors, c.pos, 'BAD_DEVIATE_ADD', (c.keyword, t.i_module.arg, t.arg)) elif t.keyword not in statements._valid_deviations[c.keyword]: err_add(self.ctx.errors, c.pos, 'BAD_DEVIATE_TYPE', c.keyword) else: self._add_deviation(t, 'add', stmt.i_module, c) else: if t.keyword not in statements._valid_deviations[c.keyword]: err_add(self.ctx.errors, c.pos, 'BAD_DEVIATE_TYPE', c.keyword) else: self._add_deviation(t, 'add', stmt.i_module, c) else: # delete or replace for c in stmt.substmts: if (c.keyword == 'config' and stmt.arg == 'replace' and hasattr(t, 'i_config')): self._add_deviation_r(t, 'replace', stmt.i_module, c) if c.keyword in statements._singleton_keywords: old = t.search_one(c.keyword) else: old = t.search_one(c.keyword, c.arg) if old is None: err_add(self.ctx.errors, c.pos, 'BAD_DEVIATE_DEL', (c.keyword, t.i_module.arg, t.arg)) else: self._add_deviation(t, stmt.arg, stmt.i_module, c)
def chk_type(old, new, ctx): oldts = old.i_type_spec newts = new.i_type_spec if oldts is None or newts is None: return # verify that the base type is the same if oldts.name != newts.name: err_add(ctx.errors, new.pos, 'CHK_BASE_TYPE_CHANGED', (oldts.name, newts.name)) return # check the allowed restriction changes if oldts.name in chk_type_func: chk_type_func[oldts.name](old, new, oldts, newts, ctx)
def v_reference_instance_list(ctx, stmt): """Verify that the complex-type referred to by an instance-list statement with config true must have a defined key """ if stmt.i_config: ct = stmt.i_complex_type if ct is None: return while ct is not None: if hasattr(ct, 'i_key'): return ct = ct.i_base_type err_add(ctx.errors, stmt.pos, 'KEY_REQUIRED', (stmt.i_complex_type.arg, stmt.pos))
def check_module_rawtext(ctx, stmt): """Perform validation of a module"s raw text. Args: ctx: The pyang.Context for the current validation. stmt: The pyang.Statement for a module that we are parsing Function is called once per module to reduce the number of iterations through the module. """ try: mod_filename = os.path.realpath(stmt.pos.ref).split("/")[-1] mod_filename = mod_filename.split(".")[0] except IndexError: err_add(ctx.errors, stmt.pos, "OC_LINTER_ERROR", "Can't determine a module name for %s" % stmt.pos) handle = None for mod in ctx.repository.get_modules_and_revisions(ctx): # stmt.pos.ref gives the reference to the file that this # key statement was within if mod[0] == mod_filename: handle = mod break if handle is not None: try: module = ctx.repository.get_module_from_handle(handle[2]) except (AttributeError, IndexError) as e: err_add(ctx.errors, stmt.pos, "OC_LINTER_ERROR", "Can't find module %s: %s" % (stmt.pos.ref, unicode(e))) return else: err_add(ctx.errors, stmt.pos, "OC_LINTER_ERROR", "Couldn't open module %s" % stmt.pos.ref) return key_re = re.compile(r"^([ ]+)?key([ ]+)(?P<arg>[^\"][a-zA-Z0-9\-_]+);$") quoted_re = re.compile(r"^\".*\"$") ln_count = 0 for ln in module[2].split("\n"): ln_count += 1 # Remove the newline from the module ln = ln.rstrip("\n") if key_re.match(ln): key_arg = key_re.sub(r"\g<arg>", ln) if not quoted_re.match(key_arg): # Need to create a fake position object for the # key statement because of this pre-initialisation # module parse. pos = error.Position(stmt.pos.ref) pos.line = ln_count # Generate an error as the key argument is not # quoted. err_add(ctx.errors, pos, "OC_KEY_ARGUMENT_UNQUOTED", key_arg) ln_count += 1
def v_chk_description(self, ctx, s): arg = re.sub(r'\s+', ' ', s.arg) if s.parent.keyword == 'module' or s.parent.keyword == 'submodule': m = re_rfc8174.search(arg) if m is not None: self.found_8174 = True arg = arg[:m.start()] + arg[m.end():] if re_tlp.search(arg) is None: err_add(ctx.errors, s.pos, 'IETF_MISSING_TRUST_LEGAL_PROVISIONING', ()) if not self.found_2119_keywords: if re_2119_keywords.search(arg) is not None: self.found_2119_keywords = True if not self.found_8174: err_add(ctx.errors, s.pos, 'IETF_MISSING_RFC8174', ())
def chk_when(old, new, ctx): oldwhen = old.search('when') newwhen = new.search('when') # remove all common whens for oldw in old.search('when'): neww = new.search_one('when', arg = oldw.arg) if neww is not None: newwhen.remove(neww) oldwhen.remove(oldw) if len(oldwhen) == 0: for neww in newwhen: err_add(ctx.errors, neww.pos, 'CHK_NEW_WHEN', ()) else: for neww in newwhen: err_add(ctx.errors, neww.pos, 'CHK_UNDECIDED_WHEN', ())
def v_set_subid(ctx, stmt): if stmt.parent.search_one((smi_module_name, 'oid')) is not None: err_add(ctx.errors, stmt.pos, 'SMIv2_SUBID_AND_OID', ()) return def find_ancestor_oid(s): if s.parent is None: return None if hasattr(s.parent, 'i_smi_oid'): return s.parent.i_smi_oid return find_ancestor_oid(s.parent) oid = find_ancestor_oid(stmt.parent) if oid is None: err_add(ctx.errors, stmt.pos, 'SMIv2_BAD_SUBID', ()) return stmt.parent.i_smi_oid = oid + [int(stmt.arg)]
def v_yang_data(ctx, stmt): def ensure_container(s): if len(s.i_children) != 1: return False ch = s.i_children[0] if ch.keyword == 'choice': for c in ch.i_children: if not ensure_container(c): return False elif ch.keyword != 'container': return False return True if not ensure_container(stmt): err_add(ctx.errors, stmt.pos, 'RESTCONF_YANG_DATA_CHILD', ())
def v_chk_ocmodule(ctx, statement): """ Check characteristics of an entire OpenConfig module. Particularly: * Module should include an "openconfig-extensions:openconfig-version" statement, which should match semantic versioning patterns. * Module should not define any data elements at the top level. * Module name should be "openconfig-". """ data_definitions = [i.arg for i in statement.substmts if i.keyword in INSTANTIATED_DATA_KEYWORDS] if len(data_definitions): err_add(ctx.errors, statement.pos, 'OC_MODULE_DATA_DEFINITIONS', (statement.arg, ", ".join(data_definitions))) version = None for s in statement.substmts: if isinstance(s.keyword, tuple) and s.keyword[1] == 'openconfig-version': version = s if version is None: err_add(ctx.errors, statement.pos, 'OC_MODULE_MISSING_VERSION', statement.arg) return if not re.match("^[0-9]+\.[0-9]+\.[0-9]+$", version.arg): err_add(ctx.errors, statement.pos, 'OC_INVALID_SEMVER', version.arg) match_revision = False for revision_stmt in statement.search('revision'): reference_stmt = revision_stmt.search_one('reference') if reference_stmt is not None and \ reference_stmt.arg == version.arg: match_revision = True if match_revision is False: err_add(ctx.errors, statement.pos, 'OC_MISSING_SEMVER_REVISION', version.arg) top_groupings = [] for grouping in statement.search('grouping'): if re.match(".*\-top$", grouping.arg): top_groupings.append(grouping.arg) if not len(top_groupings): err_add(ctx.errors, statement.pos, 'OC_MISSING_STANDARD_GROUPING', (statement.arg, "-top"))
def chk_default(old, new, ctx): newdefault = new.search_one('default') olddefault = old.search_one('default') if olddefault is None and newdefault is None: return if olddefault is not None and newdefault is None: err_def_removed(olddefault, new, ctx) elif olddefault is None and newdefault is not None: # default added, check old implicit default oldtype = old.search_one('type') if (oldtype.i_typedef is not None and hasattr(oldtype.i_typedef, 'i_default_str') and oldtype.i_typedef.i_default_str is not None and oldtype.i_typedef.i_default_str != newdefault.arg): err_add(ctx.errors, newdefault.pos, 'CHK_IMPLICIT_DEFAULT', ()) elif olddefault.arg != newdefault.arg: err_def_changed(olddefault, newdefault, ctx)