[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: |
|
||||||||
| 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.
|