Imported Upstream version 4.0.5

This commit is contained in:
Mario Fetka
2021-07-25 07:50:50 +02:00
parent 8ff3be4216
commit 3bfaa6e020
2049 changed files with 317193 additions and 1632423 deletions

View File

@@ -27,8 +27,9 @@ Example plugins
# First, let's import some stuff.
# api is an object containing references to all plugins and useful classes.
# errors is a module containing all IPA specific exceptions.
from ipalib import errors
from ipalib import api, errors
# Command is the base class for command plugin.
from ipalib import Command
# Str is a subclass of Param, it is used to define string parameters for
@@ -41,12 +42,6 @@ from ipalib import Str
from ipalib import output
# To make the example ready for Python 3, we alias "unicode" to strings.
import six
if six.PY3:
unicode = str
# We're going to create an example command plugin, that takes a name as its
# only argument. Commands in IPA support input validation by defining
# functions we're going to call 'validators'. This is an example of such
@@ -415,7 +410,7 @@ class exuser_find(Method):
# patter expects them in one dict. We need to arrange that.
for e in entries:
e[1]['dn'] = e[0]
entries = [e for (_dn, e) in entries]
entries = [e for (dn, e) in entries]
return dict(result=entries, count=len(entries), truncated=truncated)
@@ -438,3 +433,5 @@ class exuser_find(Method):
# authors from doing all the dirty work. Let's take a look at them.
# COMING SOON: baseldap.py classes, extending existing plugins, etc.

View File

@@ -19,36 +19,31 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import print_function
from ipalib import api
# 1. Initialize ipalib
#
# Run ./python-api.py --help to see the global options. Some useful options:
#
# -v Produce more verbose output
# -d Produce full debugging output
# -e in_server=True Force running in server mode
# -e xmlrpc_uri=https://foo.com/ipa/xml # Connect to a specific server
def example():
# 1. Initialize ipalib
#
# Run ./python-api.py --help to see the global options. Some useful
# options:
#
# -v Produce more verbose output
# -d Produce full debugging output
# -e in_server=True Force running in server mode
# -e xmlrpc_uri=https://foo.com/ipa/xml # Connect to a specific server
api.bootstrap_with_global_options(context='example')
api.finalize()
api.bootstrap_with_global_options(context='example')
api.finalize()
# You will need to create a connection. If you're in_server, call
# Backend.ldap.connect(), otherwise Backend.rpcclient.connect().
# You will need to create a connection. If you're in_server, call
# Backend.ldap.connect(), otherwise Backend.rpcclient.connect().
if api.env.in_server:
api.Backend.ldap2.connect()
else:
api.Backend.rpcclient.connect()
# Now that you're connected, you can make calls to api.Command.whatever():
print('The admin user:')
print(api.Command.user_show(u'admin'))
if api.env.in_server:
api.Backend.ldap2.connect(
ccache=api.Backend.krb.default_ccname()
)
else:
api.Backend.rpcclient.connect()
if __name__ == '__main__':
example()
# Now that you're connected, you can make calls to api.Command.whatever():
print 'The admin user:'
print api.Command.user_show(u'admin')

View File

