[NETCONF-804] YANG Patch target parsing does not work with RFC8072 examples Created: 13/Aug/21  Updated: 20/Oct/21  Resolved: 17/Aug/21

Status: Resolved
Project: netconf
Component/s: restconf-nb
Affects Version/s: None
Fix Version/s: 2.0.3, 1.13.5

Type: Bug Priority: High
Reporter: Robert Varga Assignee: Robert Varga
Resolution: Done Votes: 0
Labels: pt
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Blocks
blocks INTTEST-125 Improve csit/suites/netconf/scale/max... Confirmed

 Description   

A very simple invocation following RFC8072 examples:

 

curl -u admin:admin -X PATCH http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf -H "Content-Type: application/yang-patch+json" -H "Accept: application/yang-data+json" -v --data '
{
   "ietf-yang-patch:yang-patch" : {
      "edit" : [
         {
            "edit-id" : "test-edit",
            "operation" : "create",
            "target" : "/node=test-node",
            "value" : {
               "node" : [
                  {
                     "netconf-node-topology:host" : "127.0.0.1",
                     "netconf-node-topology:keepalive-delay" : 0,
                     "netconf-node-topology:password" : "topsecret",
                     "netconf-node-topology:port" : 17830,
                     "netconf-node-topology:tcp-only" : false,
                     "netconf-node-topology:username" : "admin",
                     "node-id" : "test-node"
                  }
               ]
            }
         }
      ],
      "patch-id" : "test"
   }
}'

results in a 400 bad request:

* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< Set-Cookie: JSESSIONID=node0ttrvzsoy0jfmbgck6vo7hinq0.node0; Path=/; HttpOnly
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Thu, 12-Aug-2021 14:42:43 GMT; SameSite=lax
< Content-Type: application/yang-data+json
< Content-Length: 189
<  
* Connection #0 to host localhost left intact
{"errors":{"error":[{"error-tag":"malformed-message","error-info":"Failed to lookup prefix ","error-message":"Error parsing json input: Failed to lookup prefix ","error-type":"protocol"}]}}

 



 Comments   
Comment by Robert Varga [ 13/Aug/21 ]

Failure cause is not obvious, but a bit of debugging shows the call is to resolve "/node" part with default namespace, i.e. from AbstractStringInstanceIdentifierCodec.createQName() – which is weird, as we have lastModule initialized at this point, hence we should be able to do right thing without resolving the prefix.

 

