Load librpm on demand for IPAVersion

ctypes.util.find_library() is costly and slows down startup of ipa CLI.
ipaplatform.redhat.tasks now defers loading of librpm until its needed.
CFFI has been replaced with ctypes, too.

See: https://pagure.io/freeipa/issue/6851
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
Christian Heimes 2018-04-04 17:30:37 +02:00
parent b7293a9184
commit b82a2295b8
2 changed files with 46 additions and 12 deletions

View File

@ -25,6 +25,7 @@ system tasks.
'''
from __future__ import print_function
import ctypes
import logging
import os
import socket
@ -36,7 +37,6 @@ from ctypes.util import find_library
from functools import total_ordering
from subprocess import CalledProcessError
from cffi import FFI
from pyasn1.error import PyAsn1Error
from six.moves import urllib
@ -50,15 +50,6 @@ from ipaplatform.base.tasks import BaseTaskNamespace
logger = logging.getLogger(__name__)
_ffi = FFI()
_ffi.cdef("""
int rpmvercmp (const char *a, const char *b);
""")
# use ctypes loader to get correct librpm.so library version according to
# https://cffi.readthedocs.org/en/latest/overview.html#id8
_librpm = _ffi.dlopen(find_library("rpm"))
def selinux_enabled():
"""
@ -78,6 +69,21 @@ def selinux_enabled():
@total_ordering
class IPAVersion(object):
_rpmvercmp_func = None
@classmethod
def _rpmvercmp(cls, a, b):
"""Lazy load and call librpm's rpmvercmp
"""
rpmvercmp_func = cls._rpmvercmp_func
if rpmvercmp_func is None:
librpm = ctypes.CDLL(find_library('rpm'))
rpmvercmp_func = librpm.rpmvercmp
# int rpmvercmp(const char *a, const char *b)
rpmvercmp_func.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
rpmvercmp_func.restype = ctypes.c_int
cls._rpmvercmp_func = rpmvercmp_func
return rpmvercmp_func(a, b)
def __init__(self, version):
self._version = version
@ -90,12 +96,12 @@ class IPAVersion(object):
def __eq__(self, other):
if not isinstance(other, IPAVersion):
return NotImplemented
return _librpm.rpmvercmp(self._bytes, other._bytes) == 0
return self._rpmvercmp(self._bytes, other._bytes) == 0
def __lt__(self, other):
if not isinstance(other, IPAVersion):
return NotImplemented
return _librpm.rpmvercmp(self._bytes, other._bytes) < 0
return self._rpmvercmp(self._bytes, other._bytes) < 0
def __hash__(self):
return hash(self._version)

View File

@ -0,0 +1,28 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
from ipaplatform.tasks import tasks
def test_ipa_version():
v3 = tasks.parse_ipa_version('3.0')
assert v3.version == u'3.0'
if hasattr(v3, '_rpmvercmp'):
assert v3._rpmvercmp_func is None
v3._rpmvercmp(b'1', b'2')
assert v3._rpmvercmp_func is not None
v4 = tasks.parse_ipa_version('4.0')
assert v4.version == u'4.0'
if hasattr(v4, '_rpmvercmp'):
assert v4._rpmvercmp_func is not None
assert v3 < v4
assert v3 <= v4
assert v3 <= v3
assert v3 != v4
assert v3 == v3
assert not v3 == v4
assert v4 > v3
assert v4 >= v3