@@ -738,28 +738,28 @@ cli_plugins = (
show_mappings,
)
def run(api):
error = None
try:
(_options, argv) = api.bootstrap_with_global_options(context='cli')
(options, argv) = api.bootstrap_with_global_options(context='cli')
for klass in cli_plugins:
api.add_plugin(klass)
api.register(klass)
api.load_plugins()
api.finalize()
if not 'config_loaded' in api.env and not 'help' in argv:
if not 'config_loaded' in api.env:
raise NotConfiguredError()
sys.exit(api.Backend.cli.run(argv))
except KeyboardInterrupt:
print('')
logger.info('operation aborted')
except PublicError as e:
print ''
api.log.info('operation aborted')
except PublicError, e:
error = e
except Exception as e:
logger.exception('%s: %s', e.__class__.__name__, str(e))
except StandardError, e:
api.log.exception('%s: %s', e.__class__.__name__, str(e))
error = InternalError()
if error is not None:
assert isinstance(error, PublicError)
logger.error(error.strerror)
api.log.error(error.strerror)
sys.exit(error.rval)
#+END_SRC
@@ -913,39 +913,51 @@ services, authentication setup, and means to manage security context and host na
going to be extended in future to cover other areas as well, both client- and server-side.
The code that implements platform-specific adaptation is placed under
~ipaplatform~. As of FreeIPA 4.4.2, there are two major "platforms" supported:
- /rhel/ :: Red Hat Enterprise Linux 7-based distributions utilizing Systemd
such as CentOS 7 and Scientific Linux 7.
- /fedora/ :: Fedora distribution version 23 above are supported by this platform
~ipapython/platform~. As of FreeIPA 2.1.3, there are two major "platforms" supported:
- /redhat/ :: Red Hat-based distributions utilizing SystemV init scripts such as Fedora
15 and RHEL6
- /fedora16/ :: as name suggests, Fedora 16 and above, are supported by this platform
module. It is based on ~systemd~ system management tool and utilizes
common code in ~ipaplatform/base/services.py~. ~fedora~ contains
only differentiation required to cover Fedora 23-specific implementation
common code in ~ipapython/platform/systemd.py~. ~fedora16.py~ contains
only differentiation required to cover Fedora 16-specific implementation
of systemd use, depending on changes to Dogtag, Tomcat6, and 389-ds
packages.
Each platform-specific adaptation should provide few basic building blocks:
*** AuthConfig class and tasks module
*** AuthConfig class
=ipaplatform.tasks= module implements system-independent interface to configure system
resources. In Red Hat systems some of these tasks are done with authconfig(8) utility.
=AuthConfig= class implements system-independent interface to configure system
authentication resources. In Red Hat systems this is done with authconfig(8) utility.
=AuthConfig= class is nothing more than a tool to gather configuration options and execute
their processing. These options then converted by an actual implementation to series of a
system calls to appropriate utilities performing real configuration.
From FreeIPA code perspective, the system configuration should be done with
use of ~ipaplatform.tasks.tasks~:
FreeIPA *expects* names of =AuthConfig='s options to follow authconfig(8) naming
scheme. From FreeIPA code perspective, the authentication configuration should be done with
use of ~ipapython.services.authconfig~:
#+BEGIN_SRC python -n
from ipaplatform.tasks import tasks
from ipapython import services as ipaservices
tasks.set_nisdomain('nisdomain.example')
auth_config = ipaservices.authconfig()
auth_config.disable("ldap").\
disable("krb5").\
disable("sssd").\
disable("sssdauth").\
disable("mkhomedir").\
add_option("update").\
enable("nis").\
add_parameter("nisdomain","foobar")
auth_config.execute()
#+END_SRC
The actual implementation can differ. ~redhat~ platform module builds up arguments to
authconfig(8) tool and on =execute()= method runs it with those arguments. Other systems
will need to have processing based on their respective tools.
will need to have processing of the arguments done as defined by authconfig(8) manual
page. This is, perhaps, biggest obstacle on porting FreeIPA client side to the new
platform.
*** PlatformService class
=PlatformService= class abstracts out an external process running on the system which is
@@ -954,44 +966,95 @@ etc.
Services are used thoroughly through FreeIPA server and client install tools. There are
several services that are used especially often and they are selected to be accessible via
Python properties of =ipaplatform.services.knownservices= instance.
Python properties of =ipapython.services.knownservices= instance.
To facilitate more expressive way of working with often used services, ipaplatform.services
To facilitate more expressive way of working with often used services, ipapython.services
module provides a shortcut to access them by name via
ipaplatform.services.knownservices.<service>. A typical code change looks like this:
ipapython.services.knownservices.<service>. A typical code change looks like this:
#+BEGIN_EXAMPLE
import ipaplatform.services.knownservices
from ipapython import services as ipaservices
....
- service.restart("dirsrv")
- service.restart("krb5kdc")
- service.restart("httpd")
+ ipaplatform.services.knownservices.dirsrv.restart()
+ ipaplatform.services.knownservices.krb5kdc.restart()
+ ipaplatform.services.knownservices.httpd.restart()
+ ipaservices.knownservices.dirsrv.restart()
+ ipaservices.knownservices.krb5kdc.restart()
+ ipaservices.knownservices.httpd.restart()
#+END_EXAMPLE
Besides expression change this also makes more explicit to platform providers access to
what services they have to implement. Service names are defined in
ipaplatform.platform.base.wellknownservices and represent definitive names to access these
ipapython.platform.base.wellknownservices and represent definitive names to access these
services from FreeIPA code. Of course, platform provider should remap those names to
platform-specific ones -- for ipaplatform.redhat provider mapping is identity.
platform-specific ones -- for ipapython.platform.redhat provider mapping is identity.
Porting to a new platform may be hard as can be witnessed by this example:
https://www.redhat.com/archives/freeipa-devel/2011-September/msg00408.html
If there is doubt, always consult existing providers. ~redhat/services.py~ is canonical -- it
If there is doubt, always consult existing providers. ~redhat.py~ is canonical -- it
represents the code which was used throughout FreeIPA v2 development.
*** Enabling new platform provider
When support for new platform is implemented and appropriate provider is placed to
~ipaplatform/platform/~, it is time to enable its use by the FreeIPA. Since FreeIPA is
~ipapython/platform/~, it is time to enable its use by the FreeIPA. Since FreeIPA is
supposed to be rolled out uniformly on multiple clients and servers, best approach is to
build and distribute software packages using platform-provided package management tools.
With this in mind, platform code selection in FreeIPA is static and run at package
production time. In order to select proper platform provider, one needs to pass
~--with-ipaplatform~ argument to FreeIPA's configure process:
~SUPPORTED_PLATFORM~ argument to FreeIPA's make process:
#+BEGIN_EXAMPLE
./configure --with-ipaplatform=fedora
export SUPPORTED_PLATFORM=fedora16
# Force re-generate of platform support
rm -f ipapython/services.py
make version-update
make IPA_VERSION_IS_GIT_SNAPSHOT=no all
#+END_EXAMPLE
~version-update~ target in FreeIPA top-level Makefile will re-create ipapython/services.py
file based on the value of ~SUPPORTED_PLATFORM~ variable. By default this variable is set
to ~redhat~.
~ipapython/services.py~ is generated using ~ipapython/service.py.in~. In fact, there is
only single line gets replaced in the latter file at the last line:
#+BEGIN_SRC python
# authconfig is an entry point to platform-provided AuthConfig implementation
# (instance of ipapython.platform.base.AuthConfig)
authconfig = None
# knownservices is an entry point to known platform services
# (instance of ipapython.platform.base.KnownServices)
knownservices = None
# service is a class to instantiate ipapython.platform.base.PlatformService
service = None
# restore context default implementation that does nothing
def restore_context_default(filepath):
return
# Restore security context for a path
# If the platform has security features where context is important, implement your own
# version in platform services
restore_context = restore_context_default
# Default implementation of backup and replace hostname that does nothing
def backup_and_replace_hostname_default(fstore, statestore, hostname):
return
# Backup and replace system's hostname
# Since many platforms have their own way how to store system's hostname, this method must be
# implemented in platform services
backup_and_replace_hostname = backup_and_replace_hostname_default
from ipapython.platform.SUPPORTED_PLATFORM import *
#+END_SRC
As last statement imports everything from the supported platform provider, all exposed
methods and variables above will be re-defined to platform-specific implementations. This
allows to have FreeIPA framework use of these services separated from the implementation
of the platform.
The code in ipapython/services.py is going to grow over time when more parts of FreeIPA
framework become platform-independent.