Thread [qtp579053283-382] (Suspended (exception IllegalArgumentException))	
	Preconditions.checkArgument(boolean, String, Object) line: 219	
	StringModuleInstanceIdentifierCodec(AbstractModuleStringInstanceIdentifierCodec).createQName(String, String) line: 36	
	StringModuleInstanceIdentifierCodec(AbstractStringInstanceIdentifierCodec).createQName(QNameModule, String) line: 115	
	XpathStringParsingPathArgumentBuilder.nextQName() line: 195	
	XpathStringParsingPathArgumentBuilder.computeNextArgument() line: 96	
	XpathStringParsingPathArgumentBuilder.build() line: 87	
	StringModuleInstanceIdentifierCodec(AbstractStringInstanceIdentifierCodec).deserializeImpl(String) line: 102	
	StringModuleInstanceIdentifierCodec(AbstractStringInstanceIdentifierCodec).deserializeImpl(Object) line: 32	
	StringModuleInstanceIdentifierCodec(AbstractCodec<P,I,X>).deserialize(P) line: 29	
	JsonPatchBodyReader.readEditDefinition(PatchEdit, JsonReader, InstanceIdentifierContext<?>, StringModuleInstanceIdentifierCodec) line: 235	
	JsonPatchBodyReader.parseByName(String, PatchEdit, JsonReader, InstanceIdentifierContext<?>, StringModuleInstanceIdentifierCodec, List<PatchEntity>, AtomicReference<String>) line: 183	
	JsonPatchBodyReader.read(JsonReader, InstanceIdentifierContext<?>, AtomicReference<String>) line: 144	
	JsonPatchBodyReader.readFrom(InstanceIdentifierContext<?>, InputStream) line: 84	
	JsonPatchBodyReader.readBody(InstanceIdentifierContext<?>, InputStream) line: 74	
	JsonPatchBodyReader.readBody(InstanceIdentifierContext, InputStream) line: 59	
	JsonPatchBodyReader(AbstractIdentifierAwareJaxRsProvider<T>).readFrom(Class<T>, Type, Annotation[], MediaType, MultivaluedMap<String,String>, InputStream) line: 67	
	ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorContext, MessageBodyReader, EntityInputStream) line: 257	
	ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorContext) line: 236	
	ReaderInterceptorExecutor.proceed() line: 156	
	MappableExceptionWrapperInterceptor.aroundReadFrom(ReaderInterceptorContext) line: 73	
	ReaderInterceptorExecutor.proceed() line: 156	
	MessageBodyFactory.readFrom(Class<?>, Type, Annotation[], MediaType, MultivaluedMap<String,String>, PropertiesDelegate, InputStream, Iterable<ReaderInterceptor>, boolean) line: 1091	
	ContainerRequest(InboundMessageContext).readEntity(Class<T>, Type, Annotation[], PropertiesDelegate) line: 874	
	ContainerRequest.readEntity(Class<T>, Type, Annotation[]) line: 271	
	EntityParamValueParamProvider$EntityValueSupplier.apply(ContainerRequest) line: 97	
	EntityParamValueParamProvider$EntityValueSupplier.apply(Object) line: 80	
	ParamValueFactoryWithSource<T>.apply(ContainerRequest) line: 74	
	ParameterValueHelper.getParameterValues(List<ParamValueFactoryWithSource<?>>, ContainerRequest) line: 92	
	JavaResourceMethodDispatcherProvider$TypeOutInvoker(JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker).getParamValues(ContainerRequest) line: 133	
	JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(Object, ContainerRequest) line: 243	
	JavaResourceMethodDispatcherProvider$TypeOutInvoker(AbstractJavaResourceMethodDispatcher).dispatch(Object, ContainerRequest) line: 103	
	ResourceMethodInvoker.invoke(RequestProcessingContext, Object) line: 493	
	ResourceMethodInvoker.apply(RequestProcessingContext) line: 415	
	ResourceMethodInvoker.apply(Object) line: 104	
	ServerRuntime$1.run() line: 277	
	Errors$1.call() line: 272	
	Errors$1.call() line: 268	
	Errors.process(Callable<T>, boolean) line: 316	
	Errors.process(Producer<T>, boolean) line: 298	
	Errors.process(Runnable) line: 268	
	Hk2RequestScope(RequestScope).runInScope(RequestContext, Runnable) line: 289	
	ServerRuntime.process(ContainerRequest) line: 256	
	ApplicationHandler.handle(ContainerRequest) line: 703	
	WebComponent.serviceImpl(URI, URI, HttpServletRequest, HttpServletResponse) line: 416	
	WebComponent.service(URI, URI, HttpServletRequest, HttpServletResponse) line: 370	
	ServletContainer.service(URI, URI, HttpServletRequest, HttpServletResponse) line: 389	
	ServletContainer.service(HttpServletRequest, HttpServletResponse) line: 342	
	ServletContainer.service(ServletRequest, ServletResponse) line: 229	
	ServletHolder$NotAsync.service(ServletRequest, ServletResponse) line: 1443	
	ServletHolder.handle(Request, ServletRequest, ServletResponse) line: 791	
	ServletHandler$ChainEnd.doFilter(ServletRequest, ServletResponse) line: 1626	
	CrossOriginFilter.handle(HttpServletRequest, HttpServletResponse, FilterChain) line: 319	
	CrossOriginFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 273	
	FilterHolder.doFilter(ServletRequest, ServletResponse, FilterChain) line: 193	
	ServletHandler$Chain.doFilter(ServletRequest, ServletResponse) line: 1601	
	ProxiedFilterChain.doFilter(ServletRequest, ServletResponse) line: 61	
	ODLHttpAuthenticationFilter(AdviceFilter).executeChain(ServletRequest, ServletResponse, FilterChain) line: 108	
	ODLHttpAuthenticationFilter(AdviceFilter).doFilterInternal(ServletRequest, ServletResponse, FilterChain) line: 137	
	ODLHttpAuthenticationFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 125	
	ProxiedFilterChain.doFilter(ServletRequest, ServletResponse) line: 66	
	InvalidRequestFilter(AdviceFilter).executeChain(ServletRequest, ServletResponse, FilterChain) line: 108	
	InvalidRequestFilter(AdviceFilter).doFilterInternal(ServletRequest, ServletResponse, FilterChain) line: 137	
	InvalidRequestFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 125	
	ProxiedFilterChain.doFilter(ServletRequest, ServletResponse) line: 66	
	AAAShiroFilter(AbstractShiroFilter).executeChain(ServletRequest, ServletResponse, FilterChain) line: 450	
	AbstractShiroFilter$1.call() line: 365	
	SubjectCallable<V>.doCall(Callable<V>) line: 90	
	SubjectCallable<V>.call() line: 83	
	WebDelegatingSubject(DelegatingSubject).execute(Callable<V>) line: 387	
	AAAShiroFilter(AbstractShiroFilter).doFilterInternal(ServletRequest, ServletResponse, FilterChain) line: 362	
	AAAShiroFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 125	
	FilterHolder.doFilter(ServletRequest, ServletResponse, FilterChain) line: 193	
	ServletHandler$Chain.doFilter(ServletRequest, ServletResponse) line: 1601	
	WebSocketUpgradeFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 228	
	FilterHolder.doFilter(ServletRequest, ServletResponse, FilterChain) line: 193	
	ServletHandler$Chain.doFilter(ServletRequest, ServletResponse) line: 1601	
	HttpServiceServletHandler(ServletHandler).doHandle(String, Request, HttpServletRequest, HttpServletResponse) line: 548	
	HttpServiceServletHandler.doHandle(String, Request, HttpServletRequest, HttpServletResponse) line: 71	
	HttpServiceServletHandler(ScopedHandler).handle(String, Request, HttpServletRequest, HttpServletResponse) line: 143	
	ConstraintSecurityHandler(SecurityHandler).handle(String, Request, HttpServletRequest, HttpServletResponse) line: 602	
	SessionHandler(HandlerWrapper).handle(String, Request, HttpServletRequest, HttpServletResponse) line: 127	
	SessionHandler(ScopedHandler).nextHandle(String, Request, HttpServletRequest, HttpServletResponse) line: 235	
	SessionHandler.doHandle(String, Request, HttpServletRequest, HttpServletResponse) line: 1624	
	HttpServiceContext(ScopedHandler).nextHandle(String, Request, HttpServletRequest, HttpServletResponse) line: 233	
	HttpServiceContext(ContextHandler).doHandle(String, Request, HttpServletRequest, HttpServletResponse) line: 1435	
	HttpServiceContext.doHandle(String, Request, HttpServletRequest, HttpServletResponse) line: 294	
	HttpServiceServletHandler(ScopedHandler).nextScope(String, Request, HttpServletRequest, HttpServletResponse) line: 188	
	HttpServiceServletHandler(ServletHandler).doScope(String, Request, HttpServletRequest, HttpServletResponse) line: 501	
	SessionHandler.doScope(String, Request, HttpServletRequest, HttpServletResponse) line: 1594	
	HttpServiceContext(ScopedHandler).nextScope(String, Request, HttpServletRequest, HttpServletResponse) line: 186	
	HttpServiceContext(ContextHandler).doScope(String, Request, HttpServletRequest, HttpServletResponse) line: 1350	
	HttpServiceContext(ScopedHandler).handle(String, Request, HttpServletRequest, HttpServletResponse) line: 141	
	JettyServerHandlerCollection.handle(String, Request, HttpServletRequest, HttpServletResponse) line: 82	
	JettyServerWrapper(HandlerWrapper).handle(String, Request, HttpServletRequest, HttpServletResponse) line: 127	
	JettyServerWrapper(Server).handle(HttpChannel) line: 516	
	HttpChannelOverHttp(HttpChannel).lambda$handle$1() line: 388	
	313710830.dispatch() line: not available	
	HttpChannelOverHttp(HttpChannel).dispatch(DispatcherType, HttpChannel$Dispatchable) line: 633	
	HttpChannelOverHttp(HttpChannel).handle() line: 380	
	HttpConnection.onFillable() line: 277	
	AbstractConnection$ReadCallback.succeeded() line: 311	
	AbstractEndPoint$1(FillInterest).fillable() line: 105	
	ChannelEndPoint$1.run() line: 104	
	QueuedThreadPool.runJob(Runnable) line: 882	
	QueuedThreadPool$Runner.run() line: 1036	
	Thread.run() line: 829	
Comment by Robert Varga [ 13/Aug/21 ]

I think StringModuleInstanceIdentifierCodec needs to be taught the same trick (in JSON parsing) as RFC7951JSONInstanceIdentifierCodec, i.e. to lastModule. Not sure how this works out with XML yet.

Comment by Robert Varga [ 13/Aug/21 ]

RFC8072 defines target this way:

 

     typedef target-resource-offset {
       type string;
       description
         "Contains a data resource identifier string representing
          a sub-resource within the target resource.
          The document root for this expression is the
          target resource that is specified in the
          protocol operation (e.g., the URI for the PATCH request).

          This string is encoded according to the same rules as those
          for a data resource identifier in a RESTCONF request URI.";
       reference
          "RFC 8040, Section 3.5.3.";
     }

Our implementation uses a weird XML-based subclass of ... whatever. We should not be using that, though, but rather use the URL -> YangInstanceIdentifier facilities in ParserIdentifier.

 

Generated at Wed Feb 07 20:15:57 UTC 2024 using Jira 8.20.10#820010-sha1:ace47f9899e9ee25d7157d59aa17ab06aee30d3d.