From: Maros Marsalek Date: Mon, 28 Oct 2013 10:37:25 +0000 (+0100) Subject: Added support for list, leaf-list and container in output under rpc node in yang... X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~537 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=545b424ba9278d7aee12b9a6173e23c1b1d39dd3;ds=sidebyside Added support for list, leaf-list and container in output under rpc node in yang definition. Added support for leaf-list in state data definition. Updated config subsystem as well as netconf mapping. Change-Id: I4bdfbed352e29d6d3dc569f38c07022383d565c8 Signed-off-by: Maros Marsalek --- diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java index 641fb25c1a..a10f62be79 100644 --- a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java +++ b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/ftl/TemplateFactory.java @@ -7,15 +7,11 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule; import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface; import org.opendaylight.controller.config.api.runtime.RuntimeBean; @@ -25,29 +21,17 @@ import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc; import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute.Dependency; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation; +import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.*; import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter; -import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor; -import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field; -import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header; -import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDeclaration; -import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition; -import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper; import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil; import org.opendaylight.yangtools.sal.binding.model.api.Type; -import com.google.common.base.Function; -import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import javax.management.openmbean.SimpleType; +import java.util.*; +import java.util.Map.Entry; public class TemplateFactory { @@ -107,40 +91,18 @@ public class TemplateFactory { List extendedInterfaces = Arrays.asList(RuntimeBean.class .getCanonicalName()); List methods = new ArrayList<>(); + // convert attributes to getters for (AttributeIfc attributeIfc : entry.getAttributes()) { String returnType = null; - if (attributeIfc instanceof TypedAttribute) { - returnType = ((TypedAttribute) attributeIfc).getType() - .getFullyQualifiedName(); - } else if (attributeIfc instanceof TOAttribute) { - String fullyQualifiedName = FullyQualifiedNameHelper - .getFullyQualifiedName(entry.getPackageName(), - attributeIfc.getUpperCaseCammelCase()); - - returnType = fullyQualifiedName; - } else if (attributeIfc instanceof ListAttribute) { - AttributeIfc innerAttr = ((ListAttribute) attributeIfc) - .getInnerAttribute(); - - String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr) - .getType().getFullyQualifiedName() - : FullyQualifiedNameHelper.getFullyQualifiedName( - entry.getPackageName(), - attributeIfc.getUpperCaseCammelCase()); - - returnType = "java.util.List<" + innerTpe + ">"; - } else { - throw new UnsupportedOperationException( - "Attribute not supported: " - + attributeIfc.getClass()); - } + returnType = getReturnType(entry, attributeIfc); String getterName = "get" + attributeIfc.getUpperCaseCammelCase(); MethodDeclaration getter = new MethodDeclaration(returnType, getterName, Collections. emptyList()); methods.add(getter); } + // add rpc methods for (Rpc rpc : entry.getRpcs()) { // convert JavaAttribute parameters into fields @@ -152,7 +114,7 @@ public class TemplateFactory { fields.add(field); } MethodDeclaration operation = new MethodDeclaration( - rpc.getReturnType(), rpc.getName(), fields); + getReturnType(entry, rpc.getReturnType()), rpc.getName(), fields); methods.add(operation); } @@ -170,6 +132,38 @@ public class TemplateFactory { return result; } + private static String getReturnType(RuntimeBeanEntry entry, AttributeIfc attributeIfc) { + String returnType; + if (attributeIfc instanceof TypedAttribute) { + returnType = ((TypedAttribute) attributeIfc).getType() + .getFullyQualifiedName(); + } else if (attributeIfc instanceof TOAttribute) { + String fullyQualifiedName = FullyQualifiedNameHelper + .getFullyQualifiedName(entry.getPackageName(), + attributeIfc.getUpperCaseCammelCase()); + + returnType = fullyQualifiedName; + } else if (attributeIfc instanceof ListAttribute) { + AttributeIfc innerAttr = ((ListAttribute) attributeIfc) + .getInnerAttribute(); + + String innerTpe = innerAttr instanceof TypedAttribute ? ((TypedAttribute) innerAttr) + .getType().getFullyQualifiedName() + : FullyQualifiedNameHelper.getFullyQualifiedName( + entry.getPackageName(), + attributeIfc.getUpperCaseCammelCase()); + + returnType = "java.util.List<" + innerTpe + ">"; + } else if (attributeIfc == VoidAttribute.getInstance()) { + return "void"; + } else { + throw new UnsupportedOperationException( + "Attribute not supported: " + + attributeIfc.getClass()); + } + return returnType; + } + public static GeneralInterfaceTemplate serviceInterfaceFromSie( ServiceInterfaceEntry sie) { @@ -315,8 +309,25 @@ public class TemplateFactory { RuntimeBeanEntry rbe) { Map retVal = Maps.newHashMap(); TOAttributesProcessor processor = new TOAttributesProcessor(); - processor.processAttributes(rbe.getYangPropertiesToTypesMap(), - rbe.getPackageName()); + Map yangPropertiesToTypesMap = Maps.newHashMap(rbe.getYangPropertiesToTypesMap()); + + // Add TOs from output parameters + for (Rpc rpc : rbe.getRpcs()) { + AttributeIfc returnType = rpc.getReturnType(); + + if (returnType == VoidAttribute.getInstance()) + continue; + if (returnType instanceof JavaAttribute) + continue; + if (returnType instanceof ListAttribute && returnType.getOpenType() instanceof SimpleType) + continue; + + Preconditions.checkState(yangPropertiesToTypesMap.containsKey(returnType.getAttributeYangName()) == false, + "Duplicate TO %s for %s", returnType.getAttributeYangName(), rbe); + yangPropertiesToTypesMap.put(returnType.getAttributeYangName(), returnType); + } + + processor.processAttributes(yangPropertiesToTypesMap, rbe.getPackageName()); for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor .getTOs()) { List constructors = Lists.newArrayList(); diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java index d07e20bebb..22a2b13b4a 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java @@ -10,10 +10,7 @@ package org.opendaylight.controller.config.yangjmxgenerator; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Optional; import com.google.common.collect.Lists; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException; import org.opendaylight.yangtools.yang.common.QName; @@ -219,9 +216,13 @@ public class RuntimeBeanEntry { attributes.add(listAttribute); } + } else if (child instanceof LeafListSchemaNode) { + ListAttribute listAttribute = ListAttribute.create( + (LeafListSchemaNode) child, typeProviderWrapper); + attributes.add(listAttribute); } else { - throw new IllegalStateException("Unknown running-data node " - + child + " , " + "" + "expected leaf or container"); + throw new IllegalStateException("Unexpected running-data node " + + child); } } Set rpcs = new HashSet<>(); @@ -245,29 +246,15 @@ public class RuntimeBeanEntry { for (RpcDefinition rpcDefinition : rpcDefinitions) { String name = ModuleMXBeanEntry .findJavaParameter(rpcDefinition); - String returnType; + AttributeIfc returnType; if (rpcDefinition.getOutput() == null || rpcDefinition.getOutput().getChildNodes().size() == 0) { - returnType = "void"; + returnType = VoidAttribute.getInstance(); } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) { DataSchemaNode returnDSN = rpcDefinition.getOutput() .getChildNodes().iterator().next(); - checkArgument( - returnDSN instanceof LeafSchemaNode, - "Unexpected type of rpc return type. " - + "Currently only leafs and empty output nodes are supported, got " - + returnDSN); - LeafSchemaNode returnLeaf = (LeafSchemaNode) returnDSN; - // We currently expect leaf defined in output element in yang to be named result - // FIXME: value of result is fully qualified name - should be extended to accept TOs - String localName = returnLeaf.getQName().getLocalName(); - checkArgument( - localName.equals("result"), - "Unexpected name of leaf in output element, expected leaf named result, was %s at %s", - localName, currentModule.getName()); - - returnType = typeProviderWrapper.getType(returnLeaf) - .getFullyQualifiedName(); + returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper); + } else { throw new IllegalArgumentException( "More than one child node in rpc output is not supported. " @@ -297,6 +284,23 @@ public class RuntimeBeanEntry { attributes, rpcs); } + private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper) { + if (child instanceof LeafSchemaNode) { + LeafSchemaNode leaf = (LeafSchemaNode) child; + return new JavaAttribute(leaf, typeProviderWrapper); + } else if (child instanceof ContainerSchemaNode) { + ContainerSchemaNode container = (ContainerSchemaNode) child; + TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper); + return toAttribute; + } else if (child instanceof ListSchemaNode) { + return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper); + } else if (child instanceof LeafListSchemaNode) { + return ListAttribute.create((LeafListSchemaNode) child, typeProviderWrapper); + } else { + throw new IllegalStateException("Unknown output data node " + child + " for rpc"); + } + } + private static Collection sortAttributes(Set childNodes) { final TreeSet dataSchemaNodes = new TreeSet<>(new Comparator() { @Override @@ -438,10 +442,10 @@ public class RuntimeBeanEntry { public static class Rpc { private final String name; private final List parameters; - private final String returnType; + private final AttributeIfc returnType; private final String yangName; - Rpc(String returnType, String name, String yangName, + Rpc(AttributeIfc returnType, String name, String yangName, List parameters) { this.returnType = returnType; this.name = name; @@ -461,7 +465,7 @@ public class RuntimeBeanEntry { return parameters; } - public String getReturnType() { + public AttributeIfc getReturnType() { return returnType; } } diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java index 8f516ef181..59ab0ff68e 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java @@ -7,17 +7,18 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.attribute; -import javax.management.openmbean.ArrayType; -import javax.management.openmbean.OpenDataException; -import javax.management.openmbean.OpenType; -import javax.management.openmbean.SimpleType; - import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper; import org.opendaylight.yangtools.sal.binding.model.api.Type; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.SimpleType; + public class JavaAttribute extends AbstractAttribute implements TypedAttribute { + private final Type type; private final String nullableDescription, nullableDefault; diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/VoidAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/VoidAttribute.java new file mode 100644 index 0000000000..1e590a5dbe --- /dev/null +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/VoidAttribute.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. 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.controller.config.yangjmxgenerator.attribute; + +import javax.management.openmbean.OpenType; + +public class VoidAttribute implements AttributeIfc { + + private static VoidAttribute instance = new VoidAttribute(); + + public static VoidAttribute getInstance() { + return instance; + } + + private VoidAttribute() { + } + + @Override + public String getAttributeYangName() { + return null; + } + + @Override + public String getNullableDescription() { + return null; + } + + @Override + public String getNullableDefault() { + return null; + } + + @Override + public String getUpperCaseCammelCase() { + return null; + } + + @Override + public String getLowerCaseCammelCase() { + return null; + } + + @Override + public OpenType getOpenType() { + return null; + } +} diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntryTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntryTest.java index 597cf4ccc9..a06d2caa27 100644 --- a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntryTest.java +++ b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntryTest.java @@ -7,20 +7,6 @@ */ package org.opendaylight.controller.config.yangjmxgenerator; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.doReturn; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -import javax.management.openmbean.SimpleType; - import org.junit.Test; import org.mockito.Mockito; import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; @@ -30,6 +16,13 @@ import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; +import javax.management.openmbean.SimpleType; +import java.util.*; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.*; +import static org.mockito.Mockito.doReturn; + public class RuntimeBeanEntryTest extends AbstractYangTest { public static final String PACKAGE_NAME = "packages.sis"; @@ -111,7 +104,8 @@ public class RuntimeBeanEntryTest extends AbstractYangTest { RuntimeBeanEntry.Rpc rpc = getRpcByName(rpcs, SLEEP_RPC_NAME); assertNotNull(rpc); assertThat(rpc.getYangName(), is(SLEEP_RPC_NAME)); - assertThat(rpc.getReturnType().endsWith(SLEEP_RPC_OUTPUT), is(true)); + + assertThat(((JavaAttribute)rpc.getReturnType()).getType().getFullyQualifiedName().endsWith(SLEEP_RPC_OUTPUT), is(true)); // get sleep rpc input attribute and test it List attributes = rpc.getParameters(); diff --git a/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang b/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang index 6245e6197a..291e3ebd94 100644 --- a/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang +++ b/opendaylight/config/yang-test/src/main/yang/config-test-impl.yang @@ -291,11 +291,16 @@ module config-test-impl { config:inner-state-bean; rpcx:rpc-context-instance "inner-inner-test-rpc"; + rpcx:rpc-context-instance "complex-output-rpc"; key "simple-int3"; leaf simple-int3 { - type uint16; + type uint16; + } + + leaf-list list-of-strings { + type string; } list not-state-bean { @@ -327,6 +332,8 @@ module config-test-impl { identity inner-test-rpc; identity inner-inner-test-rpc; + identity complex-output-rpc; + rpc no-arg { input { uses rpcx:rpc-context-ref { @@ -346,6 +353,66 @@ module config-test-impl { } } + rpc container-output { + input { + uses rpcx:rpc-context-ref { + refine context-instance { + rpcx:rpc-context-instance complex-output-rpc; + } + } + } + + output { + container retValContainer { + leaf v1 { + type string; + } + + leaf v2 { + type uint32; + } + } + } + } + + rpc leaf-list-output { + input { + uses rpcx:rpc-context-ref { + refine context-instance { + rpcx:rpc-context-instance complex-output-rpc; + } + } + } + + output { + leaf-list result { + type string; + } + } + } + + rpc list-output { + input { + uses rpcx:rpc-context-ref { + refine context-instance { + rpcx:rpc-context-instance complex-output-rpc; + } + } + } + + output { + list ret-val-list { + leaf v2 { + type uint32; + } + + leaf v1 { + type boolean; + } + } + } + } + rpc noArgInner { input { uses rpcx:rpc-context-ref { diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java index 52b6801cc0..1c212e9d4b 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/mapping/ObjectMapper.java @@ -8,19 +8,17 @@ package org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.mapping; -import java.util.Map; -import java.util.Map.Entry; +import com.google.common.collect.Maps; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; +import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; import javax.management.openmbean.ArrayType; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; - -import org.opendaylight.controller.config.yangjmxgenerator.attribute.*; -import org.opendaylight.controller.netconf.confignetconfconnector.mapping.attributes.AttributeIfcSwitchStatement; -import org.opendaylight.controller.netconf.confignetconfconnector.mapping.config.Services; - -import com.google.common.collect.Maps; +import java.util.Map; +import java.util.Map.Entry; public class ObjectMapper extends AttributeIfcSwitchStatement>> { @@ -41,7 +39,7 @@ public class ObjectMapper extends AttributeIfcSwitchStatement> prepareStrategy(AttributeIfc attributeIfc) { + public AttributeMappingStrategy> prepareStrategy(AttributeIfc attributeIfc) { return switchAttribute(attributeIfc); } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java index b49d20ed35..43d8db6178 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/mapping/attributes/toxml/ObjectXmlWriter.java @@ -39,7 +39,7 @@ public class ObjectXmlWriter extends AttributeIfcSwitchStatement simpleType = SimpleTypeResolver.getSimpleType(result.getClass().getName()); - final Optional mappedAttributeOpt = new SimpleAttributeMappingStrategy(simpleType).mapAttribute(result); - return mappedAttributeOpt.isPresent() ? mappedAttributeOpt.get() : ""; + private Element toXml(Document doc, Object result, AttributeIfc returnType, String namespace, String elementName) { + AttributeMappingStrategy> mappingStrategy = new ObjectMapper(null).prepareStrategy(returnType); + Optional mappedAttributeOpt = mappingStrategy.mapAttribute(result); + Preconditions.checkState(mappedAttributeOpt.isPresent(), "Unable to map return value %s as %s", result, returnType.getOpenType()); + + // FIXME: multiple return values defined as leaf-list and list in yang should not be wrapped in output xml element, + // they need to be appended directly under rpc-reply element + // + // Either allow List of Elements to be returned from NetconfOperation or + // pass reference to parent output xml element for netconf operations to + // append result(s) on their own + Element tempParent = doc.createElementNS(XmlNetconfConstants.RFC4741_TARGET_NAMESPACE, "output"); + new ObjectXmlWriter().prepareWritingStrategy(elementName, returnType, doc).writeElement(tempParent, namespace, mappedAttributeOpt.get()); + + XmlElement xmlElement = XmlElement.fromDomElement(tempParent); + return xmlElement.getChildElements().size() > 1 ? tempParent : xmlElement.getOnlyChildElement().getDomElement(); } private Object executeOperation(final ConfigRegistryClient configRegistryClient, final ObjectName on, @@ -161,12 +176,11 @@ public class RuntimeRpc extends AbstractConfigNetconfOperation { logger.info("Operation {} called successfully on {} with arguments {} with result {}", execution.operationName, execution.on, execution.attributes, result); - if (execution.returnType.equals("void")) { + if (execution.isVoid()) { return document.createElement("ok"); } else { - final Element output = XmlUtil.createTextElement(document, "result", getStringRepresentation(result)); - XmlUtil.addNamespaceAttr(output, execution.namespace); - return output; + return toXml(document, result, execution.returnType, execution.namespace, + execution.returnType.getAttributeYangName()); } } @@ -175,11 +189,11 @@ public class RuntimeRpc extends AbstractConfigNetconfOperation { private final ObjectName on; private final String operationName; private final Map attributes; - private final String returnType; + private final AttributeIfc returnType; private final String namespace; public NetconfOperationExecution(final ObjectName on, final String name, - final Map attributes, final String returnType, final String namespace) { + final Map attributes, final AttributeIfc returnType, final String namespace) { this.on = on; this.operationName = name; this.attributes = attributes; @@ -187,6 +201,10 @@ public class RuntimeRpc extends AbstractConfigNetconfOperation { this.namespace = namespace; } + boolean isVoid() { + return returnType == VoidAttribute.getInstance(); + } + } private static Map sortAttributes( diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java index c72cb7498b..62e1273e39 100644 --- a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java @@ -407,12 +407,15 @@ public class NetconfMappingTest extends AbstractConfigTest { Element response = get(); + System.err.println(XmlUtil.toString(response)); + assertEquals(2, getElementsSize(response, "instance")); assertEquals(2, getElementsSize(response, "asdf")); assertEquals(5, getElementsSize(response, "inner-running-data")); assertEquals(5, getElementsSize(response, "deep2")); assertEquals(11, getElementsSize(response, "inner-inner-running-data")); assertEquals(11, getElementsSize(response, "deep3")); + assertEquals(11 * 2, getElementsSize(response, "list-of-strings")); assertEquals(4, getElementsSize(response, "inner-running-data-additional")); assertEquals(4, getElementsSize(response, "deep4")); // TODO assert keys @@ -427,6 +430,10 @@ public class NetconfMappingTest extends AbstractConfigTest { response = executeOp(netconf, "netconfMessages/rpcInnerInner.xml"); assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("true")); + + response = executeOp(netconf, "netconfMessages/rpcInnerInner_complex_output.xml"); + assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("1")); + assertThat(XmlUtil.toString(response), JUnitMatchers.containsString("2")); } private Element get() throws NetconfDocumentedException, ParserConfigurationException, SAXException, IOException { @@ -503,11 +510,31 @@ public class NetconfMappingTest extends AbstractConfigTest { return new Deep3(); } + @Override + public List getListOfStrings() { + return Lists.newArrayList("l1", "l2"); + } + + @Override + public List listOutput() { + return Lists.newArrayList(new RetValList()); + } + @Override public Boolean noArgInnerInner(Integer integer, Boolean aBoolean) { return aBoolean; } + @Override + public RetValContainer containerOutput() { + return new RetValContainer(); + } + + @Override + public List leafListOutput() { + return Lists.newArrayList("1", "2"); + } + } reg.register(new InnerInnerRunningDataRuntimeMXBeanTest(counter++)); diff --git a/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner_complex_output.xml b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner_complex_output.xml new file mode 100644 index 0000000000..209c382252 --- /dev/null +++ b/opendaylight/netconf/netconf-util/src/test/resources/netconfMessages/rpcInnerInner_complex_output.xml @@ -0,0 +1,8 @@ + + + + /data/modules/module[name='impl-netconf']/instance[name='instance2']/inner-running-data[key='1015']/inner-inner-running-data[key='1017'] + + +