Bug 1440292

Summary: Validate API URL to respond with proper error message
Product: [oVirt] ovirt-engine-sdk-python Reporter: Reto Gantenbein <reto.gantenbein>
Component: CoreAssignee: Ondra Machacek <omachace>
Status: CLOSED CURRENTRELEASE QA Contact: Petr Kubica <pkubica>
Severity: low Docs Contact:
Priority: unspecified    
Version: 4.1.3CC: bugs, juan.hernandez, lsvaty, mperina, stirabos, ylavi
Target Milestone: ovirt-4.1.3Flags: rule-engine: ovirt-4.1+
lsvaty: testing_ack+
Target Release: 4.1.5   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: python-ovirt-engine-sdk4-4.1.5 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2017-07-06 13:18:53 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 Reto Gantenbein 2017-04-07 19:45:28 UTC
Description of problem:
When using the 'ovirt' Ansible modules for the first time, I made a mistake by not specifying the full URL in the corresponding option. This resulted in a rather generic error message:

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: Exception: Can't read first node
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "Can't read first node"}

After searching the Ansible project for a hint, I stepped over [1] which mentioned this error message with a differently related HTTP 404 error. This made me aware of the wrong URL being the cause of my error message.


Version-Release number of selected component (if applicable):
- ansible 2.3.0.0 (detached HEAD f15e1f25ae)
- ovirt-engine-sdk-python 4.1.3


How reproducible:
Always


Steps to Reproduce:
Define a ovirt_auth task in Ansible with https://<ovirt-host>/ (missing the 'ovirt-engine/api' context root) 


Actual results:
"Can't read first node" python error


Expected results:
Error message indicating a HTTP error or wrong URL format


Additional info:
[1]: https://github.com/ansible/ansible/issues/19801

Comment 1 Juan Hernández 2017-04-10 08:36:47 UTC
Currently we are trying to parse the returned document as XML, but when the URL is incorrect the server will most likely return an HTML error message. Trying to parse that HTML as XML is what causes the "Can't read first node" message. We should probably check the "Conten-Type" header returned by the server, and generate a better error message in that case. That error message could include a hint about the URL. For example, we could return an error message like this, if the value of the 'url' parameter is not the typical one:

  The server returned content type 'text/html', but we expected 'text/xml'.
  Are you sure you are using the correct 'url' parameter? The typical one is
  '/ovirt-engine/api', but you are using '/'.

What we should not do, in my opinion, is forbid the use of other URLs.

If we decided to check the returned 'Content-Type' header then we need to be careful, as it may have multiple valid values:

  text/xml
  text/xml; charset=utf-8
  application/xml
  application/xml; charset=utf-8

Currently the server always returns 'application/xml', but that may change in the future.

Comment 2 Juan Hernández 2017-04-20 15:27:24 UTC
The current fix didn't take into account that the server doesn't return any content type for responses that don't have a body. For example, the following code will trigger a 404 error, without a response body, and without a content type:

   vm_service = vms_service.vm_service('doesnotexist')
   vm_service.get()

With the current fix that generates the following trackeback:

  Traceback (most recent call last):
    File "...", line 41, in <module>
      vm_service.get()
    File ".../ovirtsdk4/services.py", line 30596, in get
      response = self._connection.send(request)
    File ".../ovirtsdk4/__init__.py", line 295, in send
      return self.__send(request)
    File ".../ovirtsdk4/__init__.py", line 408, in __send
      self._check_content_type(self.__XML_CONTENT_TYPE_RE, 'XML', content_type)
    File ".../ovirtsdk4/__init__.py", line 689, in _check_content_type
      if expected_re.match(actual) is None:
  TypeError: expected string or buffer

Comment 3 Petr Kubica 2017-06-06 14:23:33 UTC
used: python-ovirt-engine-sdk4-4.1.4-1.el7ev.x86_64
tested url: "https://<engine-url>/ugly"

The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_RjJ0si/ansible_module_ovirt_datacenters.py", line 210, in main
    ret = clusters_module.create()
  File "/tmp/ansible_RjJ0si/ansible_modlib.zip/ansible/module_utils/ovirt.py", line 537, in create
    entity = self.search_entity(search_params)
  File "/tmp/ansible_RjJ0si/ansible_modlib.zip/ansible/module_utils/ovirt.py", line 757, in search_entity
    entity = search_by_attributes(self._service, name=self._module.params['name'])
  File "/tmp/ansible_RjJ0si/ansible_modlib.zip/ansible/module_utils/ovirt.py", line 229, in search_by_attributes
    search=' and '.join('{}={}'.format(k, v) for k, v in kwargs.items())
  File "/usr/lib64/python2.7/site-packages/ovirtsdk4/services.py", line 4401, in list
    return self._internal_get(headers, query, wait)
  File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 202, in _internal_get
    return future.wait() if wait else future
  File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 53, in wait
    return self._code(response)
  File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 199, in callback
    self._check_fault(response)
  File "/usr/lib64/python2.7/site-packages/ovirtsdk4/service.py", line 123, in _check_fault
    body = reader.Reader.read(response.body)
  File "/usr/lib64/python2.7/site-packages/ovirtsdk4/reader.py", line 297, in read
    cursor = xml.XmlReader(io.BytesIO(source.encode('utf-8')))
Exception: Can't read first node

fatal: [<engine-url>]: FAILED! => {
    "changed": false, 
    "failed": true, 
    "invocation": {
        "module_args": {
            "comment": null, 
            "compatibility_version": "4.0", 
            "description": null, 
            "fetch_nested": false, 
            "local": true, 
            "mac_pool": null, 
            "name": "mydatacenter", 
            "nested_attributes": [], 
            "poll_interval": 3, 
            "quota_mode": "enabled", 
            "state": "present", 
            "timeout": 180, 
            "wait": true
        }
    }, 
    "msg": "Can't read first node"
}

Comment 4 Red Hat Bugzilla Rules Engine 2017-06-06 14:23:40 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 Petr Kubica 2017-06-14 12:11:56 UTC
Verified in python-ovirt-engine-sdk4-4.1.5-1.el7ev.x86_64

Error: The response content type 'text/html; charset=iso-8859-1' isn't the expected XML. Is the path '/ugly' included in the 'url' parameter correct? The typical one is '/ovirt-engine/api'