Skip to content

Commit

Permalink
Set correct List key type in gNMI simulator
Browse files Browse the repository at this point in the history
Previously, the List key was always set as a String for
YangInstanceIdentifier generated from gNMI path. This prevented data
from being merged into the datastore, as this type of key was not found
in the datastore.

This commit addresses the issue by setting the correct value defined
in the typeDefinition QName.

JIRA: LIGHTY-285
Signed-off-by: Peter Suna <[email protected]>
Signed-off-by: Ivan Hrasko <[email protected]>
(cherry picked from commit 78ae7fd)
  • Loading branch information
PeterSuna authored and ihrasko committed Mar 20, 2024
1 parent cd63885 commit d67a8e6
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,21 @@
import java.time.Instant;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.opendaylight.yangtools.yang.common.Decimal64;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.common.Uint16;
import org.opendaylight.yangtools.yang.common.Uint32;
import org.opendaylight.yangtools.yang.common.Uint64;
import org.opendaylight.yangtools.yang.common.Uint8;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
Expand All @@ -43,7 +49,12 @@
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.TypeDefinitions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -373,14 +384,70 @@ private Optional<DataSchemaNode> findAugmentationFromOuterModel(final String ele
.findFirst();
}

private static YangInstanceIdentifier getYIIDWithNewPredicateNode(final YangInstanceIdentifier resultIdentifier,
private YangInstanceIdentifier getYIIDWithNewPredicateNode(final YangInstanceIdentifier resultIdentifier,
final PathElem currentElement) {
final var qname = resultIdentifier.getLastPathArgument().getNodeType();
final Map<QName, Object> keysMap = currentElement.getKeyMap().entrySet().stream()
.collect(Collectors.toMap(e -> QName.create(qname, e.getKey()), Map.Entry::getValue));
.collect(Collectors.toMap(e -> QName.create(qname, e.getKey()), Map.Entry::getValue));

for (final var entry : keysMap.entrySet()) {
final var keyNode = resultIdentifier.node(NodeIdentifierWithPredicates.of(qname, keysMap))
.node(entry.getKey());
final var baseTypeDef = getBaseTypeDef(keyNode);
entry.setValue(mapToCorrectDataType(baseTypeDef, (String) entry.getValue()));
}
return resultIdentifier.node(NodeIdentifierWithPredicates.of(qname, keysMap));
}

private TypeDefinition<?> getBaseTypeDef(final YangInstanceIdentifier identifier) {
final var nodeAndStack = DataSchemaContextTree.from(context).enterPath(identifier).get();
final var dataSchemaNode = nodeAndStack.node().getDataSchemaNode();
var resultDataSchemaType = ((TypedDataSchemaNode) dataSchemaNode).getType();
if (resultDataSchemaType instanceof LeafrefTypeDefinition leafRefType) {
final var leafRefPathOrig = leafRefType.getPathStatement().getOriginalString();
final var leafRefPathList = Arrays.stream(leafRefPathOrig.split("/")).filter(s -> !s.isEmpty()).toList();
final var stack = nodeAndStack.stack();
for (final var path : leafRefPathList) {
if ("..".equals(path)) {
stack.exit();
} else {
stack.enterSchemaTree(QName.create(stack.currentModule().localQNameModule(), path));
}
}
resultDataSchemaType = ((TypedDataSchemaNode) stack.currentStatement()).getType();
}
return resultDataSchemaType.getBaseType() != null ? resultDataSchemaType.getBaseType() : resultDataSchemaType;
}

private Object mapToCorrectDataType(final TypeDefinition<?> typeDefinition, final String value) {
final var qname = typeDefinition.getQName();
if (typeDefinition instanceof IdentityrefTypeDefinition identityType) {
final var firstIdentity = identityType.getIdentities().iterator().next();
final var identityQname = firstIdentity.getQName();
final var values = value.split(":");
final var identityRefName = values[values.length - 1];
return QName.create(identityQname, identityRefName);
} else if (qname.equals(TypeDefinitions.BOOLEAN)) {
return Boolean.valueOf(value);
} else if (qname.equals(TypeDefinitions.DECIMAL64)) {
return Decimal64.valueOf(value);
} else if (qname.equals(TypeDefinitions.INT8) || qname.equals(TypeDefinitions.INT16)
|| qname.equals(TypeDefinitions.INT32) || qname.equals(TypeDefinitions.INT64)) {
return Integer.parseInt(value);
} else if (qname.equals(TypeDefinitions.UINT8)) {
return Uint8.valueOf(value);
} else if (qname.equals(TypeDefinitions.UINT16)) {
return Uint16.valueOf(value);
} else if (qname.equals(TypeDefinitions.UINT32)) {
return Uint32.valueOf(value);
} else if (qname.equals(TypeDefinitions.UINT64)) {
return Uint64.valueOf(value);
} else {
// Other types which can be sent as a String type.
return value;
}
}

private static YangInstanceIdentifier addAugmentationNodeToIdentifier(final YangInstanceIdentifier identifier,
final DataSchemaNode augmentationDataNode) {
return addAugmentationNodeToIdentifier(identifier, augmentationDataNode.getQName());
Expand All @@ -391,6 +458,6 @@ private static YangInstanceIdentifier addAugmentationNodeToIdentifier(final Yang
final HashSet<QName> augmentationQname = new HashSet<>();
augmentationQname.add(augmentation);
return identifier.node(AugmentationIdentifier.create(augmentationQname))
.node(augmentation);
.node(augmentation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,67 @@ public void setAugmentedTestInterfaceConfigTest() throws Exception {
assertEquals("UPDATE", setResponse.getResponse(0).getOp().toString());
}

@Test
public void setContainerWithMultipleListKeyInPathTest() throws Exception {
final var testDataPath = Gnmi.Path.newBuilder()
.addElem(Gnmi.PathElem.newBuilder()
.setName("gnmi-test-model:test-data")
.build())
.build();
final var multipleListKeyUpdate = Gnmi.Update.newBuilder()
.setPath(testDataPath)
.setVal(Gnmi.TypedValue.newBuilder()
.setJsonIetfVal(ByteString.copyFromUtf8("""
{
"multiple-key-list" : [
{
"number": 10,
"leafref-key": 15,
"identityref-key": "openconfig-aaa-types:SYSTEM_ROLE_ADMIN",
"union-key": "unbounded"
}
]
}
"""))
.build())
.build();

final var innerDataPath = Gnmi.Path.newBuilder()
.addElem(Gnmi.PathElem.newBuilder()
.setName("gnmi-test-model:test-data")
.build())
.addElem(Gnmi.PathElem.newBuilder()
.setName("multiple-key-list")
.putKey("number", "10")
.putKey("leafref-key", "15")
.putKey("identityref-key", "openconfig-aaa-types:SYSTEM_ROLE_ADMIN")
.putKey("union-key", "unbounded")
.build())
.addElem(Gnmi.PathElem.newBuilder()
.setName("inner-container")
.build())
.build();
final var innerDataUpdate = Gnmi.Update.newBuilder()
.setPath(innerDataPath)
.setVal(Gnmi.TypedValue.newBuilder()
.setJsonIetfVal(ByteString.copyFromUtf8("""
{
"inner-data": "data"
}
"""))
.build())
.build();
final var setRequest = Gnmi.SetRequest.newBuilder()
.addUpdate(multipleListKeyUpdate)
.addUpdate(innerDataUpdate)
.build();
LOG.info("Sending set request:\n{}", setRequest);

final var setResponse = sessionProvider.getGnmiSession().set(setRequest).get();
assertEquals("UPDATE", setResponse.getResponse(0).getOp().toString());
assertEquals("UPDATE", setResponse.getResponse(1).getOp().toString());
}

@Test
public void crudComplexValueTest() throws ExecutionException, InterruptedException, IOException, JSONException {
final Gnmi.Path path = Gnmi.Path.newBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module gnmi-test-model {
prefix "gtm";

import openconfig-extensions { prefix oc-ext; }
import openconfig-inet-types { prefix inet; }
import openconfig-aaa-types { prefix types; }

oc-ext:openconfig-version "1.0.0";

Expand All @@ -24,6 +26,35 @@ module gnmi-test-model {
type string;
}
}
list multiple-key-list {
key "number leafref-key identityref-key union-key";
leaf number {
type inet:as-number;
}
leaf leafref-key {
type leafref {
path "../number";
}
}
leaf identityref-key {
type identityref {
base "types:SYSTEM_DEFINED_ROLES";
}
}
leaf union-key {
type union {
type int32;
type enumeration {
enum "unbounded";
}
}
}
container inner-container {
leaf inner-data {
type string;
}
}
}
}

list base-list {
Expand Down

0 comments on commit d67a8e6

Please sign in to comment.