Refactor augment generator linkage 61/99161/1
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 21 Dec 2021 13:22:03 +0000 (14:22 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 24 Dec 2021 21:53:52 +0000 (22:53 +0100)
The uses/augment and module/augment cases are quite different from
the perspective how we map their argument reference to generators.

The uses/augment case is quite simple, as it refers to grouping's nodes,
not augmentations. The only variation we need to account for is the
skip back to the defining grouping.

The module/augment case, on the other hand, is more complex, as it can
refer to augmentations -- hence it need to account for changes namespace
changes along uses/grouping as well as back from grouping child to
augmentations.

JIRA: MDSAL-715
Change-Id: I7425301afa6de566d985d19450ccfc64ea527119
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit d601e4b48e111846c005b6f773634e7a5c28e89f)

binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractCompositeGenerator.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/AbstractExplicitGenerator.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/GeneratorContext.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/GeneratorReactor.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/ModuleAugmentGenerator.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/UsesAugmentGenerator.java
binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal715Test.java [new file with mode: 0644]
binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-1.yang [new file with mode: 0644]
binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-2.yang [new file with mode: 0644]
binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-3.yang [new file with mode: 0644]
binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-4.yang [new file with mode: 0644]

index f75c0e6ac3bd3ed6a34ec05d832fce28fcb94f98..13776a52841b537cfc5ff941fea15f3398139a12 100644 (file)
@@ -23,6 +23,7 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
 import org.opendaylight.mdsal.binding.model.ri.BindingTypes;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
@@ -42,6 +43,7 @@ import org.opendaylight.yangtools.yang.model.api.stmt.ListEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.NotificationEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.OutputEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.RpcEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.TypedefEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
@@ -140,21 +142,11 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
     }
 
     final void linkUsesDependencies(final GeneratorContext context) {
-        // We are establishing two linkages here:
-        // - we are resolving 'uses' statements to their corresponding 'grouping' definitions
-        // - we propagate those groupings as anchors to any augment statements
+        // We are resolving 'uses' statements to their corresponding 'grouping' definitions
         final List<GroupingGenerator> tmp = new ArrayList<>();
         for (EffectiveStatement<?, ?> stmt : statement().effectiveSubstatements()) {
             if (stmt instanceof UsesEffectiveStatement) {
-                final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt;
-                final GroupingGenerator grouping = context.resolveTreeScoped(GroupingGenerator.class, uses.argument());
-                tmp.add(grouping);
-
-                for (Generator gen : this) {
-                    if (gen instanceof UsesAugmentGenerator) {
-                        ((UsesAugmentGenerator) gen).linkGroupingDependency(uses, grouping);
-                    }
-                }
+                tmp.add(context.resolveTreeScoped(GroupingGenerator.class, ((UsesEffectiveStatement) stmt).argument()));
             }
         }
         groupings = List.copyOf(tmp);
@@ -189,7 +181,7 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
     }
 
     @Override
-    final @Nullable AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
+    final AbstractExplicitGenerator<?> findSchemaTreeGenerator(final QName qname) {
         final AbstractExplicitGenerator<?> found = super.findSchemaTreeGenerator(qname);
         return found != null ? found : findInferredGenerator(qname);
     }
@@ -213,6 +205,56 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
         return null;
     }
 
