Use namespace-aware meta importer for ipaplatform

Instead of symlinks and build-time configuration the ipaplatform module
is now able to auto-detect platforms on import time. The meta importer
uses the platform 'ID' from /etc/os-releases. It falls back to 'ID_LIKE'
on platforms like CentOS, which has ID=centos and ID_LIKE="rhel fedora".

The meta importer is able to handle namespace packages and the
ipaplatform package has been turned into a namespace package in order to
support external platform specifications.

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

Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
Christian Heimes
2017-11-15 14:17:24 +01:00
parent f6adf4f3a8
commit a48f6511f6
41 changed files with 422 additions and 141 deletions
+1 -5
View File
@@ -108,11 +108,7 @@ freeipa2-dev-doc
/client/ipa-join /client/ipa-join
/client/ipa-rmkeytab /client/ipa-rmkeytab
/ipaplatform/override.py
/ipapython/version.py /ipapython/version.py
/ipapython/.DEFAULT_PLUGINS /ipapython/.DEFAULT_PLUGINS
/ipaplatform/__init__.py
/ipaplatform/constants.py
/ipaplatform/paths.py
/ipaplatform/services.py
/ipaplatform/tasks.py
+2 -2
View File
@@ -28,9 +28,9 @@ env:
test_install test_install
test_ipaclient test_ipaclient
test_ipalib test_ipalib
test_ipaplatform
test_ipapython test_ipapython
test_ipaserver test_ipaserver
test_integration/test_ipalib_util.py
test_xmlrpc/test_[l-z]*.py" test_xmlrpc/test_[l-z]*.py"
- TASK_TO_RUN="run-tests" - TASK_TO_RUN="run-tests"
PYTHON=/usr/bin/python3 PYTHON=/usr/bin/python3
@@ -43,9 +43,9 @@ env:
test_install test_install
test_ipaclient test_ipaclient
test_ipalib test_ipalib
test_ipaplatform
test_ipapython test_ipapython
test_ipaserver test_ipaserver
test_integration/test_ipalib_util.py
test_xmlrpc/test_[l-uw-z]*.py" test_xmlrpc/test_[l-uw-z]*.py"
# FIXME: add vault tests once PKI finally fixes vault # FIXME: add vault tests once PKI finally fixes vault
install: install:
+15 -7
View File
@@ -8,10 +8,14 @@ if WITH_IPATESTS
IPATESTS_SUBDIRS = ipatests IPATESTS_SUBDIRS = ipatests
endif endif
IPACLIENT_SUBDIRS = ipaclient ipalib ipapython IPACLIENT_SUBDIRS = ipaclient ipalib ipaplatform ipapython
IPA_PLACEHOLDERS = freeipa ipa ipaplatform ipaserver ipatests IPA_PLACEHOLDERS = freeipa ipa ipaserver ipatests
SUBDIRS = asn1 util client contrib po pypi \ SUBDIRS = asn1 util client contrib po pypi \
$(IPACLIENT_SUBDIRS) ipaplatform $(IPATESTS_SUBDIRS) $(SERVER_SUBDIRS) $(IPACLIENT_SUBDIRS) $(IPATESTS_SUBDIRS) $(SERVER_SUBDIRS)
GENERATED_PYTHON_FILES = \
$(top_builddir)/ipaplatform/override.py \
$(top_builddir)/ipapython/version.py
MOSTLYCLEANFILES = ipasetup.pyc ipasetup.pyo \ MOSTLYCLEANFILES = ipasetup.pyc ipasetup.pyo \
pylint_plugins.pyc pylint_plugins.pyo pylint_plugins.pyc pylint_plugins.pyo
@@ -133,7 +137,7 @@ _srpms-body: _rpms-prep
rm -f rm -f $(top_builddir)/.version rm -f rm -f $(top_builddir)/.version
.PHONY: lite-server .PHONY: lite-server
lite-server: $(top_builddir)/ipapython/version.py lite-server: $(GENERATED_PYTHON_FILES)
+$(MAKE) -C $(top_builddir)/install/ui +$(MAKE) -C $(top_builddir)/install/ui
PYTHONPATH=$(top_srcdir) $(PYTHON) -bb \ PYTHONPATH=$(top_srcdir) $(PYTHON) -bb \
contrib/lite-server.py $(LITESERVER_ARGS) contrib/lite-server.py $(LITESERVER_ARGS)
@@ -179,16 +183,20 @@ else
endif endif
@echo "All tests passed." @echo "All tests passed."
.PHONY: $(top_builddir)/ipaplatform/override.py
$(top_builddir)/ipaplatform/override.py:
(cd $(top_builddir)/ipaplatform && make override.py)
.PHONY: $(top_builddir)/ipapython/version.py .PHONY: $(top_builddir)/ipapython/version.py
$(top_builddir)/ipapython/version.py: $(top_builddir)/ipapython/version.py:
(cd $(top_builddir)/ipapython && make version.py) (cd $(top_builddir)/ipapython && make version.py)
.PHONY: acilint .PHONY: acilint
acilint: $(top_builddir)/ipapython/version.py acilint: $(GENERATED_PYTHON_FILES)
cd $(srcdir); ./makeaci --validate cd $(srcdir); ./makeaci --validate
.PHONY: apilint .PHONY: apilint
apilint: $(top_builddir)/ipapython/version.py apilint: $(GENERATED_PYTHON_FILES)
cd $(srcdir); ./makeapi --validate cd $(srcdir); ./makeapi --validate
.PHONY: polint .PHONY: polint
@@ -203,7 +211,7 @@ polint:
.PHONY: pylint .PHONY: pylint
if WITH_PYLINT if WITH_PYLINT
pylint: $(top_builddir)/ipapython/version.py ipasetup.py pylint: $(GENERATED_PYTHON_FILES) ipasetup.py
FILES=`find $(top_srcdir) \ FILES=`find $(top_srcdir) \
-type d -exec test -e '{}/__init__.py' \; -print -prune -o \ -type d -exec test -e '{}/__init__.py' \; -print -prune -o \
-path './rpmbuild' -prune -o \ -path './rpmbuild' -prune -o \
-8
View File
@@ -520,13 +520,6 @@ AC_SUBST(LDFLAGS)
# Files # Files
AC_CONFIG_LINKS([ipaplatform/__init__.py:ipaplatform/$IPAPLATFORM/__init__.py
ipaplatform/constants.py:ipaplatform/$IPAPLATFORM/constants.py
ipaplatform/paths.py:ipaplatform/$IPAPLATFORM/paths.py
ipaplatform/services.py:ipaplatform/$IPAPLATFORM/services.py
ipaplatform/tasks.py:ipaplatform/$IPAPLATFORM/tasks.py
])
AC_CONFIG_FILES([ AC_CONFIG_FILES([
Makefile Makefile
asn1/Makefile asn1/Makefile
@@ -594,7 +587,6 @@ AC_CONFIG_FILES([
pypi/Makefile pypi/Makefile
pypi/freeipa/Makefile pypi/freeipa/Makefile
pypi/ipa/Makefile pypi/ipa/Makefile
pypi/ipaplatform/Makefile
pypi/ipaserver/Makefile pypi/ipaserver/Makefile
pypi/ipatests/Makefile pypi/ipatests/Makefile
po/Makefile.in po/Makefile.in
+2
View File
@@ -1637,6 +1637,7 @@ fi
%{python_sitelib}/ipapython-*.egg-info %{python_sitelib}/ipapython-*.egg-info
%{python_sitelib}/ipalib-*.egg-info %{python_sitelib}/ipalib-*.egg-info
%{python_sitelib}/ipaplatform-*.egg-info %{python_sitelib}/ipaplatform-*.egg-info
%{python_sitelib}/ipaplatform-*-nspkg.pth
%files common -f %{gettext_domain}.lang %files common -f %{gettext_domain}.lang
@@ -1658,6 +1659,7 @@ fi
%{python3_sitelib}/ipapython-*.egg-info %{python3_sitelib}/ipapython-*.egg-info
%{python3_sitelib}/ipalib-*.egg-info %{python3_sitelib}/ipalib-*.egg-info
%{python3_sitelib}/ipaplatform-*.egg-info %{python3_sitelib}/ipaplatform-*.egg-info
%{python3_sitelib}/ipaplatform-*-nspkg.pth
%endif # with_python3 %endif # with_python3
+3 -10
View File
@@ -28,6 +28,7 @@ of the process.
For the per-request thread-local information, see `ipalib.request`. For the per-request thread-local information, see `ipalib.request`.
""" """
from __future__ import absolute_import
import os import os
from os import path from os import path
@@ -39,6 +40,7 @@ from six.moves.urllib.parse import urlparse, urlunparse
from six.moves.configparser import RawConfigParser, ParsingError from six.moves.configparser import RawConfigParser, ParsingError
# pylint: enable=import-error # pylint: enable=import-error
from ipaplatform.tasks import tasks
from ipapython.dn import DN from ipapython.dn import DN
from ipalib.base import check_name from ipalib.base import check_name
from ipalib.constants import ( from ipalib.constants import (
@@ -47,12 +49,6 @@ from ipalib.constants import (
TLS_VERSIONS TLS_VERSIONS
) )
from ipalib import errors from ipalib import errors
try:
# pylint: disable=ipa-forbidden-import
from ipaplatform.tasks import tasks
# pylint: enable=ipa-forbidden-import
except ImportError:
tasks = None
if six.PY3: if six.PY3:
unicode = str unicode = str
@@ -451,10 +447,7 @@ class Env(object):
self.script = path.abspath(sys.argv[0]) self.script = path.abspath(sys.argv[0])
self.bin = path.dirname(self.script) self.bin = path.dirname(self.script)
self.home = os.environ.get('HOME', None) self.home = os.environ.get('HOME', None)
self.fips_mode = tasks.is_fips_enabled()
# Set fips_mode only if ipaplatform module was loaded
if tasks is not None:
self.fips_mode = tasks.is_fips_enabled()
# Merge in overrides: # Merge in overrides:
self._merge(**overrides) self._merge(**overrides)
+1
View File
@@ -37,6 +37,7 @@ if __name__ == '__main__':
"ipalib.install", "ipalib.install",
], ],
install_requires=[ install_requires=[
"ipaplatform",
"ipapython", "ipapython",
"netaddr", "netaddr",
"pyasn1", "pyasn1",
+4 -3
View File
@@ -55,6 +55,7 @@ from ipalib.constants import (
TLS_VERSIONS, TLS_VERSION_MINIMAL, TLS_HIGH_CIPHERS TLS_VERSIONS, TLS_VERSION_MINIMAL, TLS_HIGH_CIPHERS
) )
from ipalib.text import _ from ipalib.text import _
from ipaplatform.paths import paths
from ipapython.ssh import SSHPublicKey from ipapython.ssh import SSHPublicKey
from ipapython.dn import DN, RDN from ipapython.dn import DN, RDN
from ipapython.dnsutil import DNSName from ipapython.dnsutil import DNSName
@@ -1101,9 +1102,9 @@ def check_client_configuration():
""" """
Check if IPA client is configured on the system. Check if IPA client is configured on the system.
""" """
if (not os.path.isfile(_IPA_DEFAULT_CONF) or if (not os.path.isfile(paths.IPA_DEFAULT_CONF) or
not os.path.isdir(_IPA_CLIENT_SYSRESTORE) or not os.path.isdir(paths.IPA_CLIENT_SYSRESTORE) or
not os.listdir(_IPA_CLIENT_SYSRESTORE)): not os.listdir(paths.IPA_CLIENT_SYSRESTORE)):
raise ScriptError('IPA client is not configured on this system') raise ScriptError('IPA client is not configured on this system')
+11
View File
@@ -1 +1,12 @@
include $(top_srcdir)/Makefile.python.am include $(top_srcdir)/Makefile.python.am
EXTRA_DIST = override.py.in
all-local: override.py
dist-hook: override.py
install-exec-local: override.py
override.py: override.py.in $(top_builddir)/$(CONFIG_STATUS)
$(AM_V_GEN)sed \
-e 's|@IPAPLATFORM[@]|$(IPAPLATFORM)|g' \
$< > $@
+11
View File
@@ -0,0 +1,11 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
"""ipaplatform namespace package
In the presence of a namespace package, any code in this module will be
ignore.
"""
__import__('pkg_resources').declare_namespace(__name__)
NAME = None # initialized by IpaMetaImporter
+150
View File
@@ -0,0 +1,150 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
"""Meta import hook for ipaplatform.
Known Linux distros with /etc/os-release
----------------------------------------
- alpine
- centos (like rhel, fedora)
- debian
- fedora
- rhel
- ubuntu (like debian)
"""
import importlib
import io
import re
import sys
import warnings
import ipaplatform
try:
from ipaplatform.override import OVERRIDE
except ImportError:
OVERRIDE = None
_osrelease_line = re.compile(
u"^(?!#)(?P<name>[a-zA-Z0-9_]+)="
u"(?P<quote>[\"\']?)(?P<value>.+)(?P=quote)$"
)
class IpaMetaImporter(object):
"""Meta import hook and platform detector.
The meta import hook uses /etc/os-release to auto-detects the best
matching ipaplatform provider. It is compatible with external namespace
packages, too.
"""
modules = {
'ipaplatform.constants',
'ipaplatform.paths',
'ipaplatform.services',
'ipaplatform.tasks'
}
bsd_family = (
'freebsd',
'openbsd',
'netbsd',
'dragonfly',
'gnukfreebsd'
)
def __init__(self, override=OVERRIDE):
self.override = override
self.platform_ids = self._get_platform_ids(self.override)
self.platform = self._get_platform(self.platform_ids)
def _get_platform_ids(self, override):
platforms = []
# allow RPM and Debian packages to override platform
if override is not None:
platforms.append(override)
if sys.platform.startswith('linux'):
# Linux, get distribution from /etc/os-release
try:
platforms.extend(self._parse_osrelease())
except Exception as e:
warnings.warn("Failed to read /etc/os-release: {}".format(e))
elif sys.platform == 'win32':
# Windows 32 or 64bit platform
platforms.append('win32')
elif sys.platform == 'darwin':
# macOS
platforms.append('macos')
elif sys.platform.startswith(self.bsd_family):
# BSD family, look for e.g. ['freebsd10', 'freebsd']
platforms.append(sys.platform)
simple = sys.platform.rstrip('0123456789')
if simple != sys.platform:
platforms.append(simple)
if not platforms:
raise ValueError("Unsupported platform: {}".format(sys.platform))
return platforms
def _parse_osrelease(self, filename='/etc/os-release'):
release = {}
with io.open(filename, encoding='utf-8') as f:
for line in f:
mo = _osrelease_line.match(line)
if mo is not None:
release[mo.group('name')] = mo.group('value')
platforms = [
release['ID'],
]
if "ID_LIKE" in release:
platforms.extend(
v.strip() for v in release['ID_LIKE'].split(' ') if v.strip()
)
return platforms
def _get_platform(self, platform_ids):
for platform in platform_ids:
try:
importlib.import_module('ipaplatform.{}'.format(platform))
except ImportError:
pass
else:
return platform
raise ImportError('No ipaplatform available for "{}"'.format(
', '.join(platform_ids)))
def find_module(self, fullname, path=None):
"""Meta importer hook"""
if fullname in self.modules:
return self
return None
def load_module(self, fullname):
"""Meta importer hook"""
suffix = fullname.split('.', 1)[1]
alias = 'ipaplatform.{}.{}'.format(self.platform, suffix)
platform_mod = importlib.import_module(alias)
base_mod = sys.modules.get(fullname)
if base_mod is not None:
# module has been imported before, update its __dict__
base_mod.__dict__.update(platform_mod.__dict__)
for key in list(base_mod.__dict__):
if not hasattr(platform_mod, key):
delattr(base_mod, key)
else:
sys.modules[fullname] = platform_mod
return platform_mod
metaimporter = IpaMetaImporter()
sys.meta_path.insert(0, metaimporter)
fixup_module = metaimporter.load_module
ipaplatform.NAME = metaimporter.platform
+3
View File
@@ -37,3 +37,6 @@ class BaseConstantsNamespace(object):
'httpd_dbus_sssd': 'on', 'httpd_dbus_sssd': 'on',
} }
SSSD_USER = "sssd" SSSD_USER = "sssd"
constants = BaseConstantsNamespace()
+3 -1
View File
@@ -355,5 +355,7 @@ class BasePathNamespace(object):
GSSPROXY_CONF = '/etc/gssproxy/10-ipa.conf' GSSPROXY_CONF = '/etc/gssproxy/10-ipa.conf'
KRB5CC_HTTPD = '/tmp/krb5cc-httpd' KRB5CC_HTTPD = '/tmp/krb5cc-httpd'
IF_INET6 = '/proc/net/if_inet6' IF_INET6 = '/proc/net/if_inet6'
AUTHCONFIG = None
path_namespace = BasePathNamespace
paths = BasePathNamespace()
+6 -2
View File
@@ -505,8 +505,12 @@ class SystemdService(PlatformService):
# Objects below are expected to be exported by platform module # Objects below are expected to be exported by platform module
service = None def base_service_class_factory(name, api=None):
knownservices = None raise NotImplementedError
service = base_service_class_factory
knownservices = KnownServices({})
# System may support more time&date services. FreeIPA supports ntpd only, other # System may support more time&date services. FreeIPA supports ntpd only, other
# services will be disabled during IPA installation # services will be disabled during IPA installation
+6
View File
@@ -204,6 +204,9 @@ class BaseTaskNamespace(object):
"""Configure httpd service to work with IPA""" """Configure httpd service to work with IPA"""
raise NotImplementedError() raise NotImplementedError()
def configure_http_gssproxy_conf(self, ipauser):
raise NotImplementedError()
def remove_httpd_service_ipa_conf(self): def remove_httpd_service_ipa_conf(self):
"""Remove configuration of httpd service of IPA""" """Remove configuration of httpd service of IPA"""
raise NotImplementedError() raise NotImplementedError()
@@ -219,3 +222,6 @@ class BaseTaskNamespace(object):
logger.debug('Done adding user to group') logger.debug('Done adding user to group')
except ipautil.CalledProcessError as e: except ipautil.CalledProcessError as e:
logger.debug('Failed to add user to group: %s', e) logger.debug('Failed to add user to group: %s', e)
tasks = BaseTaskNamespace()
+8
View File
@@ -0,0 +1,8 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
"""IpaMetaImporter replaces this module with ipaplatform.$NAME.constants.
"""
import ipaplatform._importhook
ipaplatform._importhook.fixup_module('ipaplatform.constants')
+1
View File
@@ -0,0 +1 @@
OVERRIDE = '@IPAPLATFORM@'
+8
View File
@@ -0,0 +1,8 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
"""IpaMetaImporter replaces this module with ipaplatform.$NAME.paths.
"""
import ipaplatform._importhook
ipaplatform._importhook.fixup_module('ipaplatform.paths')
+8
View File
@@ -0,0 +1,8 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
"""IpaMetaImporter replaces this module with ipaplatform.$NAME.services.
"""
import ipaplatform._importhook
ipaplatform._importhook.fixup_module('ipaplatform.services')
+1
View File
@@ -32,6 +32,7 @@ if __name__ == '__main__':
name="ipaplatform", name="ipaplatform",
doc=__doc__, doc=__doc__,
package_dir={'ipaplatform': ''}, package_dir={'ipaplatform': ''},
namespace_packages=['ipaplatform'],
packages=[ packages=[
"ipaplatform", "ipaplatform",
"ipaplatform.base", "ipaplatform.base",
+8
View File
@@ -0,0 +1,8 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
"""IpaMetaImporter replaces this module with ipaplatform.$NAME.tasks.
"""
import ipaplatform._importhook
ipaplatform._importhook.fixup_module('ipaplatform.tasks')
+9 -19
View File
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from __future__ import absolute_import
import collections import collections
import logging import logging
@@ -30,24 +31,12 @@ import shutil
import cryptography.x509 import cryptography.x509
from ipaplatform.paths import paths
from ipapython.dn import DN from ipapython.dn import DN
from ipapython.kerberos import Principal from ipapython.kerberos import Principal
from ipapython import ipautil from ipapython import ipautil
from ipalib import x509 # pylint: disable=ipa-forbidden-import from ipalib import x509 # pylint: disable=ipa-forbidden-import
try:
# pylint: disable=import-error,ipa-forbidden-import
from ipaplatform.paths import paths
# pylint: enable=import-error,ipa-forbidden-import
except ImportError:
CERTUTIL = '/usr/bin/certutil'
PK12UTIL = '/usr/bin/pk12util'
OPENSSL = '/usr/bin/openssl'
else:
CERTUTIL = paths.CERTUTIL
PK12UTIL = paths.PK12UTIL
OPENSSL = paths.OPENSSL
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -188,7 +177,8 @@ def verify_kdc_cert_validity(kdc_cert, ca_certs, realm):
try: try:
ipautil.run( ipautil.run(
[OPENSSL, 'verify', '-CAfile', ca_file.name, kdc_file.name], [paths.OPENSSL, 'verify', '-CAfile', ca_file.name,
kdc_file.name],
capture_output=True) capture_output=True)
except ipautil.CalledProcessError as e: except ipautil.CalledProcessError as e:
raise ValueError(e.output) raise ValueError(e.output)
@@ -244,7 +234,7 @@ class NSSDatabase(object):
self.close() self.close()
def run_certutil(self, args, stdin=None, **kwargs): def run_certutil(self, args, stdin=None, **kwargs):
new_args = [CERTUTIL, "-d", self.secdir] new_args = [paths.CERTUTIL, "-d", self.secdir]
new_args = new_args + args new_args = new_args + args
new_args.extend(['-f', self.pwd_file]) new_args.extend(['-f', self.pwd_file])
return ipautil.run(new_args, stdin, **kwargs) return ipautil.run(new_args, stdin, **kwargs)
@@ -367,7 +357,7 @@ class NSSDatabase(object):
return root_nicknames return root_nicknames
def export_pkcs12(self, nickname, pkcs12_filename, pkcs12_passwd=None): def export_pkcs12(self, nickname, pkcs12_filename, pkcs12_passwd=None):
args = [PK12UTIL, "-d", self.secdir, args = [paths.PK12UTIL, "-d", self.secdir,
"-o", pkcs12_filename, "-o", pkcs12_filename,
"-n", nickname, "-n", nickname,
"-k", self.pwd_file] "-k", self.pwd_file]
@@ -391,7 +381,7 @@ class NSSDatabase(object):
pkcs12_password_file.close() pkcs12_password_file.close()
def import_pkcs12(self, pkcs12_filename, pkcs12_passwd=None): def import_pkcs12(self, pkcs12_filename, pkcs12_passwd=None):
args = [PK12UTIL, "-d", self.secdir, args = [paths.PK12UTIL, "-d", self.secdir,
"-i", pkcs12_filename, "-i", pkcs12_filename,
"-k", self.pwd_file, '-v'] "-k", self.pwd_file, '-v']
pkcs12_password_file = None pkcs12_password_file = None
@@ -501,7 +491,7 @@ class NSSDatabase(object):
(key_file, filename)) (key_file, filename))
args = [ args = [
OPENSSL, 'pkcs8', paths.OPENSSL, 'pkcs8',
'-topk8', '-topk8',
'-passout', 'file:' + self.pwd_file, '-passout', 'file:' + self.pwd_file,
] ]
@@ -588,7 +578,7 @@ class NSSDatabase(object):
out_password = ipautil.ipa_generate_password() out_password = ipautil.ipa_generate_password()
out_pwdfile = ipautil.write_tmp_file(out_password) out_pwdfile = ipautil.write_tmp_file(out_password)
args = [ args = [
OPENSSL, 'pkcs12', paths.OPENSSL, 'pkcs12',
'-export', '-export',
'-in', in_file.name, '-in', in_file.name,
'-out', out_file.name, '-out', out_file.name,
+3 -10
View File
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from __future__ import absolute_import
# pylint: disable=deprecated-module # pylint: disable=deprecated-module
from optparse import ( from optparse import (
@@ -33,18 +34,10 @@ from six.moves.configparser import SafeConfigParser
from six.moves.urllib.parse import urlsplit from six.moves.urllib.parse import urlsplit
# pylint: enable=import-error # pylint: enable=import-error
from ipaplatform.paths import paths
from ipapython.dn import DN from ipapython.dn import DN
from ipapython.ipautil import CheckedIPAddress, CheckedIPAddressLoopback from ipapython.ipautil import CheckedIPAddress, CheckedIPAddressLoopback
try:
# pylint: disable=ipa-forbidden-import
from ipaplatform.paths import paths
# pylint: enable=ipa-forbidden-import
except ImportError:
IPA_DEFAULT_CONF = '/etc/ipa/default.conf'
else:
IPA_DEFAULT_CONF = paths.IPA_DEFAULT_CONF
class IPAConfigError(Exception): class IPAConfigError(Exception):
def __init__(self, msg=''): def __init__(self, msg=''):
@@ -188,7 +181,7 @@ config = IPAConfig()
def __parse_config(discover_server = True): def __parse_config(discover_server = True):
p = SafeConfigParser() p = SafeConfigParser()
p.read(IPA_DEFAULT_CONF) p.read(paths.IPA_DEFAULT_CONF)
try: try:
if not config.default_realm: if not config.default_realm:
+1
View File
@@ -42,6 +42,7 @@ if __name__ == '__main__':
"dnspython", "dnspython",
"gssapi", "gssapi",
# "ipalib", # circular dependency # "ipalib", # circular dependency
"ipaplatform",
"netaddr", "netaddr",
"netifaces", "netifaces",
"six", "six",
+9 -4
View File
@@ -25,22 +25,27 @@ class build_py(setuptools_build_py):
""" """
def initialize_options(self): def initialize_options(self):
setuptools_build_py.initialize_options(self) setuptools_build_py.initialize_options(self)
self.skip_package = None self.skip_modules = ()
def finalize_options(self): def finalize_options(self):
setuptools_build_py.finalize_options(self) setuptools_build_py.finalize_options(self)
omit = os.environ.get('IPA_OMIT_INSTALL', '0') omit = os.environ.get('IPA_OMIT_INSTALL', '0')
if omit == '1': if omit == '1':
distname = self.distribution.metadata.name distname = self.distribution.metadata.name
self.skip_package = '{}.install'.format(distname) self.skip_modules = (
# *.install.* subpackages
'{}.install'.format(distname),
# platform override module
'ipaplatform.override',
)
log.warn("bdist_wheel: Ignore package: %s", log.warn("bdist_wheel: Ignore package: %s",
self.skip_package) ', '.join(self.skip_modules))
def build_module(self, module, module_file, package): def build_module(self, module, module_file, package):
if isinstance(package, str): if isinstance(package, str):
package = package.split('.') package = package.split('.')
name = '.'.join(list(package) + [module]) name = '.'.join(list(package) + [module])
if self.skip_package and name.startswith(self.skip_package): if self.skip_modules and name.startswith(self.skip_modules):
# remove file in case it has been copied to build/lib before # remove file in case it has been copied to build/lib before
outfile = self.get_module_outfile(self.build_lib, package, module) outfile = self.get_module_outfile(self.build_lib, package, module)
try: try:
+3
View File
@@ -41,6 +41,7 @@ if __name__ == '__main__':
"ipatests.test_integration", "ipatests.test_integration",
"ipatests.test_ipaclient", "ipatests.test_ipaclient",
"ipatests.test_ipalib", "ipatests.test_ipalib",
"ipatests.test_ipaplatform",
"ipatests.test_ipapython", "ipatests.test_ipapython",
"ipatests.test_ipaserver", "ipatests.test_ipaserver",
"ipatests.test_ipaserver.test_install", "ipatests.test_ipaserver.test_install",
@@ -54,6 +55,7 @@ if __name__ == '__main__':
'ipatests.test_integration': ['scripts/*'], 'ipatests.test_integration': ['scripts/*'],
'ipatests.test_ipaclient': ['data/*/*/*'], 'ipatests.test_ipaclient': ['data/*/*/*'],
'ipatests.test_ipalib': ['data/*'], 'ipatests.test_ipalib': ['data/*'],
'ipatests.test_ipaplatform': ['data/*'],
"ipatests.test_ipaserver": ['data/*'], "ipatests.test_ipaserver": ['data/*'],
'ipatests.test_xmlrpc': ['data/*'], 'ipatests.test_xmlrpc': ['data/*'],
}, },
@@ -63,6 +65,7 @@ if __name__ == '__main__':
"gssapi", "gssapi",
"ipaclient", "ipaclient",
"ipalib", "ipalib",
"ipaplatform",
"ipapython", "ipapython",
"nose", "nose",
"polib", "polib",
@@ -1,23 +0,0 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
"""
Test the `ipalib.util` module.
This tests is in test_integration beucase we only can import ipaplatform here.
"""
from ipalib import util
from ipaplatform.paths import paths
import pytest
pytestmark = pytest.mark.tier0
def test_hardcoded_paths_are_right():
"""
Test if constants created in ipalib.util are in sync with
paths declared in ipaplatform.paths
"""
assert util._IPA_CLIENT_SYSRESTORE == paths.IPA_CLIENT_SYSRESTORE
assert util._IPA_DEFAULT_CONF == paths.IPA_DEFAULT_CONF
@@ -0,0 +1,15 @@
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
@@ -0,0 +1,16 @@
NAME=Fedora
VERSION="26 (Workstation Edition)"
ID=fedora
VERSION_ID=26
PRETTY_NAME="Fedora 26 (Workstation Edition)"
ANSI_COLOR="0;34"
CPE_NAME="cpe:/o:fedoraproject:fedora:26"
HOME_URL="https://fedoraproject.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=26
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=26
PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy
VARIANT="Workstation Edition"
VARIANT_ID=workstation
@@ -0,0 +1,11 @@
NAME="Ubuntu"
VERSION="16.04.3 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.3 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
@@ -0,0 +1,54 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
import os
import sys
import pytest
import ipaplatform.constants
import ipaplatform.paths
import ipaplatform.services
import ipaplatform.tasks
from ipaplatform._importhook import metaimporter
try:
from ipaplatform.override import OVERRIDE
except ImportError:
OVERRIDE = None
HERE = os.path.dirname(os.path.abspath(__file__))
DATA = os.path.join(HERE, 'data')
@pytest.mark.skipif(OVERRIDE is None,
reason='test requires override')
def test_override():
assert OVERRIDE == metaimporter.platform_ids[0]
assert OVERRIDE == metaimporter.platform
@pytest.mark.parametrize('mod, name', [
(ipaplatform.constants, 'ipaplatform.constants'),
(ipaplatform.paths, 'ipaplatform.paths'),
(ipaplatform.services, 'ipaplatform.services'),
(ipaplatform.tasks, 'ipaplatform.tasks'),
])
def test_importhook(mod, name):
assert name in metaimporter.modules
prefix, suffix = name.split('.')
assert prefix == 'ipaplatform'
override = '.'.join((prefix, metaimporter.platform, suffix))
assert mod.__name__ == override
# dicts are equal, modules may not be identical
assert mod.__dict__ == sys.modules[override].__dict__
@pytest.mark.parametrize('filename, expected_platforms', [
(os.path.join(DATA, 'os-release-centos'), ['centos', 'rhel', 'fedora']),
(os.path.join(DATA, 'os-release-fedora'), ['fedora']),
(os.path.join(DATA, 'os-release-ubuntu'), ['ubuntu', 'debian']),
])
def test_parse_os_release(filename, expected_platforms):
parsed = metaimporter._parse_osrelease(filename)
assert parsed == expected_platforms
+43
View File
@@ -269,6 +269,49 @@ def pytest_config_transform():
register_module_extender(MANAGER, 'pytest', pytest_config_transform) register_module_extender(MANAGER, 'pytest', pytest_config_transform)
def ipaplatform_constants_transform():
return AstroidBuilder(MANAGER).string_build(textwrap.dedent('''
from ipaplatform.base.constants import constants
__all__ = ('constants',)
'''))
def ipaplatform_paths_transform():
return AstroidBuilder(MANAGER).string_build(textwrap.dedent('''
from ipaplatform.base.paths import paths
__all__ = ('paths',)
'''))
def ipaplatform_services_transform():
return AstroidBuilder(MANAGER).string_build(textwrap.dedent('''
from ipaplatform.base.services import knownservices
from ipaplatform.base.services import timedate_services
from ipaplatform.base.services import service
from ipaplatform.base.services import wellknownservices
from ipaplatform.base.services import wellknownports
__all__ = ('knownservices', 'timedate_services', 'service',
'wellknownservices', 'wellknownports')
'''))
def ipaplatform_tasks_transform():
return AstroidBuilder(MANAGER).string_build(textwrap.dedent('''
from ipaplatform.base.tasks import tasks
__all__ = ('tasks',)
'''))
register_module_extender(MANAGER, 'ipaplatform.constants',
ipaplatform_constants_transform)
register_module_extender(MANAGER, 'ipaplatform.paths',
ipaplatform_paths_transform)
register_module_extender(MANAGER, 'ipaplatform.services',
ipaplatform_services_transform)
register_module_extender(MANAGER, 'ipaplatform.tasks',
ipaplatform_tasks_transform)
class IPAChecker(BaseChecker): class IPAChecker(BaseChecker):
__implements__ = IAstroidChecker __implements__ = IAstroidChecker
+3 -3
View File
@@ -116,9 +116,9 @@ dummy-variables-rgx=_.+
[IPA] [IPA]
forbidden-imports= forbidden-imports=
client/:ipaserver, client/:ipaserver,
ipaclient/:ipaclient.install:ipalib.install:ipaplatform:ipaserver, ipaclient/:ipaclient.install:ipalib.install:ipaserver,
ipaclient/install/:ipaserver, ipaclient/install/:ipaserver,
ipalib/:ipaclient.install:ipalib.install:ipaplatform:ipaserver, ipalib/:ipaclient.install:ipalib.install:ipaserver,
ipalib/install/:ipaserver, ipalib/install/:ipaserver,
ipaplatform/:ipaclient:ipalib:ipaserver, ipaplatform/:ipaclient:ipalib:ipaserver,
ipapython/:ipaclient:ipalib:ipaplatform:ipaserver ipapython/:ipaclient:ipalib:ipaserver
-1
View File
@@ -7,7 +7,6 @@ NULL =
SUBDIRS = \ SUBDIRS = \
freeipa \ freeipa \
ipa \ ipa \
ipaplatform \
ipaserver \ ipaserver \
ipatests \ ipatests \
$(NULL) $(NULL)
-3
View File
@@ -1,3 +0,0 @@
include $(top_srcdir)/Makefile.python.am
pkginstall = false
-2
View File
@@ -1,2 +0,0 @@
This is a dummy package for FreeIPA's ipaplatform.
-5
View File
@@ -1,5 +0,0 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
raise ImportError("ipaplatform is not yet supported as PyPI package.")
-6
View File
@@ -1,6 +0,0 @@
[bdist_wheel]
universal = 1
[aliases]
packages = clean --all egg_info bdist_wheel
release = packages register upload
-26
View File
@@ -1,26 +0,0 @@
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#
"""Dummy package for FreeIPA
ipaplatform is not yet available as PyPI package.
"""
from os.path import abspath, dirname
import sys
if __name__ == '__main__':
# include ../../ for ipasetup.py
sys.path.append(dirname(dirname(dirname(abspath(__file__)))))
from ipasetup import ipasetup # noqa: E402
ipasetup(
name='ipaplatform',
doc = __doc__,
packages=[
"ipaplatform",
],
install_requires=[
"ipaclient",
]
)
+3 -1
View File
@@ -9,13 +9,14 @@ import pytest
@pytest.mark.parametrize("modname", [ @pytest.mark.parametrize("modname", [
# placeholder packages raise ImportError # placeholder packages raise ImportError
'ipaplatform',
'ipaserver', 'ipaserver',
'ipatests', 'ipatests',
# PyPI packages do not have install subpackage # PyPI packages do not have install subpackage
'ipaclient.install', 'ipaclient.install',
'ipalib.install', 'ipalib.install',
'ipapython.install', 'ipapython.install',
# override module should not be shipped in wheels
'ipaplatform.override',
]) ])
def test_fail_import(modname): def test_fail_import(modname):
try: try:
@@ -29,6 +30,7 @@ def test_fail_import(modname):
@pytest.mark.parametrize("modname", [ @pytest.mark.parametrize("modname", [
'ipaclient', 'ipaclient',
'ipalib', 'ipalib',
'ipaplatform',
'ipapython', 'ipapython',
]) ])
def test_import(modname): def test_import(modname):