Bug 1225109

Summary: Sending a DELETE request with a Content-Type selects wrong "remove" method
Product: [oVirt] ovirt-engine Reporter: Carlos Mestre González <cmestreg>
Component: RestAPIAssignee: Juan Hernández <juan.hernandez>
Status: CLOSED CURRENTRELEASE QA Contact: Lucie Leistnerova <lleistne>
Severity: medium Docs Contact:
Priority: unspecified    
Version: ---CC: acanan, amureini, bugs, cmestreg, gklein, juan.hernandez, lleistne, lsurette, lsvaty, ratamir, rbalakri, srevivo, ykaul
Target Milestone: ovirt-4.0.0-alphaFlags: rule-engine: ovirt-4.0.0+
rule-engine: planning_ack+
juan.hernandez: devel_ack+
lsvaty: testing_ack+
Target Release: 4.0.0   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: ovirt 4.0.0 alpha1 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2016-08-17 14:37:07 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: Infra RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Carlos Mestre González 2015-05-26 15:16:00 UTC
Description of problem:
There are a few things wrong with the specification I see in the rsdl, 1) is the need to send an <action> in the body, and 2) the async parameter is not part of the body (is defined in the header?)

According to the rsdl:

<link href="/api/storageconnections/{storageconnection:id}" rel="delete">
[...]
<url>
  <parameters_set>
    <parameter required="false" type="xs:boolean" context="matrix">
      <name>async</name>
      <value>true|false</value>
    </parameter>
  </parameters_set>
</url>

<body required="false">
  <type>Action</type>
  <parameters_set>
    <parameter required="false" type="xs:string">
      <name>action.host.id|name</name>
    </parameter>
  </parameters_set>
</body>

1) But this seems strange to me, since <action> is need it, if not the server returns a 400:
<usage_message>
<message>
Request syntactically incorrect. See the description below for the correct usage:
</message>
</usage_message>

2) Also I'm assuming <async> needs to be in the body, if I send <action><async>dfasdf</async></action>
<fault>
<reason>Invalid value</reason>
<detail>
Value "dfasdf" isn't a valid boolean, it should be "true" or "false"
</detail>
</fault>


Version-Release number of selected component (if applicable):
rhevm-3.5.1.1-0.1.el6ev.noarch

How reproducible:
100%

Steps to Reproduce:
1. Add a iscsi storage connection
2. Try to remove it sending DELETE with nothing in the body.

Actual results:
<usage_message>
<message>
Request syntactically incorrect. See the description below for the correct usage:
</message>
</usage_message>


Expected results:
1) I assume this should work since I don't see the point of sending an empty <action></action>, but if that's the case, then change the specs (required="false") and improve the error message.
2) also set async as a parameter set inside the body.

Additional info:
DELETE works sending <action></action> in body.

Comment 1 Juan Hernández 2015-06-09 14:02:22 UTC
The RSDL documentation is correct, and the <action> element is optional. The problem is that you are probably trying to invoke this action including the "Content-Type: application/xml" header, something like this:

---8<---
url="https://engine36.example.com/ovirt-engine/api"
user="admin@internal"
password="..."
connection_id="..."

curl \
--verbose \
--insecure \
--user "${user}:${password}" \
--header "Content-Type: application/xml" \
--request DELETE \
"${url}/storageconnections/${connection_id}"
--->8---

The implementation in the server side (see the StorageServerConnectionsResource interface) has two "remove" methods, one that receives no parameters (except the identifier of the storage connection) and one that receives the Action parameter:

  @DELETE
  @Path("{id}")
  public Response remove(@PathParam("id") String id);

  @DELETE
  @Consumes({ ApiMediaType.APPLICATION_XML, ApiMediaType.APPLICATION_JSON, ApiMediaType.APPLICATION_X_YAML })
  @Path("{id}")
  public Response remove(@PathParam("id") String id, Action action);

