URL: https://github.com/freeipa/freeipa/pull/790 Author: martbab Title: #790: RFC: API for reporting PKINIT status Action: opened
PR body: """ This PR implements easily-consumable API that reports PKINIT status on masters based on the presence of pkinitEnabled value in KDC entry's ipaConfigString attribute. https://pagure.io/freeipa/issue/6937 """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/790/head:pr790 git checkout pr790
From efc1f9d17c208b5d8b3ff99ed73e165ae7ca05dd Mon Sep 17 00:00:00 2001 From: Martin Babinsky <mbabi...@redhat.com> Date: Thu, 11 May 2017 15:55:53 +0200 Subject: [PATCH 1/4] Allow for multivalued server attributes In order to achieve the task, the following changes were required: * vectorize the base class for server attributes * add a child class that enforces single-value attributes. It still accepts/returns single-value lists in order to not break Liskov substitution principle * Existing attributes inherit from the child class https://pagure.io/freeipa/issue/6937 --- ipaserver/plugins/serverroles.py | 4 +- ipaserver/servroles.py | 109 +++++++++++++++++++--------- ipatests/test_ipaserver/test_serverroles.py | 10 +-- 3 files changed, 79 insertions(+), 44 deletions(-) diff --git a/ipaserver/plugins/serverroles.py b/ipaserver/plugins/serverroles.py index e22eadd..e81635c 100644 --- a/ipaserver/plugins/serverroles.py +++ b/ipaserver/plugins/serverroles.py @@ -136,9 +136,7 @@ def config_retrieve(self, servrole): for name, attr in assoc_attributes.items(): attr_value = attr.get(self.api) - - if attr_value is not None: - result.update({name: attr_value}) + result.update({name: attr_value}) return result diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py index cf45999..84fed10 100644 --- a/ipaserver/servroles.py +++ b/ipaserver/servroles.py @@ -277,29 +277,33 @@ def get(self, api_instance): try: entries = ldap2.get_entries(search_base, filter=search_filter) except errors.EmptyResult: - return + return [] - master_cn = entries[0].dn[1]['cn'] + master_cns = {e.dn[1]['cn'] for e in entries} associated_role_providers = set( self._get_assoc_role_providers(api_instance)) - if master_cn not in associated_role_providers: + if not master_cns.issubset(associated_role_providers): raise errors.ValidationError( name=self.name, error=_("all masters must have %(role)s role enabled" % {'role': self.associated_role.name}) ) - return master_cn + return sorted(master_cns) - def _get_master_dn(self, api_instance, server): - return DN(('cn', server), api_instance.env.container_masters, - api_instance.env.basedn) + def _get_master_dns(self, api_instance, servers): + return [ + DN(('cn', server), api_instance.env.container_masters, + api_instance.env.basedn) for server in servers] + + def _get_masters_service_entries(self, ldap, master_dns): + service_dns = [ + DN(('cn', self.associated_service_name), master_dn) for master_dn + in master_dns] - def _get_masters_service_entry(self, ldap, master_dn): - service_dn = DN(('cn', self.associated_service_name), master_dn) - return ldap.get_entry(service_dn) + return [ldap.get_entry(service_dn) for service_dn in service_dns] def _add_attribute_to_svc_entry(self, ldap, service_entry): """ @@ -341,65 +345,98 @@ def _get_assoc_role_providers(self, api_instance): r[u'server_server'] for r in self.associated_role.status( api_instance) if r[u'status'] == ENABLED] - def _remove(self, api_instance, master): + def _remove(self, api_instance, masters): """ - remove attribute from the master + remove attribute from one or more masters :param api_instance: API instance - :param master: master FQDN + :param master: list or iterable containing master FQDNs """ ldap = api_instance.Backend.ldap2 - master_dn = self._get_master_dn(api_instance, master) - service_entry = self._get_masters_service_entry(ldap, master_dn) - self._remove_attribute_from_svc_entry(ldap, service_entry) + master_dns = self._get_master_dns(api_instance, masters) + service_entries = self._get_masters_service_entries(ldap, master_dns) + + for service_entry in service_entries: + self._remove_attribute_from_svc_entry(ldap, service_entry) - def _add(self, api_instance, master): + def _add(self, api_instance, masters): """ add attribute to the master :param api_instance: API instance - :param master: master FQDN + :param master: iterable containing master FQDNs :raises: * errors.ValidationError if the associated role is not enabled on the master """ - assoc_role_providers = self._get_assoc_role_providers(api_instance) + assoc_role_providers = set( + self._get_assoc_role_providers(api_instance)) + masters_set = set(masters) ldap = api_instance.Backend.ldap2 - if master not in assoc_role_providers: + masters_without_role = masters_set - assoc_role_providers + + if masters_without_role: raise errors.ValidationError( - name=master, + name=', '.join(sorted(masters_without_role)), error=_("must have %(role)s role enabled" % {'role': self.associated_role.name}) ) - master_dn = self._get_master_dn(api_instance, master) - service_entry = self._get_masters_service_entry(ldap, master_dn) - self._add_attribute_to_svc_entry(ldap, service_entry) + master_dns = self._get_master_dns(api_instance, masters) + service_entries = self._get_masters_service_entries(ldap, master_dns) + for service_entry in service_entries: + self._add_attribute_to_svc_entry(ldap, service_entry) - def set(self, api_instance, master): + def set(self, api_instance, masters): """ - set the attribute on master + set the attribute on masters :param api_instance: API instance - :param master: FQDN of the new master + :param masters: an interable with FQDNs of the new masters - the attribute is automatically unset from previous master if present + the attribute is automatically unset from previous masters if present :raises: errors.EmptyModlist if the new masters is the same as - the original on + the original ones """ - old_master = self.get(api_instance) + old_masters = self.get(api_instance) - if old_master == master: + if sorted(old_masters) == sorted(masters): raise errors.EmptyModlist - self._add(api_instance, master) + if old_masters: + self._remove(api_instance, old_masters) + + self._add(api_instance, masters) + + +class SingleValuedServerAttribute(ServerAttribute): + """ + Base class for server attributes that are forced to be single valued + + this means that `get` method will return a one-element list, and `set` + method will accept only one-element list + """ + + def set(self, api_instance, masters): + if len(masters) > 1: + raise errors.ValidationError( + name=self.attr_name, + error=_("must be enabled only on a single master")) + + super(SingleValuedServerAttribute, self).set(api_instance, masters) + + def get(self, api_instance): + masters = super(SingleValuedServerAttribute, self).get(api_instance) + num_masters = len(masters) + + if num_masters > 1: + raise errors.SingleMatchExpected(found=num_masters) - if old_master is not None: - self._remove(api_instance, old_master) + return masters _Service = namedtuple('Service', ['name', 'enabled']) @@ -574,14 +611,14 @@ def create_search_params(self, ldap, api_instance, server=None): ) attribute_instances = ( - ServerAttribute( + SingleValuedServerAttribute( u"ca_renewal_master_server", u"CA renewal master", u"ca_server_server", u"CA", u"caRenewalMaster", ), - ServerAttribute( + SingleValuedServerAttribute( u"dnssec_key_master_server", u"DNSSec key master", u"dns_server_server", diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py index d8844df..e671272 100644 --- a/ipatests/test_ipaserver/test_serverroles.py +++ b/ipatests/test_ipaserver/test_serverroles.py @@ -706,7 +706,7 @@ def test_attribute_master(self, mock_api, mock_masters, actual_attr_masters = self.config_retrieve( assoc_role, mock_api)[attr_name] - assert actual_attr_masters == fqdn + assert actual_attr_masters == [fqdn] def test_set_attribute_on_the_same_provider_raises_emptymodlist( self, mock_api, mock_masters): @@ -727,7 +727,7 @@ def test_set_attribute_on_master_without_assoc_role_raises_validationerror( non_ca_fqdn = mock_masters.get_fqdn('trust-controller-dns') with pytest.raises(errors.ValidationError): - self.config_update(mock_api, **{attr_name: non_ca_fqdn}) + self.config_update(mock_api, **{attr_name: [non_ca_fqdn]}) def test_set_unknown_attribute_on_master_raises_notfound( self, mock_api, mock_masters): @@ -735,7 +735,7 @@ def test_set_unknown_attribute_on_master_raises_notfound( fqdn = mock_masters.get_fqdn('trust-controller-ca') with pytest.raises(errors.NotFound): - self.config_update(mock_api, **{attr_name: fqdn}) + self.config_update(mock_api, **{attr_name: [fqdn]}) def test_set_ca_renewal_master_on_other_ca_and_back(self, mock_api, mock_masters): @@ -747,7 +747,7 @@ def test_set_ca_renewal_master_on_other_ca_and_back(self, mock_api, other_ca_server = mock_masters.get_fqdn('trust-controller-ca') for host in (other_ca_server, original_renewal_master): - self.config_update(mock_api, **{attr_name: host}) + self.config_update(mock_api, **{attr_name: [host]}) assert ( - self.config_retrieve(role_name, mock_api)[attr_name] == host) + self.config_retrieve(role_name, mock_api)[attr_name] == [host]) From e87ae9f95197df963203017e958ee38a4810b977 Mon Sep 17 00:00:00 2001 From: Martin Babinsky <mbabi...@redhat.com> Date: Fri, 12 May 2017 15:15:37 +0200 Subject: [PATCH 2/4] Add an attribute reporting client PKINIT-capable servers A new multi-valued server attribute `pkinit_server` was added which reports IPA masters that have PKINIT configuration usable by clients. The existing tests were modified to allow for testing the new attribute. https://pagure.io/freeipa/issue/6937 --- ipaserver/servroles.py | 7 ++ ipatests/test_ipaserver/test_serverroles.py | 103 +++++++++++++--------------- 2 files changed, 53 insertions(+), 57 deletions(-) diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py index 84fed10..f6e7933 100644 --- a/ipaserver/servroles.py +++ b/ipaserver/servroles.py @@ -625,4 +625,11 @@ def create_search_params(self, ldap, api_instance, server=None): u"DNSSEC", u"dnssecKeyMaster", ), + ServerAttribute( + u"pkinit_server_server", + u"PKINIT enabled server", + u"ipa_master_server", + u"KDC", + u"pkinitEnabled" + ) ) diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py index e671272..fd67afa 100644 --- a/ipatests/test_ipaserver/test_serverroles.py +++ b/ipatests/test_ipaserver/test_serverroles.py @@ -7,6 +7,7 @@ """ from collections import namedtuple +from copy import copy import ldap import pytest @@ -58,7 +59,7 @@ def _make_master_entry_mods(ca=False): master_data = { - 'ca-dns-dnssec-keymaster': { + 'ca-dns-dnssec-keymaster-pkinit-server': { 'services': { 'CA': { 'enabled': True, @@ -72,14 +73,19 @@ def _make_master_entry_mods(ca=False): 'DNSSEC': { 'enabled': True, 'config': ['DNSSecKeyMaster'] + }, + 'KDC': { + 'enabled': True, + 'config': ['pkinitEnabled'] } }, 'expected_roles': { 'enabled': ['IPA master', 'CA server', 'DNS server'] }, - 'expected_attributes': {'DNS server': 'dnssec_key_master_server'} + 'expected_attributes': {'DNS server': 'dnssec_key_master_server', + 'IPA master': 'pkinit_server_server'} }, - 'ca-kra-renewal-master': { + 'ca-kra-renewal-master-pkinit-server': { 'services': { 'CA': { 'enabled': True, @@ -88,11 +94,16 @@ def _make_master_entry_mods(ca=False): 'KRA': { 'enabled': True, }, + 'KDC': { + 'enabled': True, + 'config': ['pkinitEnabled'] + }, }, 'expected_roles': { 'enabled': ['IPA master', 'CA server', 'KRA server'] }, - 'expected_attributes': {'CA server': 'ca_renewal_master_server'} + 'expected_attributes': {'CA server': 'ca_renewal_master_server', + 'IPA master': 'pkinit_server_server'} }, 'dns-trust-agent': { 'services': { @@ -234,7 +245,7 @@ def __init__(self, api_instance, domain_data): no_members=True, raw=True)['result']} - self.existing_attributes = self._check_test_host_attributes() + self.original_entries = self._remove_test_host_attrs() def iter_domain_data(self): MasterData = namedtuple('MasterData', @@ -287,7 +298,6 @@ def _del_service_entry(self, service, fqdn): pass def _add_svc_entries(self, master_dn, svc_desc): - self._add_ipamaster_services(master_dn) for name in svc_desc: svc_dn = self.get_service_dn(name, master_dn) svc_mods = svc_desc[name] @@ -298,6 +308,8 @@ def _add_svc_entries(self, master_dn, svc_desc): enabled=svc_mods['enabled'], other_config=svc_mods.get('config', None))) + self._add_ipamaster_services(master_dn) + def _remove_svc_master_entries(self, master_dn): try: entries = self.ldap.connection.search_s( @@ -317,7 +329,11 @@ def _add_ipamaster_services(self, master_dn): """ for svc_name in self.ipamaster_services: svc_dn = self.get_service_dn(svc_name, master_dn) - self.ldap.add_entry(str(svc_dn), _make_service_entry_mods()) + try: + self.api.Backend.ldap2.get_entry(svc_dn) + except errors.NotFound: + self.ldap.add_entry( + str(svc_dn), _make_service_entry_mods()) def _add_members(self, dn, fqdn, member_attrs): _entry, attrs = self.ldap.connection.search_s( @@ -376,57 +392,30 @@ def _remove_members(self, dn, fqdn, member_attrs): except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE): pass - def _check_test_host_attributes(self): - existing_attributes = set() + def _remove_test_host_attrs(self): + original_entries = [] - for service, value, attr_name in ( - ('CA', 'caRenewalMaster', 'ca renewal master'), - ('DNSSEC', 'DNSSecKeyMaster', 'dnssec key master')): - - svc_dn = DN(('cn', service), self.test_master_dn) + for attr_name in ( + 'caRenewalMaster', 'dnssecKeyMaster', 'pkinitEnabled'): try: - svc_entry = self.api.Backend.ldap2.get_entry(svc_dn) + svc_entry = self.api.Backend.ldap2.find_entry_by_attr( + 'ipaConfigString', attr_name, 'ipaConfigObject', + base_dn=self.test_master_dn) except errors.NotFound: continue else: - config_string_val = svc_entry.get('ipaConfigString', []) - - if value in config_string_val: - existing_attributes.add(attr_name) - - return existing_attributes - - def _remove_ca_renewal_master(self): - if 'ca renewal master' not in self.existing_attributes: - return - - ca_dn = DN(('cn', 'CA'), self.test_master_dn) - ca_entry = self.api.Backend.ldap2.get_entry(ca_dn) - - config_string_val = ca_entry.get('ipaConfigString', []) - try: - config_string_val.remove('caRenewalMaster') - except KeyError: - return - - ca_entry.update({'ipaConfigString': config_string_val}) - self.api.Backend.ldap2.update_entry(ca_entry) - - def _restore_ca_renewal_master(self): - if 'ca renewal master' not in self.existing_attributes: - return - - ca_dn = DN(('cn', 'CA'), self.test_master_dn) - ca_entry = self.api.Backend.ldap2.get_entry(ca_dn) + original_entries.append(svc_entry) + new_entry = copy(svc_entry) + new_entry[u'ipaConfigString'].remove(attr_name) + self.api.Backend.ldap2.update_entry(new_entry) - config_string_val = ca_entry.get('ipaConfigString', []) - config_string_val.append('caRenewalMaster') + return original_entries - ca_entry.update({'ipaConfigString': config_string_val}) - self.api.Backend.ldap2.update_entry(ca_entry) + def _restore_test_host_attrs(self): + for entry in self.original_entries: + self.api.Backend.ldap2.update_entry(entry) def setup_data(self): - self._remove_ca_renewal_master() for master_data in self.iter_domain_data(): # create host self._add_host_entry(master_data.fqdn) @@ -449,7 +438,7 @@ def setup_data(self): ) def teardown_data(self): - self._restore_ca_renewal_master() + self._restore_test_host_attrs() for master_data in self.iter_domain_data(): # first remove the master entries and service containers self._remove_svc_master_entries(master_data.dn) @@ -665,14 +654,14 @@ def test_provided_roles_on_master( def test_unknown_role_status_raises_notfound(self, mock_api, mock_masters): unknown_role = 'IAP maestr' - fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster') + fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster-pkinit-server') with pytest.raises(errors.NotFound): mock_api.Backend.serverroles.server_role_retrieve( fqdn, unknown_role) def test_no_servrole_queries_all_roles_on_server(self, mock_api, mock_masters): - master_name = 'ca-dns-dnssec-keymaster' + master_name = 'ca-dns-dnssec-keymaster-pkinit-server' enabled_roles = master_data[master_name]['expected_roles']['enabled'] result = self.find_role(None, mock_api, mock_masters, master=master_name) @@ -688,7 +677,7 @@ def test_invalid_substring_search_returns_nothing(self, mock_api, invalid_substr = 'fwfgbb' assert (not self.find_role(invalid_substr, mock_api, mock_masters, - 'ca-dns-dnssec-keymaster')) + 'ca-dns-dnssec-keymaster-pkinit-server')) class TestServerAttributes(object): @@ -706,7 +695,7 @@ def test_attribute_master(self, mock_api, mock_masters, actual_attr_masters = self.config_retrieve( assoc_role, mock_api)[attr_name] - assert actual_attr_masters == [fqdn] + assert fqdn in actual_attr_masters def test_set_attribute_on_the_same_provider_raises_emptymodlist( self, mock_api, mock_masters): @@ -744,10 +733,10 @@ def test_set_ca_renewal_master_on_other_ca_and_back(self, mock_api, original_renewal_master = self.config_retrieve( role_name, mock_api)[attr_name] - other_ca_server = mock_masters.get_fqdn('trust-controller-ca') + other_ca_server = [mock_masters.get_fqdn('trust-controller-ca')] for host in (other_ca_server, original_renewal_master): - self.config_update(mock_api, **{attr_name: [host]}) + self.config_update(mock_api, **{attr_name: host}) assert ( - self.config_retrieve(role_name, mock_api)[attr_name] == [host]) + self.config_retrieve(role_name, mock_api)[attr_name] == host) From 4a0d43726ba0043c474f4dcba623239f72549aae Mon Sep 17 00:00:00 2001 From: Martin Babinsky <mbabi...@redhat.com> Date: Fri, 12 May 2017 15:27:36 +0200 Subject: [PATCH 3/4] Add the list of PKINIT servers as a virtual attribute to global config https://pagure.io/freeipa/issue/6937 --- ipaserver/plugins/config.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py index b50e7a4..77a3f8a 100644 --- a/ipaserver/plugins/config.py +++ b/ipaserver/plugins/config.py @@ -256,6 +256,12 @@ class config(LDAPObject): flags={'virtual_attribute', 'no_create'} ), Str( + 'pkinit_server_server*', + label=_('IPA master capable of PKINIT'), + doc=_('IPA master which can process PKINIT requests'), + flags={'virtual_attribute', 'no_create', 'no_update'} + ), + Str( 'ipadomainresolutionorder?', cli_name='domain_resolution_order', label=_('Domain resolution order'), From 81e20180597121a5928f924e9dda16d197e93ea9 Mon Sep 17 00:00:00 2001 From: Martin Babinsky <mbabi...@redhat.com> Date: Fri, 12 May 2017 17:25:30 +0200 Subject: [PATCH 4/4] Add `pkinit-status` command This command is a more streamlined reporting tool for PKINIT feature status in the FreeIPA topology. It prints out whether PKINIT is enabled or disabled on individual masters in a topology. If a`--server` is specified, it reports status for an individual server. If `--status` is specified, it searches for all servers that have PKINIT enabled or disabled. https://pagure.io/freeipa/issue/6937 --- API.txt | 15 ++++++++ VERSION.m4 | 4 +- ipaserver/plugins/pkinit.py | 90 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/API.txt b/API.txt index fa7582d..da7a73b 100644 --- a/API.txt +++ b/API.txt @@ -3741,6 +3741,20 @@ args: 1,1,1 arg: Str('action') option: Str('version?') output: Output('result') +command: pkinit_status/1 +args: 1,7,4 +arg: Str('criteria?') +option: Flag('all', autofill=True, cli_name='all', default=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False) +option: Str('server_server?', autofill=False, cli_name='server') +option: Int('sizelimit?', autofill=False) +option: StrEnum('status?', autofill=False, cli_name='status', values=[u'enabled', u'disabled']) +option: Int('timelimit?', autofill=False) +option: Str('version?') +output: Output('count', type=[<type 'int'>]) +output: ListOfEntries('result') +output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>]) +output: Output('truncated', type=[<type 'bool'>]) command: plugins/1 args: 0,3,3 option: Flag('all', autofill=True, cli_name='all', default=True) @@ -6810,6 +6824,7 @@ default: permission_show/1 default: ping/1 default: pkinit/1 default: pkinit_anonymous/1 +default: pkinit_status/1 default: plugins/1 default: privilege/1 default: privilege_add/1 diff --git a/VERSION.m4 b/VERSION.m4 index 6ec56c5..a130edd 100644 --- a/VERSION.m4 +++ b/VERSION.m4 @@ -73,8 +73,8 @@ define(IPA_DATA_VERSION, 20100614120000) # # ######################################################## define(IPA_API_VERSION_MAJOR, 2) -define(IPA_API_VERSION_MINOR, 225) -# Last change: Add --password-expiration option to force password change +define(IPA_API_VERSION_MINOR, 226) +# Last change: Add `pkinit-status` command ######################################################## diff --git a/ipaserver/plugins/pkinit.py b/ipaserver/plugins/pkinit.py index b6b3f38..a75020d 100644 --- a/ipaserver/plugins/pkinit.py +++ b/ipaserver/plugins/pkinit.py @@ -18,9 +18,10 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. from ipalib import api, errors -from ipalib import Str +from ipalib import Int, Str, StrEnum from ipalib import Object, Command -from ipalib import _ +from ipalib import _, ngettext +from ipalib.crud import Search from ipalib.plugable import Registry from ipalib.constants import ANON_USER from ipapython.dn import DN @@ -56,6 +57,23 @@ class pkinit(Object): label=_('PKINIT') + takes_params = ( + Str( + 'server_server?', + cli_name='server', + label=_('Server name'), + doc=_('IPA server hostname'), + ), + StrEnum( + 'status?', + cli_name='status', + label=_('PKINIT status'), + doc=_('Whether PKINIT is enabled or disabled'), + values=(u'enabled', u'disabled'), + flags={'virtual_attribute', 'no_create', 'no_update'} + ) + ) + def valid_arg(ugettext, action): """ @@ -103,3 +121,71 @@ def execute(self, action, **options): ldap.update_entry(entry_attrs) return dict(result=True) + + +@register() +class pkinit_status(Search): + __doc__ = _('Report PKINIT status on the IPA masters') + + msg_summary = ngettext('%(count)s server matched', + '%(count)s servers matched', 0) + + takes_options = Search.takes_options + ( + Int( + 'timelimit?', + label=_('Time Limit'), + doc=_('Time limit of search in seconds (0 is unlimited)'), + flags=['no_display'], + minvalue=0, + autofill=False, + ), + Int( + 'sizelimit?', + label=_('Size Limit'), + doc=_('Maximum number of entries returned (0 is unlimited)'), + flags=['no_display'], + minvalue=0, + autofill=False, + ), + ) + + def get_pkinit_status(self, server, status): + backend = self.api.Backend.serverroles + ipa_master_config = backend.config_retrieve("IPA master") + + if server is not None: + servers = [server] + else: + servers = ipa_master_config['ipa_master_server'] + + pkinit_servers = ipa_master_config['pkinit_server_server'] + + for s in servers: + pkinit_status = { + u'server_server': s, + u'status': ( + u'enabled' if s in pkinit_servers else u'disabled' + ) + } + if status is not None and pkinit_status[u'status'] != status: + continue + + yield pkinit_status + + def execute(self, *keys, **options): + if keys: + return dict( + result=[], + count=0, + truncated=False + ) + + server = options.get('server_server', None) + status = options.get('status', None) + + if server is not None: + self.api.Object.server_role.ensure_master_exists(server) + + result = sorted(self.get_pkinit_status(server, status)) + + return dict(result=result, count=len(result), truncated=False)
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code