As developer , I want a simple Python context manager to initialize and connect ipalib.api to an IPA server so that I don't have to learn implementation details of IPA's API plugin framework.
ipalib.api
For now, users must call several ipalib.api methods in the right order (bootstrap, finalize, connect on the right backend, disconnect on the right backend) before they can execute a command. Some methods (bootstrap, finalize) must be called exactly once. api.Backend has different backend object depending on api.env.in_server property.
api.Backend
api.env.in_server
API should be as simple as:
from ipalib import api if not api.isdone("bootstrap"): api.bootstrap(custom=settings) with api: api.Commands.ping()
Add context manager protocol to api.
api
The __enter__ method should - support automatic TGT acquisition, see #9442 - finalize the api if it hasn't been finalized before: if not api.isdone("finalize"): api.finalize() - allow users to call bootstrap or finalize before api.__enter__, so users can customize the API- automatically figure out if the api usesapi.Backend.ldap2orapi.Backend.rcpclientbased onapi.env.in_server` setting - automatically connect the backend if it is not connected - if the backend is connected, we may want to raise an exception to prevent nesting enter/exit. - return self
__enter__
if not api.isdone("finalize"): api.finalize()
bootstrap
finalize
api.__enter__
- automatically figure out if the api uses
or
based on
The __exit__ method should - disconnect the backend if it is still connected (it may have been disconnected already due to an internal error)
__exit__
Metadata Update from @cheimes: - Custom field rhbz adjusted to https://bugzilla.redhat.com/show_bug.cgi?id=1513934
Here is a working implementation for ipalib.API class.
ipalib.API
def __enter__(self): # Several IPA module require api.env at import time, some even # a fully finalized ipalib.ap, e.g. register() with MethodOverride. if self is not ipalib.api and not ipalib.api.isdone("finalize"): raise RuntimeError("global ipalib.api must be finalized first.") # initialize this api (might be global) if not self.isdone("finalize"): self.finalize() # connect backend, server and client use different backends. if self.env.in_server: conn = self.Backend.ldap2 else: conn = self.Backend.rpcclient if conn.isconnected(): raise RuntimeError("API is already connected") else: conn.connect() return self def __exit__(self, exc_type, exc_val, exc_tb): if self.env.in_server: conn = self.Backend.ldap2 else: conn = self.Backend.rpcclient if conn.isconnected(): conn.disconnect()
This looks nice, thank you. Please submit a PR with a simple test added, if that is not too much.
One note I would leave definitely in the documentation is that this implementation of context manager assumes connection and disconnection will happen around each use of the context manager. It might be something to consider performance-wise for a specific use case.
Metadata Update from @cheimes: - Custom field on_review adjusted to https://github.com/freeipa/freeipa/pull/7003
master:
ipa-4-11:
Metadata Update from @rcritten: - Issue close_status updated to: fixed - Issue status updated to: Closed (was: Open)