When you send the DELETE request with the Content-Type header and without the Action in the body, the server automatically selects the second method, because it is the only one that explicitly accepts the given content type. Then the server tries to deserialize the body to create the Action object, and it fails because there isn't a body, that is why you get the 400 code. Currently the correct way to send this request is without a Content-Type header.

---8<---
url="https://engine36.example.com/ovirt-engine/api"
user="admin@internal"
password="..."
connection_id="..."

curl \
--verbose \
--insecure \
--user "${user}:${password}" \
--request DELETE \
"${url}/storageconnections/${connection_id}"
--->8---

The "async" parameter is a matrix parameter in this case, so it must be part of the request URL:

  DELETE /storageconnections/{storageconnection:id};async

The fact that a wrong "remove" method is selected can be considered a bug, and it affects all the resources that have more than one "remove" method, not just the storage connections, so I'm making this an infra bug and targeting it for 4.x, as I think we can't fix it for 3.6.

Comment 2 Carlos Mestre González 2015-07-10 14:13:09 UTC
Hi Juan, thank your for this response.

Did you run your example in 3.6? I've tried to not pass the body/content type as you mention in your example and is removing the object but it's returning a 500 error:

curl \
--insecure \
--request DELETE \
--verbose \
-u user@domain:password \
$url/api/storageconnections/21b9ffd2-a6e8-4158-98dc-f807b71977cd

result:

> DELETE /api/storageconnections/21b9ffd2-a6e8-4158-98dc-f807b71977cd HTTP/1.1
> Authorization: Basic YWRtaW5AaW50ZXJuYWw6MTIzNDU2
> User-Agent: curl/7.37.0
> Accept: */*
> 
< HTTP/1.1 500 Internal Server Error
< Date: Fri, 10 Jul 2015 14:00:10 GMT
< Content-Type: text/html; charset=UTF-8
< Content-Length: 135
< Vary: Accept-Encoding
< Connection: close
< 
* Closing connection 0
Could not find MessageBodyWriter for response object of type: org.ovirt.engine.api.mod

versions:
java_sdk-3.6.0.0.0.12.20150623.94212-1.x86_64
ovirt-engine-3.6.0-0.0.master.20150627185750.git6f063c1.el6.noarch

Comment 3 Juan Hernández 2015-07-10 14:17:02 UTC
Try adding the "Accept: application/xml" header:

curl \
--insecure \
--request DELETE \
--header "Accept: application/xml" \
--verbose \
-u user@domain:password \
$url/api/storageconnections/21b9ffd2-a6e8-4158-98dc-f807b71977cd

Comment 4 Red Hat Bugzilla Rules Engine 2015-10-19 10:57:28 UTC
Target release should be placed once a package build is known to fix a issue. Since this bug is not modified, the target version has been reset. Please use target milestone to plan a fix for a oVirt release.

Comment 5 Juan Hernández 2016-03-15 18:26:19 UTC
It isn't 100% clear if the HTTP specification allows bodies in DELETE requests, but anyhow using them is bad practice.

In version 4 of the API all the DELETE methods that received an "action" as parameter have been removed. When a parameter is required it can now be specified as a matrix or query parameter, for example, to delete a storage domain specifying a host:

  DELETE /api/storagedomains/{storagedomainid};host=myhost

If the parameter is missing, then an error message will be returned:

  <fault>
    <reason>host parameter is missing</reason>
  </fault>

Comment 6 Lucie Leistnerova 2016-08-16 10:35:10 UTC
DELETE /api/storagedomains/{storagedomainid} returns fault 'parameter is missing'

DELETE /api/storagedomains/{storagedomainid}?host=xxx deletes storage sucessfully and returns status complete

both in Version 4 (without header "Version:..") and it ignores anything in the body, I tried other DELETE commands (vms,hosts,storagesessions,...) and all ignores body

verified in ovirt-engine-restapi-4.0.2.6-0.1.el7ev.noarch