+    final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final SchemaNodeIdentifier path) {
+        // This is not quite straightforward. 'path' works on top of schema tree, which is instantiated view. Since we
+        // do not generate duplicate instantiations along 'uses' path, findSchemaTreeGenerator() would satisfy our
+        // request by returning a child of the source 'grouping'.
+        //
+        // When that happens, our subsequent lookups need to adjust the namespace being looked up to the grouping's
+        // namespace... except for the case when the step is actually an augmentation, in which case we must not make
+        // that adjustment.
+        //
+        // Hence we deal with this lookup recursively, dropping namespace hints when we cross into groupings.
+        return resolveSchemaNode(path.getNodeIdentifiers().iterator(), null);
+    }
+
+    private @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final Iterator<QName> qnames,
+            final @Nullable QNameModule localNamespace) {
+        final QName qname = qnames.next();
+
+        // First try local augments, as those are guaranteed to match namespace exactly
+        for (AbstractAugmentGenerator augment : augments) {
+            final AbstractExplicitGenerator<?> gen = augment.findSchemaTreeGenerator(qname);
+            if (gen != null) {
+                return resolveNext(gen, qnames, null);
+            }
+        }
+
+        // Second try local groupings, as those perform their own adjustment
+        for (GroupingGenerator grouping : groupings) {
+            final QNameModule ns = grouping.statement().argument().getModule();
+            final AbstractExplicitGenerator<?> gen = grouping.findSchemaTreeGenerator(qname.bindTo(ns));
+            if (gen != null) {
+                return resolveNext(gen, qnames, ns);
+            }
+        }
+
+        // Lastly try local statements adjusted with namespace, if applicable
+        final QName lookup = localNamespace == null ? qname : qname.bindTo(localNamespace);
+        final AbstractExplicitGenerator<?> gen = verifyNotNull(super.findSchemaTreeGenerator(lookup),
+            "Failed to find %s as %s in %s", qname, lookup, this);
+        return resolveNext(gen, qnames, localNamespace);
+    }
+
+    private static @NonNull AbstractExplicitGenerator<?> resolveNext(final @NonNull AbstractExplicitGenerator<?> gen,
+            final Iterator<QName> qnames, final QNameModule localNamespace) {
+        if (qnames.hasNext()) {
+            verify(gen instanceof AbstractCompositeGenerator, "Unexpected generator %s", gen);
+            return ((AbstractCompositeGenerator<?>) gen).resolveSchemaNode(qnames, localNamespace);
+        }
+        return gen;
+    }
+
     /**
      * Update the specified builder to implement interfaces generated for the {@code grouping} statements this generator
      * is using.
@@ -338,7 +380,7 @@ abstract class AbstractCompositeGenerator<T extends EffectiveStatement<?, ?>> ex
                 final UsesEffectiveStatement uses = (UsesEffectiveStatement) stmt;
                 for (EffectiveStatement<?, ?> usesSub : uses.effectiveSubstatements()) {
                     if (usesSub instanceof AugmentEffectiveStatement) {
-                        tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, this, uses));
+                        tmpAug.add(new UsesAugmentGenerator((AugmentEffectiveStatement) usesSub, this));
                     }
                 }
             } else {
index 9a59a88f1459c671ec8d6e8e4773b9050b4b320b..3f862b2c928b78ee00fe17e5963dcd6cab94ff01 100644 (file)
@@ -8,7 +8,6 @@
 package org.opendaylight.mdsal.binding.generator.impl.reactor;
 
 import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects.ToStringHelper;
@@ -24,12 +23,10 @@ import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuil
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.yang.common.AbstractQName;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.AddedByUsesAware;
 import org.opendaylight.yangtools.yang.model.api.CopyableNode;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.DescriptionEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -107,27 +104,6 @@ public abstract class AbstractExplicitGenerator<T extends EffectiveStatement<?,
         return null;
     }
 
-    final @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(final @NonNull SchemaNodeIdentifier path,
-            final @Nullable QNameModule targetModule) {
-        AbstractExplicitGenerator<?> current = this;
-        QNameModule currentModule = targetModule;
-
-        for (QName next : path.getNodeIdentifiers()) {
-            final QName qname = currentModule == null ? next : next.bindTo(currentModule);
-            current = verifyNotNull(current.findSchemaTreeGenerator(qname),
-                "Failed to find %s as %s in %s", next, qname, current);
-
-            final QNameModule foundNamespace = current.getQName().getModule();
-            if (!foundNamespace.equals(qname.getModule())) {
-                // We have located a different QName than the one we were looking for. We need to make sure we adjust
-                // all subsequent QNames to this new namespace
-                currentModule = foundNamespace;
-            }
-        }
-
-        return current;
-    }
-
     final @NonNull QName getQName() {
         final Object arg = statement.argument();
         verify(arg instanceof QName, "Unexpected argument %s", arg);
index ab0f3b2788ce0be329b0bac26580d1c0e94c7ff5..fb17e65bfe22edff54dafd9d08e973d45f7a03fe 100644 (file)
@@ -10,9 +10,9 @@ package org.opendaylight.mdsal.binding.generator.impl.reactor;
 import edu.umd.cs.findbugs.annotations.Nullable;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.model.api.PathExpression;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 
 /**
  * Abstract view on generation tree as viewed by a particular {@link Generator}.
@@ -43,9 +43,19 @@ abstract class GeneratorContext {
     abstract <E extends EffectiveStatement<QName, ?>, G extends AbstractExplicitGenerator<E>>
         @NonNull G resolveTreeScoped(@NonNull Class<G> type, @NonNull QName argument);
 
-    abstract @NonNull AbstractExplicitGenerator<?> resolveSchemaNode(@NonNull SchemaNodeIdentifier path);
+    abstract @NonNull ModuleGenerator resolveModule(@NonNull QNameModule namespace);
 
-    abstract @NonNull IdentityGenerator resolveIdentity(@NonNull QName name);
+    final @NonNull IdentityGenerator resolveIdentity(final @NonNull QName name) {
+        for (Generator gen : resolveModule(name.getModule())) {
+            if (gen instanceof IdentityGenerator) {
+                final IdentityGenerator idgen = (IdentityGenerator) gen;
+                if (name.equals(idgen.statement().argument())) {
+                    return idgen;
+                }
+            }
+        }
+        throw new IllegalStateException("Failed to find identity " + name);
+    }
 
     final @NonNull TypedefGenerator resolveTypedef(final @NonNull QName qname) {
         return resolveTreeScoped(TypedefGenerator.class, qname);
index b93cd50bd6cfa6b00659d593569c867ccf8a255e..57d1899219ea70affc222d954799bd4736af61bf 100644 (file)
@@ -7,8 +7,10 @@
  */
 package org.opendaylight.mdsal.binding.generator.impl.reactor;
 
