Add support for yang-data extension to the YANG statement parser.
This extension is defined in RFC8040:
https://tools.ietf.org/html/rfc8040#section-8
Config and if-feature statements are ignored
when placed within a yang-data extension body.
Change-Id: Iccc1ffc76cf1ba1032552e68a4591d44f586c889
Signed-off-by: Igor Foltin <igor.foltin@pantheon.tech>
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.model.api;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Represents 'yang-data' extension statement defined in https://tools.ietf.org/html/rfc8040#section-8
+ * This statement must appear as a top-level statement, otherwise it is ignored and does not appear in the final
+ * schema context. It must contain exactly one top-level container node (directly or indirectly via a uses statement).
+ */
+@Beta
+public interface YangDataSchemaNode extends UnknownSchemaNode {
+
+ /**
+ * Returns container schema node.
+ *
+ * @return container schema node
+ */
+ ContainerSchemaNode getContainer();
+}
boolean isConfiguration();
+ /**
+ * Checks whether this statement is placed within a 'yang-data' extension statement.
+ * Some YANG statements are constrained when used within a 'yang-data' statement.
+ * See the following link for more information - https://tools.ietf.org/html/rfc8040#section-8
+ *
+ * @return true if it is placed within a 'yang-data' extension statement, otherwise false
+ */
+ boolean isInYangDataExtensionBody();
+
boolean isEnabledSemanticVersioning();
@Nonnull
import org.opendaylight.yangtools.yang.parser.stmt.reactor.RootStatementContext;
import org.opendaylight.yangtools.yang.parser.stmt.reactor.StatementContextBase;
import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.UnknownStatementImpl;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangDataStatementImpl;
public final class StmtContextUtils {
public static final Splitter LIST_KEY_SPLITTER = Splitter.on(' ').omitEmptyStrings().trimResults();
return false;
}
+ /**
+ * Checks if the statement context has a 'yang-data' extension node as its parent.
+ *
+ * @param stmtCtx statement context to be checked
+ * @return true if the parent node is a 'yang-data' node, otherwise false
+ */
+ public static boolean hasYangDataExtensionParent(final StmtContext<?, ?, ?> stmtCtx) {
+ return producesDeclared(stmtCtx.getParentContext(), YangDataStatementImpl.class);
+ }
+
public static boolean isUnknownStatement(final StmtContext<?, ?, ?> stmtCtx) {
return producesDeclared(stmtCtx, UnknownStatementImpl.class);
}
boolean containsIfFeature = false;
for (final StatementContextBase<?, ?, ?> stmt : stmtContext.declaredSubstatements()) {
if (YangStmtMapping.IF_FEATURE.equals(stmt.getPublicDefinition())) {
+ if (stmtContext.isInYangDataExtensionBody()) {
+ break;
+ }
+
containsIfFeature = true;
if (((Predicate<Set<QName>>) stmt.getStatementArgument()).test(supportedFeatures)) {
isSupported = true;
return true;
}
+ @Override
+ public boolean isInYangDataExtensionBody() {
+ return false;
+ }
+
@Override
public boolean isEnabledSemanticVersioning() {
return sourceContext.isEnabledSemanticVersioning();
*/
private boolean haveConfiguration;
private boolean configuration;
+ private boolean wasCheckedIfInYangDataExtensionBody;
+ private boolean isInYangDataExtensionBody;
private volatile SchemaPath schemaPath;
@Override
public boolean isConfiguration() {
+ // if this statement is within a 'yang-data' extension body, config substatements are ignored as if
+ // they were not declared. As 'yang-data' is always a top-level node, all configs that are within it are
+ // automatically true
+ if (isInYangDataExtensionBody()) {
+ return true;
+ }
+
if (haveConfiguration) {
return configuration;
}
return isConfig;
}
+ @Override
+ public boolean isInYangDataExtensionBody() {
+ if (wasCheckedIfInYangDataExtensionBody) {
+ return isInYangDataExtensionBody;
+ }
+
+ final boolean parentIsInYangDataExtensionBody = parent.isInYangDataExtensionBody();
+ if (parentIsInYangDataExtensionBody) {
+ isInYangDataExtensionBody = parentIsInYangDataExtensionBody;
+ } else {
+ isInYangDataExtensionBody = StmtContextUtils.hasYangDataExtensionParent(this);
+ }
+
+ wasCheckedIfInYangDataExtensionBody = true;
+ return isInYangDataExtensionBody;
+ }
+
@Override
public boolean isEnabledSemanticVersioning() {
return parent.isEnabledSemanticVersioning();
import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.AnyxmlSchemaLocationEffectiveStatementImpl;
import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.OpenconfigVersionEffectiveStatementImpl;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.YangDataEffectiveStatementImpl;
@Beta
public enum SupportedExtensionsMapping implements StatementDefinition {
"anyxml-schema-location", "target-node", false),
OPENCONFIG_VERSION("http://openconfig.net/yang/openconfig-ext",
OpenconfigVersionStatementImpl.class, OpenconfigVersionEffectiveStatementImpl.class,
- "openconfig-version", "semver", false);
+ "openconfig-version", "semver", false),
+ YANG_DATA("urn:ietf:params:xml:ns:yang:ietf-restconf", "2017-01-26", YangDataStatementImpl.class,
+ YangDataEffectiveStatementImpl.class, "yang-data", "name", true);
private final Class<? extends DeclaredStatement<?>> type;
private final Class<? extends EffectiveStatement<?, ?>> effectiveType;
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+import org.opendaylight.yangtools.yang.parser.spi.SubstatementValidator;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractDeclaredStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.AbstractStatementSupport;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.YangDataEffectiveStatementImpl;
+
+/**
+ * Declared statement representation of 'yang-data' extension defined in https://tools.ietf.org/html/rfc8040#section-8
+ */
+@Beta
+public class YangDataStatementImpl extends AbstractDeclaredStatement<String> implements UnknownStatement<String> {
+ private static final SubstatementValidator SUBSTATEMENT_VALIDATOR = SubstatementValidator.builder(
+ SupportedExtensionsMapping.YANG_DATA)
+ .addMandatory(YangStmtMapping.CONTAINER)
+ .addOptional(YangStmtMapping.USES)
+ .build();
+
+ YangDataStatementImpl(final StmtContext<String, UnknownStatement<String>, ?> ctx) {
+ super(ctx);
+ }
+
+ public static class YangDataSupport extends AbstractStatementSupport<String, UnknownStatement<String>,
+ EffectiveStatement<String, UnknownStatement<String>>> {
+
+ public YangDataSupport() {
+ super(SupportedExtensionsMapping.YANG_DATA);
+ }
+
+ @Override
+ protected SubstatementValidator getSubstatementValidator() {
+ return SUBSTATEMENT_VALIDATOR;
+ }
+
+ @Override
+ public String parseArgumentValue(final StmtContext<?, ?, ?> ctx, String value) {
+ return value;
+ }
+
+ @Override
+ public UnknownStatement<String> createDeclared(final StmtContext<String, UnknownStatement<String>, ?> ctx) {
+ return new YangDataStatementImpl(ctx);
+ }
+
+ @Override
+ public EffectiveStatement<String, UnknownStatement<String>> createEffective(final StmtContext<String,
+ UnknownStatement<String>, EffectiveStatement<String, UnknownStatement<String>>> ctx) {
+ // in case of yang-data node we need to perform substatement validation at the point when we have
+ // effective substatement contexts already available - if the node has only a uses statement declared in it,
+ // one top-level container node may very well be added to the yang-data as an effective statement
+ SUBSTATEMENT_VALIDATOR.validate(ctx);
+ return new YangDataEffectiveStatementImpl(ctx);
+ }
+
+ @Override
+ public void onFullDefinitionDeclared(final Mutable<String, UnknownStatement<String>,
+ EffectiveStatement<String, UnknownStatement<String>>> ctx) {
+ // as per https://tools.ietf.org/html/rfc8040#section-8,
+ // yang-data is ignored unless it appears as a top-level statement
+ if (!ctx.getParentContext().isRootContext()) {
+ ctx.setIsSupportedToBuildEffective(false);
+ }
+ }
+ }
+
+ @Override
+ public String getArgument() {
+ return argument();
+ }
+}
.addSupport(new ValueStatementImpl.Definition())
.addSupport(new AnyxmlSchemaLocationStatementImpl.AnyxmlSchemaLocationSupport())
.addSupport(treeScoped(AnyxmlSchemaLocationNamespace.class))
+ .addSupport(new YangDataStatementImpl.YangDataSupport())
.addSupport(global(StmtOrderingNamespace.class))
.build();
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import java.util.Objects;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.YangDataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.stmt.UnknownStatement;
+import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
+
+/**
+ * Effective statement representation of 'yang-data' extension defined in https://tools.ietf.org/html/rfc8040#section-8
+ */
+@Beta
+public final class YangDataEffectiveStatementImpl extends UnknownEffectiveStatementBase<String>
+ implements YangDataSchemaNode {
+
+ private final SchemaPath path;
+ private final QName maybeQNameArgument;
+ private final ContainerSchemaNode containerSchemaNode;
+
+ public YangDataEffectiveStatementImpl(final StmtContext<String, UnknownStatement<String>, ?> ctx) {
+ super(ctx);
+
+ QName maybeQNameArgumentInit;
+ try {
+ maybeQNameArgumentInit = Utils.qNameFromArgument(ctx, argument());
+ } catch (IllegalArgumentException e) {
+ maybeQNameArgumentInit = getNodeType();
+ }
+ this.maybeQNameArgument = maybeQNameArgumentInit;
+
+ path = ctx.getParentContext().getSchemaPath().get().createChild(maybeQNameArgument);
+ containerSchemaNode = Preconditions.checkNotNull(firstEffective(ContainerEffectiveStatementImpl.class));
+ }
+
+ @Nonnull
+ @Override
+ public QName getQName() {
+ return maybeQNameArgument;
+ }
+
+ @Nonnull
+ @Override
+ public SchemaPath getPath() {
+ return path;
+ }
+
+ @Nonnull
+ @Override
+ public ContainerSchemaNode getContainer() {
+ return containerSchemaNode;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(maybeQNameArgument, path);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof YangDataEffectiveStatementImpl)) {
+ return false;
+ }
+
+ final YangDataEffectiveStatementImpl other = (YangDataEffectiveStatementImpl) obj;
+ return Objects.equals(maybeQNameArgument, other.maybeQNameArgument) && Objects.equals(path, other.path);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("qname", maybeQNameArgument).add("path", path).toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.stmt;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.stmt.StmtTestUtils.sourceForResource;
+
+import com.google.common.collect.ImmutableSet;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ExtensionDefinition;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.YangDataSchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.InvalidSubstatementException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.MissingSubstatementException;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+
+public class YangDataExtensionTest {
+
+ private static final StatementStreamSource FOO_MODULE = sourceForResource(
+ "/yang-data-extension-test/foo.yang");
+ private static final StatementStreamSource FOO_INVALID_1_MODULE = sourceForResource(
+ "/yang-data-extension-test/foo-invalid-1.yang");
+ private static final StatementStreamSource FOO_INVALID_2_MODULE = sourceForResource(
+ "/yang-data-extension-test/foo-invalid-2.yang");
+ private static final StatementStreamSource FOO_INVALID_3_MODULE = sourceForResource(
+ "/yang-data-extension-test/foo-invalid-3.yang");
+ private static final StatementStreamSource BAR_MODULE = sourceForResource(
+ "/yang-data-extension-test/bar.yang");
+ private static final StatementStreamSource BAZ_MODULE = sourceForResource(
+ "/yang-data-extension-test/baz.yang");
+ private static final StatementStreamSource FOOBAR_MODULE = sourceForResource(
+ "/yang-data-extension-test/foobar.yang");
+ private static final StatementStreamSource IETF_RESTCONF_MODULE = sourceForResource(
+ "/yang-data-extension-test/ietf-restconf.yang");
+
+ private static Date revision;
+ private static QNameModule fooModule;
+ private static QName myYangDataA;
+ private static QName myYangDataB;
+
+ @BeforeClass
+ public static void setup() throws ParseException {
+ revision = SimpleDateFormatUtil.getRevisionFormat().parse("2017-06-01");
+ fooModule = QNameModule.create(URI.create("foo"), revision);
+ myYangDataA = QName.create(fooModule, "my-yang-data-a");
+ myYangDataB = QName.create(fooModule, "my-yang-data-b");
+ }
+
+ @Test
+ public void testYangData() throws Exception {
+ final SchemaContext schemaContext = StmtTestUtils.parseYangSources(FOO_MODULE, IETF_RESTCONF_MODULE);
+ assertNotNull(schemaContext);
+
+ final Set<ExtensionDefinition> extensions = schemaContext.getExtensions();
+ assertEquals(1, extensions.size());
+
+ final Module foo = schemaContext.findModuleByName("foo", revision);
+ assertNotNull(foo);
+
+ final List<UnknownSchemaNode> unknownSchemaNodes = foo.getUnknownSchemaNodes();
+ assertEquals(2, unknownSchemaNodes.size());
+
+ YangDataSchemaNode myYangDataANode = null;
+ YangDataSchemaNode myYangDataBNode = null;
+ for (final UnknownSchemaNode unknownSchemaNode : unknownSchemaNodes) {
+ assertTrue(unknownSchemaNode instanceof YangDataSchemaNode);
+ final YangDataSchemaNode yangDataSchemaNode = (YangDataSchemaNode) unknownSchemaNode;
+ if (myYangDataA.equals(yangDataSchemaNode.getQName())) {
+ myYangDataANode = yangDataSchemaNode;
+ } else if (myYangDataB.equals(yangDataSchemaNode.getQName())) {
+ myYangDataBNode = yangDataSchemaNode;
+ }
+ }
+
+ assertNotNull(myYangDataANode);
+ assertNotNull(myYangDataBNode);
+
+ assertNotNull(myYangDataANode.getContainer());
+ assertNotNull(myYangDataBNode.getContainer());
+ }
+
+ @Test
+ public void testConfigStatementBeingIgnoredInYangDataBody() throws Exception {
+ final SchemaContext schemaContext = StmtTestUtils.parseYangSources(BAZ_MODULE, IETF_RESTCONF_MODULE);
+ assertNotNull(schemaContext);
+
+ final Module baz = schemaContext.findModuleByName("baz", revision);
+ assertNotNull(baz);
+
+ final List<UnknownSchemaNode> unknownSchemaNodes = baz.getUnknownSchemaNodes();
+ assertEquals(1, unknownSchemaNodes.size());
+
+ final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.iterator().next();
+ assertTrue(unknownSchemaNode instanceof YangDataSchemaNode);
+ final YangDataSchemaNode myYangDataNode = (YangDataSchemaNode) unknownSchemaNode;
+ assertNotNull(myYangDataNode);
+
+ final ContainerSchemaNode contInYangData = myYangDataNode.getContainer();
+ assertNotNull(contInYangData);
+ assertTrue(contInYangData.isConfiguration());
+ final ContainerSchemaNode innerCont = (ContainerSchemaNode) contInYangData.getDataChildByName(
+ QName.create(baz.getQNameModule(), "inner-cont"));
+ assertNotNull(innerCont);
+ assertTrue(innerCont.isConfiguration());
+ final ContainerSchemaNode grpCont = (ContainerSchemaNode) contInYangData.getDataChildByName(
+ QName.create(baz.getQNameModule(), "grp-cont"));
+ assertNotNull(grpCont);
+ assertTrue(grpCont.isConfiguration());
+ }
+
+ @Test
+ public void testIfFeatureStatementBeingIgnoredInYangDataBody() throws Exception {
+ final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+ reactor.setSupportedFeatures(ImmutableSet.of());
+ reactor.addSources(FOOBAR_MODULE, IETF_RESTCONF_MODULE);
+
+ final SchemaContext schemaContext = reactor.buildEffective();
+ assertNotNull(schemaContext);
+
+ final Module foobar = schemaContext.findModuleByName("foobar", revision);
+ assertNotNull(foobar);
+
+ final List<UnknownSchemaNode> unknownSchemaNodes = foobar.getUnknownSchemaNodes();
+ assertEquals(1, unknownSchemaNodes.size());
+
+ final UnknownSchemaNode unknownSchemaNode = unknownSchemaNodes.iterator().next();
+ assertTrue(unknownSchemaNode instanceof YangDataSchemaNode);
+ final YangDataSchemaNode myYangDataNode = (YangDataSchemaNode) unknownSchemaNode;
+ assertNotNull(myYangDataNode);
+
+ final ContainerSchemaNode contInYangData = myYangDataNode.getContainer();
+ assertNotNull(contInYangData);
+ final ContainerSchemaNode innerCont = (ContainerSchemaNode) contInYangData.getDataChildByName(
+ QName.create(foobar.getQNameModule(), "inner-cont"));
+ assertNotNull(innerCont);
+ final ContainerSchemaNode grpCont = (ContainerSchemaNode) contInYangData.getDataChildByName(
+ QName.create(foobar.getQNameModule(), "grp-cont"));
+ assertNotNull(grpCont);
+ }
+
+ @Test
+ public void testYangDataBeingIgnored() throws Exception {
+ // yang-data statement is ignored if it does not appear as a top-level statement
+ // i.e., it will not appear in the final SchemaContext
+ final SchemaContext schemaContext = StmtTestUtils.parseYangSources(BAR_MODULE, IETF_RESTCONF_MODULE);
+ assertNotNull(schemaContext);
+
+ final Module bar = schemaContext.findModuleByName("bar", revision);
+ assertNotNull(bar);
+ final ContainerSchemaNode cont = (ContainerSchemaNode) bar.getDataChildByName(
+ QName.create(bar.getQNameModule(), "cont"));
+ assertNotNull(cont);
+
+ final Set<ExtensionDefinition> extensions = schemaContext.getExtensions();
+ assertEquals(1, extensions.size());
+
+ final List<UnknownSchemaNode> unknownSchemaNodes = cont.getUnknownSchemaNodes();
+ assertEquals(0, unknownSchemaNodes.size());
+ }
+
+ @Test
+ public void testYangDataWithMissingTopLevelContainer() {
+ try {
+ StmtTestUtils.parseYangSources(FOO_INVALID_1_MODULE, IETF_RESTCONF_MODULE);
+ fail("Exception should have been thrown because of missing top-level container in yang-data statement.");
+ } catch (final ReactorException ex) {
+ final Throwable cause = ex.getCause();
+ assertTrue(cause instanceof MissingSubstatementException);
+ assertTrue(cause.getMessage().startsWith("YANG_DATA is missing CONTAINER. Minimal count is 1."));
+ }
+ }
+
+ @Test
+ public void testYangDataWithTwoTopLevelContainers() {
+ try {
+ StmtTestUtils.parseYangSources(FOO_INVALID_2_MODULE, IETF_RESTCONF_MODULE);
+ fail("Exception should have been thrown because of two top-level containers in yang-data statement.");
+ } catch (final ReactorException ex) {
+ final Throwable cause = ex.getCause();
+ assertTrue(cause instanceof InvalidSubstatementException);
+ assertTrue(cause.getMessage().startsWith("Maximal count of CONTAINER for YANG_DATA is 1, detected 2."));
+ }
+ }
+
+ @Test
+ public void testYangDataWithInvalidToplevelNode() {
+ try {
+ StmtTestUtils.parseYangSources(FOO_INVALID_3_MODULE, IETF_RESTCONF_MODULE);
+ fail("Exception should have been thrown because of invalid top-level node in yang-data statement.");
+ } catch (final ReactorException ex) {
+ final Throwable cause = ex.getCause();
+ assertTrue(cause instanceof InvalidSubstatementException);
+ assertTrue(cause.getMessage().startsWith("LEAF is not valid for YANG_DATA."));
+ }
+ }
+}
--- /dev/null
+module bar {
+ namespace bar;
+ prefix bar;
+
+ import ietf-restconf {
+ prefix rc;
+ revision-date 2017-01-26;
+ }
+
+ revision 2017-06-01;
+
+ container cont {
+ rc:yang-data "my-yang-data" {
+ // only one top-level container is allowed, but in this case it does not matter
+ // as the whole yang-data node is ignored (because it does not appear as a top-level statement)
+ container top-level-cont {}
+ container another-top-level-cont {}
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module baz {
+ namespace baz;
+ prefix baz;
+
+ import ietf-restconf {
+ prefix rc;
+ revision-date 2017-01-26;
+ }
+
+ revision 2017-06-01;
+
+ rc:yang-data "my-yang-data" {
+ container cont {
+ config false;
+
+ container inner-cont {
+ config false;
+ }
+
+ uses grp;
+ }
+ }
+
+ grouping grp {
+ container grp-cont {
+ config false;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo;
+ prefix foo;
+
+ import ietf-restconf {
+ prefix rc;
+ revision-date 2017-01-26;
+ }
+
+ revision 2017-06-01;
+
+ rc:yang-data "my-yang-data" {
+ // missing top level container = should throw an exception
+ }
+}
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo;
+ prefix foo;
+
+ import ietf-restconf {
+ prefix rc;
+ revision-date 2017-01-26;
+ }
+
+ revision 2017-06-01;
+
+ rc:yang-data "my-yang-data" {
+ // two top level containers, but only one is allowed = should throw an exception
+ container cont-1 {}
+ uses grp-1;
+ }
+
+ grouping grp-1 {
+ uses grp-2;
+ }
+
+ grouping grp-2 {
+ container cont-2 {}
+ }
+}
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo;
+ prefix foo;
+
+ import ietf-restconf {
+ prefix rc;
+ revision-date 2017-01-26;
+ }
+
+ revision 2017-06-01;
+
+ rc:yang-data "my-yang-data" {
+ // invalid top level node, it must be a container = should throw an exception
+ leaf lf {
+ type string;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module foo {
+ namespace foo;
+ prefix foo;
+
+ import ietf-restconf {
+ prefix rc;
+ revision-date 2017-01-26;
+ }
+
+ revision 2017-06-01;
+
+ rc:yang-data "my-yang-data-a" {
+ container cont {}
+ }
+
+ rc:yang-data "my-yang-data-b" {
+ uses grp;
+ }
+
+ grouping grp {
+ container grp-cont {}
+ }
+}
\ No newline at end of file
--- /dev/null
+module foobar {
+ namespace foobar;
+ prefix foobar;
+
+ import ietf-restconf {
+ prefix rc;
+ revision-date 2017-01-26;
+ }
+
+ revision 2017-06-01;
+
+ rc:yang-data "my-yang-data" {
+ container cont {
+ if-feature feat;
+
+ container inner-cont {
+ if-feature feat;
+ }
+
+ uses grp {
+ if-feature feat;
+ }
+ }
+ }
+
+ grouping grp {
+ container grp-cont {}
+ }
+}
\ No newline at end of file
--- /dev/null
+module ietf-restconf {
+ yang-version 1.1;
+ namespace "urn:ietf:params:xml:ns:yang:ietf-restconf";
+ prefix "rc";
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+ Author: Andy Bierman
+ <mailto:andy@yumaworks.com>
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>
+ Author: Kent Watsen
+ <mailto:kwatsen@juniper.net>";
+
+ description
+ "This module contains conceptual YANG specifications
+ for basic RESTCONF media type definitions used in
+ RESTCONF protocol messages.
+ Note that the YANG definitions within this module do not
+ represent configuration data of any kind.
+ The 'restconf-media-type' YANG extension statement
+ provides a normative syntax for XML and JSON
+ message-encoding purposes.
+ Copyright (c) 2017 IETF Trust 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 Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+ This version of this YANG module is part of RFC 8040; see
+ the RFC itself for full legal notices.";
+
+ revision 2017-01-26 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 8040: RESTCONF Protocol.";
+ }
+
+ extension yang-data {
+ argument name {
+ yin-element true;
+ }
+ description
+ "This extension is used to specify a YANG data template that
+ represents conceptual data defined in YANG. It is
+ intended to describe hierarchical data independent of
+ protocol context or specific message-encoding format.
+ Data definition statements within a yang-data extension
+ specify the generic syntax for the specific YANG data
+ template, whose name is the argument of the 'yang-data'
+ extension statement.
+ Note that this extension does not define a media type.
+ A specification using this extension MUST specify the
+ message-encoding rules, including the content media type.
+ The mandatory 'name' parameter value identifies the YANG
+ data template that is being defined. It contains the
+ template name.
+ This extension is ignored unless it appears as a top-level
+ statement. It MUST contain data definition statements
+ that result in exactly one container data node definition.
+ An instance of a YANG data template can thus be translated
+ into an XML instance document, whose top-level element
+ corresponds to the top-level container.
+ The module name and namespace values for the YANG module using
+ the extension statement are assigned to instance document data
+ conforming to the data definition statements within
+ this extension.
+ The substatements of this extension MUST follow the
+ 'data-def-stmt' rule in the YANG ABNF.
+ The XPath document root is the extension statement itself,
+ such that the child nodes of the document root are
+ represented by the data-def-stmt substatements within
+ this extension. This conceptual document is the context
+ for the following YANG statements:
+ - must-stmt
+ - when-stmt
+ - path-stmt
+ - min-elements-stmt
+ - max-elements-stmt
+ - mandatory-stmt
+ - unique-stmt
+ - ordered-by
+ - instance-identifier data type
+ The following data-def-stmt substatements are constrained
+ when used within a 'yang-data' extension statement.
+ - The list-stmt is not required to have a key-stmt defined.
+ - The if-feature-stmt is ignored if present.
+ - The config-stmt is ignored if present.
+ - The available identity values for any 'identityref'
+ leaf or leaf-list nodes are limited to the module
+ containing this extension statement and the modules
+ imported into that module.
+ ";
+ }
+
+ rc:yang-data yang-errors {
+ uses errors;
+ }
+
+ rc:yang-data yang-api {
+ uses restconf;
+ }
+
+ grouping errors {
+ description
+ "A grouping that contains a YANG container
+ representing the syntax and semantics of a
+ YANG Patch error report within a response message.";
+
+ container errors {
+ description
+ "Represents an error report returned by the server if
+ a request results in an error.";
+
+ list error {
+ description
+ "An entry containing information about one
+ specific error that occurred while processing
+ a RESTCONF request.";
+ reference
+ "RFC 6241, Section 4.3.";
+
+ leaf error-type {
+ type enumeration {
+ enum transport {
+ description
+ "The transport layer.";
+ }
+ enum rpc {
+ description
+ "The rpc or notification layer.";
+ }
+ enum protocol {
+ description
+ "The protocol operation layer.";
+ }
+ enum application {
+ description
+ "The server application layer.";
+ }
+ }
+ mandatory true;
+ description
+ "The protocol layer where the error occurred.";
+ }
+
+ leaf error-tag {
+ type string;
+ mandatory true;
+ description
+ "The enumerated error-tag.";
+ }
+
+ leaf error-app-tag {
+ type string;
+ description
+ "The application-specific error-tag.";
+ }
+
+ leaf error-path {
+ type instance-identifier;
+ description
+ "The YANG instance identifier associated
+ with the error node.";
+ }
+
+ leaf error-message {
+ type string;
+ description
+ "A message describing the error.";
+ }
+
+ anydata error-info {
+ description
+ "This anydata value MUST represent a container with
+ zero or more data nodes representing additional
+ error information.";
+ }
+ }
+ }
+ }
+
+ grouping restconf {
+ description
+ "Conceptual grouping representing the RESTCONF
+ root resource.";
+
+ container restconf {
+ description
+ "Conceptual container representing the RESTCONF
+ root resource.";
+
+ container data {
+ description
+ "Container representing the datastore resource.
+ Represents the conceptual root of all state data
+ and configuration data supported by the server.
+ The child nodes of this container can be any data
+ resources that are defined as top-level data nodes
+ from the YANG modules advertised by the server in
+ the 'ietf-yang-library' module.";
+ }
+
+ container operations {
+ description
+ "Container for all operation resources.
+ Each resource is represented as an empty leaf with the
+ name of the RPC operation from the YANG 'rpc' statement.
+ For example, the 'system-restart' RPC operation defined
+ in the 'ietf-system' module would be represented as
+ an empty leaf in the 'ietf-system' namespace. This is
+ a conceptual leaf and will not actually be found in
+ the module:
+ module ietf-system {
+ leaf system-reset {
+ type empty;
+ }
+ }
+ To invoke the 'system-restart' RPC operation:
+ POST /restconf/operations/ietf-system:system-restart
+ To discover the RPC operations supported by the server:
+ GET /restconf/operations
+ In XML, the YANG module namespace identifies the module:
+ <system-restart
+ xmlns='urn:ietf:params:xml:ns:yang:ietf-system'/>
+ In JSON, the YANG module name identifies the module:
+ { 'ietf-system:system-restart' : [null] }
+ ";
+ }
+
+ leaf yang-library-version {
+ type string {
+ pattern '\d{4}-\d{2}-\d{2}';
+ }
+ config false;
+ mandatory true;
+ description
+ "Identifies the revision date of the 'ietf-yang-library'
+ module that is implemented by this RESTCONF server.
+ Indicates the year, month, and day in YYYY-MM-DD
+ numeric format.";
+ }
+ }
+ }
+
+}
\ No newline at end of file