View File

@@ -1,23 +1,26 @@
import logging
import os
from ipaplatform.paths import paths
from ipalib import api
from ipalib.config import Env
from ipalib.constants import DEFAULT_CONFIG
logger = logging.getLogger(os.path.basename(__file__))
# Determine what debug level is configured. We can only do this
# by reading in the configuration file(s). The server always reads
# default.conf and will also read in `context'.conf.
env = Env()
env._bootstrap(context='server', log=None)
env._finalize_core(**dict(DEFAULT_CONFIG))
api.bootstrap(context='server', confdir=paths.ETC_IPA, log=None) (ref:wsgi-app-bootstrap)
# Initialize the API with the proper debug level
api.bootstrap(context='server', debug=env.debug, log=None) (ref:wsgi-app-bootstrap)
try:
api.finalize() (ref:wsgi-app-finalize)
except Exception as e:
logger.error('Failed to start IPA: %s', e)
except StandardError, e:
api.log.error('Failed to start IPA: %s' % e)
else:
logger.info('*** PROCESS START ***')
api.log.info('*** PROCESS START ***')
# This is the WSGI callable:
def application(environ, start_response): (ref:wsgi-app-start)
if not environ['wsgi.multithread']:
return api.Backend.session(environ, start_response)
else:
logger.error("IPA does not work with the threaded MPM, "
"use the pre-fork MPM") (ref:wsgi-app-end)
api.log.error("IPA does not work with the threaded MPM, use the pre-fork MPM") (ref:wsgi-app-end)