+import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Verify.verify;
 import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.Stopwatch;
 import com.google.common.base.VerifyException;
@@ -34,7 +36,6 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 import org.opendaylight.yangtools.yang.model.api.PathExpression;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 import org.opendaylight.yangtools.yang.model.ri.type.TypeBuilder;
 import org.opendaylight.yangtools.yang.model.spi.ModuleDependencySort;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
@@ -213,13 +214,6 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
         }
     }
 
-    @Override
-    AbstractExplicitGenerator<?> resolveSchemaNode(final SchemaNodeIdentifier path) {
-        verify(path instanceof SchemaNodeIdentifier.Absolute, "Unexpected path %s", path);
-        return verifyNotNull(generators.get(path.firstNodeIdentifier().getModule()), "Cannot find module for %s", path)
-            .resolveSchemaNode(path, null);
-    }
-
     @Override
     <E extends EffectiveStatement<QName, ?>, G extends AbstractExplicitGenerator<E>> G resolveTreeScoped(
             final Class<G> type, final QName argument) {
@@ -260,19 +254,10 @@ public final class GeneratorReactor extends GeneratorContext implements Mutable
     }
 
     @Override
-    IdentityGenerator resolveIdentity(final QName name) {
-        final ModuleGenerator module = generators.get(name.getModule());
-        if (module != null) {
-            for (Generator gen : module) {
-                if (gen instanceof IdentityGenerator) {
-                    final IdentityGenerator idgen = (IdentityGenerator) gen;
-                    if (name.equals(idgen.statement().argument())) {
-                        return idgen;
-                    }
-                }
-            }
-        }
-        throw new IllegalStateException("Failed to find identity " + name);
+    ModuleGenerator resolveModule(final QNameModule namespace) {
+        final ModuleGenerator module = generators.get(requireNonNull(namespace));
+        checkState(module != null, "Failed to find module for %s", namespace);
+        return module;
     }
 
     @Override
index 25658cf294ec1420f765340dabb64dcb55aaaef3..f4c01a3e7e0f38bab1a9135925e0eb0620033a9b 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.mdsal.binding.generator.impl.reactor;
 
 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 
 /**
  * Generator corresponding to a {@code augment} statement used as a child of a {@code module} statement.
@@ -23,19 +24,8 @@ final class ModuleAugmentGenerator extends AbstractAugmentGenerator {
     }
 
     void linkAugmentationTarget(final GeneratorContext context) {
-        // FIXME: do we need two-step resolution here? we probably have solved this somehow, or it's part of...
-        // FIXME: MDSAL-696: this looks like the sort of check which should be involved in replacing getOriginal()
-        //
-        //      if (targetSchemaNode instanceof DataSchemaNode && ((DataSchemaNode) targetSchemaNode).isAddedByUses()) {
-        //          if (targetSchemaNode instanceof DerivableSchemaNode) {
-        //              targetSchemaNode = ((DerivableSchemaNode) targetSchemaNode).getOriginal().orElse(null);
-        //          }
-        //          if (targetSchemaNode == null) {
-        //              throw new IllegalStateException("Failed to find target node from grouping in augmentation "
-        //                  + augSchema + " in module " + context.module().getName());
-        //          }
-        //      }
-
-        setTargetGenerator(context.resolveSchemaNode(statement().argument()));
+        final SchemaNodeIdentifier path = statement().argument();
+        final ModuleGenerator module = context.resolveModule(path.firstNodeIdentifier().getModule());
+        setTargetGenerator(module.resolveSchemaNode(path));
     }
 }
index 919ac5fccf2f7035b094e791d02d83eae1c2c5c4..f8a9eb7fb2ca843465ae44cf1723147fdbb011ef 100644 (file)
@@ -7,51 +7,26 @@
  */
 package org.opendaylight.mdsal.binding.generator.impl.reactor;
 
-import static com.google.common.base.Verify.verify;
-import static com.google.common.base.Verify.verifyNotNull;
-import static java.util.Objects.requireNonNull;
-
 import org.opendaylight.yangtools.yang.model.api.stmt.AugmentEffectiveStatement;
-import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
-import org.opendaylight.yangtools.yang.model.api.stmt.UsesEffectiveStatement;
 
 /**
  * Generator corresponding to a {@code augment} statement used as a child of a {@code uses} statement.
  */
 final class UsesAugmentGenerator extends AbstractAugmentGenerator {
-    private final UsesEffectiveStatement uses;
-
-    private GroupingGenerator grouping;
-
-    UsesAugmentGenerator(final AugmentEffectiveStatement statement, final AbstractCompositeGenerator<?> parent,
-            final UsesEffectiveStatement uses) {
+    UsesAugmentGenerator(final AugmentEffectiveStatement statement, final AbstractCompositeGenerator<?> parent) {
         super(statement, parent);
-        this.uses = requireNonNull(uses);
-    }
-
-    void linkGroupingDependency(final UsesEffectiveStatement checkUses, final GroupingGenerator resolvedGrouping) {
-        if (uses.equals(checkUses)) {
-            verify(grouping == null, "Attempted to relink %s from %s to %s", this, grouping, resolvedGrouping);
-            this.grouping = requireNonNull(resolvedGrouping);
-        }
     }
 
     @Override
     void loadTargetGenerator() {
-        final GroupingGenerator grp = verifyNotNull(grouping, "No grouping linked in %s", this);
-        final SchemaNodeIdentifier path = statement().argument();
-
-        /*
-         *  Here we are going in the opposite direction of RFC7950, section 3.13:
-         *
-         *        The effect of a "uses" reference to a grouping is that the nodes
-         *        defined by the grouping are copied into the current schema tree and
-         *        are then updated according to the "refine" and "augment" statements.
-         *
-         *  Our argument is composed of QNames in the current schema tree's namespace, but the grouping may have been
-         *  defined in a different module -- and therefore it knows those children under its namespace. Adjust the path
-         *  we are searching if that happens to be the case.
-         */
-        setTargetGenerator(grp.resolveSchemaNode(path, grp.statement().argument().getModule()));
+        // Here we are going in the opposite direction of RFC7950, section 7.13:
+        //
+        //    The effect of a "uses" reference to a grouping is that the nodes
+        //    defined by the grouping are copied into the current schema tree and
+        //    are then updated according to the "refine" and "augment" statements.
+        //
+        // Our parent here is *not* the uses statement, but rather the statement which contains uses -- and its
+        // getSchemaTreeGenerator() is well equipped to deal with the namespace hopping needed to perform the lookups
+        setTargetGenerator(getParent().resolveSchemaNode(statement().argument()));
     }
 }
diff --git a/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal715Test.java b/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal715Test.java
new file mode 100644 (file)
index 0000000..01a4d90
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2021 PANTHEON.tech, 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.mdsal.binding.generator.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+
+public class Mdsal715Test {
+    @Test
+    public void testAugmentLinking() {
+        final var generatedTypes = DefaultBindingGenerator.generateFor(
+            YangParserTestUtils.parseYangResourceDirectory("/mdsal715"));
+        assertEquals(12, generatedTypes.size());
+    }
+}
diff --git a/binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-1.yang b/binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-1.yang
new file mode 100644 (file)
index 0000000..df7e570
--- /dev/null
@@ -0,0 +1,14 @@
+module test-module-1 {
+  namespace "test-module-1";
+  prefix "tm1";
+
+  import test-module-2 {
+    prefix tm2;
+  }
+
+  container test-cont {
+    list test-list {
+      uses tm2:cont-1-grouping;
+    }
+  }
+}
diff --git a/binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-2.yang b/binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-2.yang
new file mode 100644 (file)
index 0000000..eb14cd3
--- /dev/null
@@ -0,0 +1,14 @@
+module test-module-2 {
+  namespace "test-module-2";
+  prefix "tm2";
+
+  import test-module-3 {
+    prefix tm3;
+  }
+
+  grouping cont-1-grouping {
+    container used-cont-1 {
+      uses tm3:cont-2-grouping;
+    }
+  }
+}
diff --git a/binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-3.yang b/binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-3.yang
new file mode 100644 (file)
index 0000000..40ab21e
--- /dev/null
@@ -0,0 +1,8 @@
+module test-module-3 {
+  namespace "test-module-3";
+  prefix "tm3";
+
+  grouping cont-2-grouping {
+    container used-cont-2;
+  }
+}
diff --git a/binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-4.yang b/binding/mdsal-binding-generator/src/test/resources/mdsal715/test-module-4.yang
new file mode 100644 (file)
index 0000000..7fb3b9c
--- /dev/null
@@ -0,0 +1,18 @@
+module test-module-4 {
+  namespace "test-module-4";
+  prefix "tm4";
+
+  import test-module-1 {
+      prefix tm1;
+  }
+
+  augment "/tm1:test-cont/tm1:test-list/tm1:used-cont-1/tm1:used-cont-2" {
+    container augmented-cont-1 {
+      list augmented-list;
+    }
+  }
+
+  augment "/tm1:test-cont/tm1:test-list/tm1:used-cont-1/tm1:used-cont-2/tm4:augmented-cont-1/tm4:augmented-list" {
+    container augmented-cont-2;
+  }
+}