Bug 1924928 - ovirt sdk tracebacks while parsing URL
Summary: ovirt sdk tracebacks while parsing URL
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: ovirt-engine-sdk-python
Classification: oVirt
Component: General
Version: 4.4.9
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
: ---
Assignee: Ori Liel
QA Contact: meital avital
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2021-02-03 22:17 UTC by Brendan Shephard
Modified: 2021-04-09 12:29 UTC (History)
4 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2021-03-11 10:07:37 UTC
oVirt Team: Infra
Embargoed:


Attachments (Terms of Use)

Description Brendan Shephard 2021-02-03 22:17:32 UTC
Description of problem:
oVirt SDK seems to not be formatting strings before parsing them, resulting in tracebacks with Python 3.6

(Traceback below in the Actual Results section)

This can be resolved by decoding the URL as UTF8 before parsing it:
/usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py:

    653         # Decode the URL to UTF8 format
    654         self._url = self._url.decode('UTF-8')
    655 
    656         # Set proper Authorization header if using kerberos:
    657         if self._kerberos:
    658             curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_GSSNEGOTIATE)
    659             curl.setopt(pycurl.USERPWD, ':')
    660 
    661         # Configure debug mode:
    662         if self._debug and self._log is not None:
    663             curl.setopt(pycurl.VERBOSE, 1)
    664             curl.setopt(pycurl.DEBUGFUNCTION, self._curl_debug)
    665 
    666         # Configure TLS parameters:
    667         if self._url.startswith('https'):
    668             curl.setopt(pycurl.SSL_VERIFYPEER, 0 if self._insecure else 1)
    669             curl.setopt(pycurl.SSL_VERIFYHOST, 0 if self._insecure else 2)
    670             if self._ca_file is not None:
    671                 curl.setopt(pycurl.CAINFO, self._ca_file)

Although, this leads to other issues, presumably in other scripts where the URL is still seen as bytes:

2021-02-04 08:02:42.758 8 ERROR ironic_staging_drivers.ovirt.ovirt [req-e3777549-b5eb-4b8d-8a5c-b58b3c8c66d7 c12188ef21664945bf44b7f35423f765 81e8b5c8134447fc9adc3c1025cdfb32 - default default] Could not fetch information about VM vm b'compute-1', got error: Error while sending HTTP request: (1, 'Protocol "b\'https\'" not supported or disabled in libcurl'): ovirtsdk4.Error: Error while sending HTTP request: (1, 'Protocol "b\'https\'" not supported or disabled in libcurl')   


Version-Release number of selected component (if applicable):
python3-ovirt-engine-sdk4-4.4.8-1.el8.x86_64
bash-4.4$ python3 --version
Python 3.6.8


How reproducible:
I'm using the SDK with Ironic in upstream TripleO and seeing this error. It doesn't seem to be a problem in RHOSP16.1 at the moment

Steps to Reproduce:
1. Install the ovirtsdk in the Ironic containers
2. Try to import VM's from oVirt into Ironic
3. Tracebacks are observed in the ironic-conductor.log file while parsing the URL

Actual results:
Traceback:
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager [req-a0b371dd-ac22-481f-a5de-8fa5ebef7e95 c12188ef21664945bf44b7f35423f765 81e8b5c8134447fc9adc3c1025cdfb32 - default default] Faile
d to get power state for node cc4ea979-2bcf-4591-8603-2455db9d8cbd. Error: startswith first arg must be bytes or a tuple of bytes, not str: TypeError: startswith first arg must be bytes or
a tuple of bytes, not str                                                                                                                                                                   
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager Traceback (most recent call last):                                                                                                 
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib/python3.6/site-packages/ironic/conductor/manager.py", line 1163, in _do_node_verify                               
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     power_state = task.driver.power.get_power_state(task)                                                                           2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib/python3.6/site-packages/ironic_staging_drivers/ovirt/ovirt.py", line 200, in get_power_state                       2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     with _getvm(driver_info) as vm:                                                                                                 2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib64/python3.6/contextlib.py", line 81, in __enter__
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     return next(self.gen)                                                                                                           2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib/python3.6/site-packages/ironic_staging_drivers/ovirt/ovirt.py", line 155, in _getvm
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     vmsearch = vms_service.list(search='name=%s' % name)                                                                            2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib64/python3.6/site-packages/ovirtsdk4/services.py", line 37486, in list                                              2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     return self._internal_get(headers, query, wait)                                                                                
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib64/python3.6/site-packages/ovirtsdk4/service.py", line 202, in _internal_get                                        2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     context = self._connection.send(request)                                                                                        
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py", line 371, in send                                                2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     return self.__send(request)                                                                                                    
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py", line 389, in __send                                             
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     self.authenticate()                                                                                                            
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py", line 382, in authenticate
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     self._sso_token = self._get_access_token()
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py", line 618, in _get_access_token
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     sso_response = self._get_sso_response(self._sso_url, post_data)
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager   File "/usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py", line 667, in _get_sso_response
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager     if self._url.startswith('https'):
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager TypeError: startswith first arg must be bytes or a tuple of bytes, not str
2021-02-04 08:01:25.908 8 ERROR ironic.conductor.manager

Expected results:
self._url needs to be passed to startswith in an acceptable format

Additional info:
I'll report back once it's all working and we can discuss if this is a problem with ovirtsdk, or the combination of python36 and the version of ovirtsdk I'm using. Quite possibly the latter. I can see Bugzilla wont let me select 4.4.8 and only 4.4.9. So maybe I need to go find that version and see if it makes a difference.

