From 98e63173627375ef3de2750750f0c4d8ea05852c Mon Sep 17 00:00:00 2001 From: Peter Suna Date: Wed, 14 Feb 2024 09:16:00 +0100 Subject: [PATCH] Set correct Json value in gNMI simulator Utilizing Gson to convert key values from NodeIdentifierWithPredicates may produce unexpected outcomes if the key value is not parsable by Gson's default behavior. For instance, the result for Uint8(10) would be {"value":10} instead of simply 10. Ensure custom ODL types are correctly parsed by Gson. JIRA: LIGHTY-285 Signed-off-by: Peter Suna (cherry picked from commit fe6b30748d311d6f23bd0b7d0ac638d6fa665631) --- .../modules/gnmi/commons/util/JsonUtils.java | 22 +++++++++++-- .../simulatordevice/gnmi/GnmiCrudService.java | 2 +- .../GetResponseToNormalizedNodeCodec.java | 3 +- .../gnmi/test/gnmi/SimulatorCrudTest.java | 33 +++++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-commons/src/main/java/io/lighty/modules/gnmi/commons/util/JsonUtils.java b/lighty-modules/lighty-gnmi/lighty-gnmi-commons/src/main/java/io/lighty/modules/gnmi/commons/util/JsonUtils.java index c35db5f12f..222a15c7dd 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-commons/src/main/java/io/lighty/modules/gnmi/commons/util/JsonUtils.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-commons/src/main/java/io/lighty/modules/gnmi/commons/util/JsonUtils.java @@ -18,8 +18,10 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.opendaylight.yangtools.yang.common.Decimal64; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; public final class JsonUtils { @@ -28,10 +30,26 @@ private JsonUtils() { } public static String wrapJsonWithArray(final String jsonString, final String wrapper, final Gson gson, - final NodeIdentifierWithPredicates predicates) { + final NodeIdentifierWithPredicates predicates, final EffectiveModelContext context) { final JsonObject innerJson = JsonParser.parseString(jsonString).getAsJsonObject(); for (final Entry key : predicates.entrySet()) { - innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(key.getValue())); + final var keyValue = key.getValue(); + // InstanceIdentifier for identityref is stored as a QName, value should be in format MODULE:IDENTITY_NAME. + if (keyValue instanceof QName qnameValue) { + final var module = context.findModule(qnameValue.getModule()).orElseThrow(); + final var value = String.format("%s:%s", module.getName(), qnameValue.getLocalName()); + innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(value)); + // Custom ODL Number types are not correctly parsed by Gson. + } else if (keyValue instanceof Number numberValue) { + if (numberValue instanceof Decimal64) { + innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(numberValue.doubleValue())); + } else { + innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(numberValue.longValue())); + } + // Other parse-able types. + } else { + innerJson.add(key.getKey().getLocalName(), gson.toJsonTree(keyValue)); + } } final JsonObject result = new JsonObject(); diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-device-simulator/src/main/java/io/lighty/modules/gnmi/simulatordevice/gnmi/GnmiCrudService.java b/lighty-modules/lighty-gnmi/lighty-gnmi-device-simulator/src/main/java/io/lighty/modules/gnmi/simulatordevice/gnmi/GnmiCrudService.java index 82f6ba3ac3..a4b0be4ea3 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-device-simulator/src/main/java/io/lighty/modules/gnmi/simulatordevice/gnmi/GnmiCrudService.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-device-simulator/src/main/java/io/lighty/modules/gnmi/simulatordevice/gnmi/GnmiCrudService.java @@ -221,7 +221,7 @@ private Gnmi.UpdateResult processUpdateListNonSimpleValue(final Gnmi.Update upda String.format("%s:%s", module.getName(), Iterables.getLast(identifier.getPathArguments()).getNodeType().getLocalName()), gson, - lastPathArgument); + lastPathArgument, context); node = DataConverter.nodeFromJsonString(identifier, json, context); // In case of list entry, point to the list itself resultingIdentifier = identifier.getParent(); diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java b/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java index 16932060e4..0fb6fa419b 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-sb/src/main/java/io/lighty/gnmi/southbound/mountpoint/codecs/GetResponseToNormalizedNodeCodec.java @@ -105,7 +105,8 @@ private NormalizedNode updateToNormalizedNode(final Update update, final YangIns if (identifier.getLastPathArgument() instanceof NodeIdentifierWithPredicates) { final NodeIdentifierWithPredicates lastPathArgument = (NodeIdentifierWithPredicates) identifier.getLastPathArgument(); - responseJson = JsonUtils.wrapJsonWithArray(responseJson, wrapWith, gson, lastPathArgument); + responseJson = JsonUtils.wrapJsonWithArray(responseJson, wrapWith, gson, lastPathArgument, + schemaContextProvider.getSchemaContext()); } else { responseJson = JsonUtils.wrapJsonWithObject(responseJson, wrapWith, gson); } diff --git a/lighty-modules/lighty-gnmi/lighty-gnmi-test/src/test/java/io/lighty/modules/gnmi/test/gnmi/SimulatorCrudTest.java b/lighty-modules/lighty-gnmi/lighty-gnmi-test/src/test/java/io/lighty/modules/gnmi/test/gnmi/SimulatorCrudTest.java index b706f1aa27..626e50964c 100644 --- a/lighty-modules/lighty-gnmi/lighty-gnmi-test/src/test/java/io/lighty/modules/gnmi/test/gnmi/SimulatorCrudTest.java +++ b/lighty-modules/lighty-gnmi/lighty-gnmi-test/src/test/java/io/lighty/modules/gnmi/test/gnmi/SimulatorCrudTest.java @@ -324,6 +324,39 @@ public void setContainerWithMultipleListKeyInPathTest() throws Exception { assertEquals("UPDATE", setResponse.getResponse(1).getOp().toString()); } + @Test + public void setMultipleKeyListAsLastElementInPathTest() throws Exception { + final var multipleKeyPath = 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_DEFINED_ROLES") + .putKey("union-key", "5") + .build()) + .build(); + final var innerContainerUpdate = Gnmi.Update.newBuilder() + .setPath(multipleKeyPath) + .setVal(Gnmi.TypedValue.newBuilder() + .setJsonIetfVal(ByteString.copyFromUtf8(""" + { + "inner-container": { + "inner-data": "data" + } + } + """)) + .build()) + .build(); + final var setRequest = Gnmi.SetRequest.newBuilder().addUpdate(innerContainerUpdate).build(); + LOG.info("Sending set request:\n{}", setRequest); + + final var setResponse = sessionProvider.getGnmiSession().set(setRequest).get(); + assertEquals("UPDATE", setResponse.getResponse(0).getOp().toString()); + } + @Test public void crudComplexValueTest() throws ExecutionException, InterruptedException, IOException, JSONException { final Gnmi.Path path = Gnmi.Path.newBuilder()