use FFI call to rpmvercmp function for version comparison

Stop using rpm-python to compare package versions since the implicit NSS
initialization upon  the module import breaks NSS handling in IPA code. Call
rpm-libs C-API function via CFFI instead.

Big thanks to Martin Kosek <mkosek@redhat.com> for sharing the code snippet
that spurred this patch.

https://fedorahosted.org/freeipa/ticket/5572

Reviewed-By: Jan Cholasta <jcholast@redhat.com>
This commit is contained in:
Martin Babinsky 2016-01-11 16:22:40 +01:00 committed by Jan Cholasta
parent bc6543efae
commit 7cd99e8520
2 changed files with 13 additions and 33 deletions

View File

@ -214,7 +214,7 @@ Requires: python-pyasn1
Requires: dbus-python Requires: dbus-python
Requires: python-dns >= 1.11.1 Requires: python-dns >= 1.11.1
Requires: python-kdcproxy >= 0.3 Requires: python-kdcproxy >= 0.3
Requires: rpm-python Requires: rpm-libs
%description -n python2-ipaserver %description -n python2-ipaserver
IPA is an integrated solution to provide centrally managed Identity (users, IPA is an integrated solution to provide centrally managed Identity (users,

View File

@ -30,13 +30,14 @@ import stat
import socket import socket
import sys import sys
import base64 import base64
from cffi import FFI
from ctypes.util import find_library
from functools import total_ordering from functools import total_ordering
from subprocess import CalledProcessError from subprocess import CalledProcessError
from nss.error import NSPRError from nss.error import NSPRError
from pyasn1.error import PyAsn1Error from pyasn1.error import PyAsn1Error
from six.moves import urllib from six.moves import urllib
import rpm
from ipapython.ipa_log_manager import root_logger, log_mgr from ipapython.ipa_log_manager import root_logger, log_mgr
from ipapython import ipautil from ipapython import ipautil
@ -48,35 +49,14 @@ from ipaplatform.paths import paths
from ipaplatform.redhat.authconfig import RedHatAuthConfig from ipaplatform.redhat.authconfig import RedHatAuthConfig
from ipaplatform.base.tasks import BaseTaskNamespace from ipaplatform.base.tasks import BaseTaskNamespace
_ffi = FFI()
_ffi.cdef("""
int rpmvercmp (const char *a, const char *b);
""")
# copied from rpmUtils/miscutils.py # use ctypes loader to get correct librpm.so library version according to
def stringToVersion(verstring): # https://cffi.readthedocs.org/en/latest/overview.html#id8
if verstring in [None, '']: _librpm = _ffi.dlopen(find_library("rpm"))
return (None, None, None)
i = verstring.find(':')
if i != -1:
try:
epoch = str(long(verstring[:i]))
except ValueError:
# look, garbage in the epoch field, how fun, kill it
epoch = '0' # this is our fallback, deal
else:
epoch = '0'
j = verstring.find('-')
if j != -1:
if verstring[i + 1:j] == '':
version = None
else:
version = verstring[i + 1:j]
release = verstring[j + 1:]
else:
if verstring[i + 1:] == '':
version = None
else:
version = verstring[i + 1:]
release = None
return (epoch, version, release)
log = log_mgr.get_logger(__name__) log = log_mgr.get_logger(__name__)
@ -101,15 +81,15 @@ def selinux_enabled():
class IPAVersion(object): class IPAVersion(object):
def __init__(self, version): def __init__(self, version):
self.version_tuple = stringToVersion(version) self.version = version
def __eq__(self, other): def __eq__(self, other):
assert isinstance(other, IPAVersion) assert isinstance(other, IPAVersion)
return rpm.labelCompare(self.version_tuple, other.version_tuple) == 0 return _librpm.rpmvercmp(self.version, other.version) == 0
def __lt__(self, other): def __lt__(self, other):
assert isinstance(other, IPAVersion) assert isinstance(other, IPAVersion)
return rpm.labelCompare(self.version_tuple, other.version_tuple) == -1 return _librpm.rpmvercmp(self.version, other.version) < 0
class RedHatTaskNamespace(BaseTaskNamespace): class RedHatTaskNamespace(BaseTaskNamespace):