Comment 1 Brendan Shephard 2021-02-03 23:10:22 UTC
Ok, so I made sure I was using the latest ovirtsdk 4.4.9. Still getting the same error,

So, I added the decode the the top of the script when we first initialise the variable. That seems to have solved that problem, now the same problem with VM names:

2021-02-04 08:59:13.242 8 ERROR ironic.conductor.manager [req-4f1e7ff9-8f28-44a5-b36b-1f6e937f65d9 c12188ef21664945bf44b7f35423f765 81e8b5c8134447fc9adc3c1025cdfb32 - default default] Failed to get power state for node cc4ea979-2bcf-4591-8603-2455db9d8cbd. Error: VM with name b'compute-1' was not found: ironic_staging_drivers.common.exception.OVirtError: VM with name b'compute-1' was not found


So I'll change that one as well.

So far:

bash-4.4$ diff -u /usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py-backup /usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py
--- /usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py-backup     2021-02-04 09:03:27.134089438 +1000
+++ /usr/lib64/python3.6/site-packages/ovirtsdk4/__init__.py    2021-02-04 09:04:05.233743979 +1000
@@ -308,7 +308,7 @@
                 raise Error('The CA file \'%s\' doesn\'t exist' % ca_file)
 
         # Save the URL:
-        self._url = url
+        self._url = url.decode('UTF-8')
 
         # Save the logger:
         self._log = log

Comment 2 Brendan Shephard 2021-02-03 23:18:17 UTC
Next issue seems to be on the Ironic side, so we can ignore it for this bug.

Messy solution since we're encoding and decoding, but I can probably change ascii to utf-8 there instead. I'll look at that independently of this BZ though. Let's discuss the above change.


For completion, I also made this change on the Ironic side - albeit a hack solution that needs revision:

bash-4.4$ diff -u /usr/lib/python3.6/site-packages/ironic_staging_drivers/ovirt/ovirt.py-backup /usr/lib/python3.6/site-packages/ironic_staging_drivers/ovirt/ovirt.py
--- /usr/lib/python3.6/site-packages/ironic_staging_drivers/ovirt/ovirt.py-backup       2021-02-04 09:11:52.068016965 +1000
+++ /usr/lib/python3.6/site-packages/ironic_staging_drivers/ovirt/ovirt.py      2021-02-04 09:15:45.813164874 +1000
@@ -135,6 +135,7 @@
     ca_file = driver_info['ovirt_ca_file']
     name = str.encode(
         driver_info['ovirt_vm_name'], encoding='ascii', errors='ignore')
+    name = name.decode('UTF-8')
     url = "https://%s/ovirt-engine/api" % address
     try:
         # pycurl.Curl.setopt doesn't support unicode strings,

Comment 3 Brendan Shephard 2021-02-04 07:28:41 UTC
I'm fairly confident all objects passed from Ironic to ovirtsdk are all strings here. So there must be something turning them into byte objects at some point here.

I'll need to keep digging. But I'm happy for feedback from the oVirt side if you might know what's going on here.

Comment 4 Brendan Shephard 2021-03-11 10:07:37 UTC
So, I've spent some additional time on this. Basically the URL is being encoded for $reasons here:
https://opendev.org/x/ironic-staging-drivers/src/branch/master/ironic_staging_drivers/ovirt/ovirt.py#L143

Presumably to validate that it doesn't contain non-ascii characters. But we probably shouldn't send the ASCII encoded string over to ovirtsdk. I would say it's better to validate the string and then send the URL as a string.

I experimented with removing that section:
https://opendev.org/x/ironic-staging-drivers/src/branch/master/ironic_staging_drivers/ovirt/ovirt.py#L139-L148

This works, but then Ironic complains that it can't find the byte encoded VM names. So we're doing the same thing for the VM names:
https://opendev.org/x/ironic-staging-drivers/src/branch/master/ironic_staging_drivers/ovirt/ovirt.py#L136-L137

So, I'm fairly happy this is an issue on the Ironic side at this stage. Let's close this BZ off and I'll open a new one for Ironic and submit the relevant fixes over there.

Comment 5 Brendan Shephard 2021-04-06 04:59:26 UTC
For reference on this one, before I forget. This is what I'm doing to work around the issue for now:

[root@tripleo-director ironic]# podman exec -it -u0 ironic_conductor sed -n 136,153p /usr/lib/python3.6/site-packages/ironic_staging_drivers/ovirt/ovirt.py
    name = str.encode(
        driver_info['ovirt_vm_name'], encoding='ascii', errors='ignore')
    url = "https://%s/ovirt-engine/api" % address
    try:
        # pycurl.Curl.setopt doesn't support unicode strings,
        # attempt to turn `url` into an all-ASCII string;
        # in Python 3.x setopt accepts bytes as it should.
        url = str.encode(url, encoding='ascii', errors='strict')

    except UnicodeEncodeError:
        LOG.warning("oVirt URL '%(url)s' contains non-ascii characters, "
                    "that might cause pycurl to explode "
                    "momentarily", {'url': url})

    try:
        name = driver_info['ovirt_vm_name']
        url = "https://%s/ovirt-engine/api" % address
        connection = sdk.Connection(url=url, username=username,

So after this string encoding is done to validate them, I'm resetting them back to the originals.

It works for now, but needs a better solution.

Comment 6 Brendan Shephard 2021-04-09 12:29:37 UTC
Fixed in Ironic staging drivers here: https://review.opendev.org/c/x/ironic-staging-drivers/+/784879


Note You need to log in before you can comment on or make changes to this bug.