Hi,

these problems have been fixed.

F.

----- Original Message -----
> NACK.
> Some little changes still required:
>  * fixing the pep8 errors
>  * fixing the wrong comment
> 
> [root@master2 freeipa]# pep8 ipatests/test_xmlrpc/test_sudocmd_plugin.py
> ipatests/test_xmlrpc/test_sudocmd_plugin.py:94:80: E501 line too long (87 >
> 79 characters)
> ipatests/test_xmlrpc/test_sudocmd_plugin.py:97:80: E501 line too long (87 >
> 79 characters)
> ipatests/test_xmlrpc/test_sudocmd_plugin.py:134:80: E501 line too long (80 >
> 79 characters)
>       
> [root@master2 freeipa]# pep8 ipatests/test_xmlrpc/tracker/sudocmd_plugin.py
> ipatests/test_xmlrpc/tracker/sudocmd_plugin.py:14:80: E501 line too long (81
> > 79 characters)
> 
> [root@master2 freeipa]# grep 'Class for'
> ipatests/test_xmlrpc/tracker/sudocmd_plugin.py
>     """ Class for host plugin like tests """
> 
> 
> ----- Original Message -----
> > From: "Filip Skola" <fsk...@redhat.com>
> > To: "Aleš Mareček" <amare...@redhat.com>
> > Cc: freeipa-devel@redhat.com, "Milan Kubík" <mku...@redhat.com>
> > Sent: Monday, February 22, 2016 1:59:43 PM
> > Subject: Re: [Freeipa-devel] [PATCH] 0007 Refactor test_sudocmd_plugin
> > 
> > Hi,
> > 
> > sudocmd tracker has been created.
> > 
> > Filip
> > 
> > ----- Original Message -----
> > > NACK.
> > > 
> > > "create_sudocmd" and "delete_sudocmd" should be placed in Tracker. So
> > > this
> > > patch should create the tracker as well.
> > > 
> > > ----- Original Message -----
> > > > From: "Filip Skola" <fsk...@redhat.com>
> > > > To: freeipa-devel@redhat.com
> > > > Sent: Monday, January 25, 2016 3:57:25 PM
> > > > Subject: [Freeipa-devel] [PATCH] 0007 Refactor test_sudocmd_plugin
> > > > 
> > > > Hello,
> > > > 
> > > > attaching refactored sudocmd_plugin.
> > > > 
> > > > Filip
> > > > --
> > > > 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
> > > 
> > 
> 
From 1da1bdc46eb96b966e060b0ad83a3edebcc9c37b Mon Sep 17 00:00:00 2001
From: Filip Skola <fsk...@redhat.com>
Date: Mon, 18 Jan 2016 13:56:44 +0100
Subject: [PATCH] Refactor test_sudocmd_plugin

---
 ipatests/test_xmlrpc/test_sudocmd_plugin.py    | 448 +++++++++----------------
 ipatests/test_xmlrpc/tracker/sudocmd_plugin.py | 113 +++++++
 2 files changed, 269 insertions(+), 292 deletions(-)
 create mode 100644 ipatests/test_xmlrpc/tracker/sudocmd_plugin.py

diff --git a/ipatests/test_xmlrpc/test_sudocmd_plugin.py b/ipatests/test_xmlrpc/test_sudocmd_plugin.py
index 2056118ba763be45e78ddf6643059e32d7680af8..7ffe7a1d0497ad9c755ccdbe057fee1a13523f20 100644
--- a/ipatests/test_xmlrpc/test_sudocmd_plugin.py
+++ b/ipatests/test_xmlrpc/test_sudocmd_plugin.py
@@ -21,309 +21,173 @@
 Test the `ipalib/plugins/sudocmd.py` module.
 """
 
-from ipalib import errors
-from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, fuzzy_sudocmddn,
-    fuzzy_uuid)
-from ipatests.test_xmlrpc import objectclasses
+from ipalib import api, errors
+from ipatests.util import assert_deepequal
+from ipatests.test_xmlrpc.xmlrpc_test import (XMLRPC_test, raises_exact)
+from ipatests.test_xmlrpc.tracker.sudocmd_plugin import SudoCmdTracker
 import pytest
 
-sudocmd1 = u'/usr/bin/sudotestcmd1'
-sudocmd1_camelcase = u'/usr/bin/sudoTestCmd1'
 
-sudorule1 = u'test_sudorule1'
+@pytest.fixture(scope='class')
+def sudocmd1(request):
+    tracker = SudoCmdTracker(command=u'/usr/bin/sudotestcmd1',
+                             description=u'Test sudo command 1')
+    return tracker.make_fixture(request)
 
 
-@pytest.mark.tier1
-class test_sudocmd(Declarative):
-
-    cleanup_commands = [
-        ('sudocmd_del', [sudocmd1], {}),
-        ('sudocmd_del', [sudocmd1_camelcase], {}),
-        ('sudorule_del', [sudorule1], {}),
-    ]
-
-    tests = [
-
-        dict(
-            desc='Try to retrieve non-existent %r' % sudocmd1,
-            command=('sudocmd_show', [sudocmd1], {}),
-            expected=errors.NotFound(
-                reason=u'%s: sudo command not found' % sudocmd1),
-        ),
-
-
-        dict(
-            desc='Try to update non-existent %r' % sudocmd1,
-            command=('sudocmd_mod', [sudocmd1], dict(description=u'Nope')),
-            expected=errors.NotFound(
-                reason=u'%s: sudo command not found' % sudocmd1),
-        ),
-
-
-        dict(
-            desc='Try to delete non-existent %r' % sudocmd1,
-            command=('sudocmd_del', [sudocmd1], {}),
-            expected=errors.NotFound(
-                reason=u'%s: sudo command not found' % sudocmd1),
-        ),
-
-
-        dict(
-            desc='Create %r' % sudocmd1,
-            command=('sudocmd_add', [sudocmd1],
-                dict(
-                    description=u'Test sudo command 1',
-                ),
-            ),
-            expected=dict(
-                value=sudocmd1,
-                summary=u'Added Sudo Command "%s"' % sudocmd1,
-                result=dict(
-                    dn=fuzzy_sudocmddn,
-                    sudocmd=[sudocmd1],
-                    description=[u'Test sudo command 1'],
-                    objectclass=objectclasses.sudocmd,
-                    ipauniqueid=[fuzzy_uuid],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Create %r' % sudocmd1_camelcase,
-            command=('sudocmd_add', [sudocmd1_camelcase],
-                dict(
-                    description=u'Test sudo command 2',
-                ),
-            ),
-            expected=dict(
-                value=sudocmd1_camelcase,
-                summary=u'Added Sudo Command "%s"' % sudocmd1_camelcase,
-                result=dict(
-                    dn=fuzzy_sudocmddn,
-                    sudocmd=[sudocmd1_camelcase],
-                    description=[u'Test sudo command 2'],
-                    objectclass=objectclasses.sudocmd,
-                    ipauniqueid=[fuzzy_uuid],
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc='Try to create duplicate %r' % sudocmd1,
-            command=('sudocmd_add', [sudocmd1],
-                dict(
-                    description=u'Test sudo command 1',
-                ),
-            ),
-            expected=errors.DuplicateEntry(message=u'sudo command with ' +
-                u'name "%s" already exists' % sudocmd1),
-        ),
-
-        dict(
-            desc='Try to create duplicate %r' % sudocmd1_camelcase,
-            command=('sudocmd_add', [sudocmd1_camelcase],
-                dict(
-                    description=u'Test sudo command 2',
-                ),
-            ),
-            expected=errors.DuplicateEntry(message=u'sudo command with ' +
-                u'name "%s" already exists' % sudocmd1_camelcase),
-        ),
-
-
-        dict(
-            desc='Retrieve %r' % sudocmd1,
-            command=('sudocmd_show', [sudocmd1], {}),
-            expected=dict(
-                value=sudocmd1,
-                summary=None,
-                result=dict(
-                    dn=fuzzy_sudocmddn,
-                    sudocmd=[sudocmd1],
-                    description=[u'Test sudo command 1'],
-                ),
-            ),
-        ),
+@pytest.fixture(scope='class')
+def sudocmd2(request):
+    tracker = SudoCmdTracker(command=u'/usr/bin/sudoTestCmd1',
+                             description=u'Test sudo command 2')
+    return tracker.make_fixture(request)
 
 
-        dict(
-            desc='Search for %r' % sudocmd1,
-            command=('sudocmd_find', [sudocmd1], {}),
-            expected=dict(
-                count=1,
-                truncated=False,
-                summary=u'1 Sudo Command matched',
-                result=[
-                    dict(
-                        dn=fuzzy_sudocmddn,
-                        sudocmd=[sudocmd1],
-                        description=[u'Test sudo command 1'],
-                    ),
-                ],
-            ),
-        ),
+@pytest.fixture(scope='class')
+def sudorule1(request):
+    name = u'test_sudorule1'
 
-        dict(
-            desc='Search for %r' % sudocmd1_camelcase,
-            command=('sudocmd_find', [sudocmd1_camelcase], {}),
-            expected=dict(
-                count=1,
-                truncated=False,
-                summary=u'1 Sudo Command matched',
-                result=[
-                    dict(
-                        dn=fuzzy_sudocmddn,
-                        sudocmd=[sudocmd1_camelcase],
-                        description=[u'Test sudo command 2'],
-                    ),
-                ],
-            ),
-        ),
+    def fin():
+        api.Command['sudorule_del'](name)
+    request.addfinalizer(fin)
+    return name
 
 
-        dict(
-            desc='Update %r' % sudocmd1,
-            command=('sudocmd_mod', [sudocmd1], dict(
-                description=u'Updated sudo command 1')),
-            expected=dict(
-                value=sudocmd1,
-                summary=u'Modified Sudo Command "%s"' % sudocmd1,
-                result=dict(
-                    sudocmd=[sudocmd1],
-                    description=[u'Updated sudo command 1'],
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc='Retrieve %r to verify update' % sudocmd1,
-            command=('sudocmd_show', [sudocmd1], {}),
-            expected=dict(
-                value=sudocmd1,
-                summary=None,
-                result=dict(
-                    dn=fuzzy_sudocmddn,
-                    sudocmd=[sudocmd1],
-                    description=[u'Updated sudo command 1'],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Create %r' % sudorule1,
-            command=('sudorule_add', [sudorule1], {}),
-            expected=lambda e, result: True,
-        ),
-
-        dict(
-            desc='Add %r to %r allow list' % (sudocmd1, sudorule1),
-            command=('sudorule_add_allow_command', [sudorule1],
-                dict(sudocmd=sudocmd1)),
-            expected=dict(
-                    completed=1,
-                    failed=dict(
-                        memberallowcmd=dict(sudocmdgroup=(), sudocmd=())),
-                    result=lambda result: True,
-                ),
-        ),
-
-        dict(
-            desc="Test %r can't be deleted when in %r" % (sudocmd1, sudorule1),
-            command=('sudocmd_del', [sudocmd1], {}),
-            expected=errors.DependentEntry(key=sudocmd1, label='sudorule',
-                dependent=sudorule1),
-        ),
-
-        dict(
-            desc='Remove %r from %r' % (sudocmd1, sudorule1),
-            command=('sudorule_remove_allow_command', [sudorule1],
-                dict(sudocmd=sudocmd1)),
-            expected=dict(
-                    completed=1,
-                    failed=dict(
-                        memberallowcmd=dict(sudocmdgroup=(), sudocmd=())),
-                    result=lambda result: True,
-                ),
-        ),
-
-        dict(
-            desc='Add %r to %r deny list' % (sudocmd1, sudorule1),
-            command=('sudorule_add_deny_command', [sudorule1],
-                dict(sudocmd=sudocmd1)),
-            expected=dict(
-                    completed=1,
-                    failed=dict(
-                        memberdenycmd=dict(sudocmdgroup=(), sudocmd=())),
-                    result=lambda result: True,
-                ),
-        ),
-
-        dict(
-            desc="Test %r can't be deleted when in %r" % (sudocmd1, sudorule1),
-            command=('sudocmd_del', [sudocmd1], {}),
-            expected=errors.DependentEntry(key=sudocmd1, label='sudorule',
-                dependent=sudorule1),
-        ),
-
-        dict(
-            desc='Remove %r from %r' % (sudocmd1, sudorule1),
-            command=('sudorule_remove_deny_command', [sudorule1],
-                dict(sudocmd=sudocmd1)),
-            expected=dict(
-                    completed=1,
-                    failed=dict(
-                        memberdenycmd=dict(sudocmdgroup=(), sudocmd=())),
-                    result=lambda result: True,
-                ),
-        ),
-
-        dict(
-            desc='Delete %r' % sudocmd1,
-            command=('sudocmd_del', [sudocmd1], {}),
-            expected=dict(
-                value=[sudocmd1],
-                summary=u'Deleted Sudo Command "%s"' % sudocmd1,
-                result=dict(failed=[]),
-            ),
-        ),
-
-
-        dict(
-            desc='Try to retrieve non-existent %r' % sudocmd1,
-            command=('sudocmd_show', [sudocmd1], {}),
-            expected=errors.NotFound(
-                reason=u'%s: sudo command not found' % sudocmd1),
-        ),
-
+@pytest.mark.tier1
+class TestNonexistentSudoCmd(XMLRPC_test):
+    def test_retrieve_nonexistent(self, sudocmd1):
+        """ Try to retrieve non-existent sudocmd """
+        command = sudocmd1.make_retrieve_command()
+        with raises_exact(errors.NotFound(
+                reason=u'%s: sudo command not found' % sudocmd1.cmd)):
+            command()
+
+    def test_update_nonexistent(self, sudocmd1):
+        """ Try to update non-existent sudocmd """
+        command = sudocmd1.make_update_command(dict(description=u'Nope'))
+        with raises_exact(errors.NotFound(
+                reason=u'%s: sudo command not found' % sudocmd1.cmd)):
+            command()
+
+    def test_delete_nonexistent(self, sudocmd1):
+        """ Try to delete non-existent sudocmd """
+        command = sudocmd1.make_delete_command()
+        with raises_exact(errors.NotFound(
+                reason=u'%s: sudo command not found' % sudocmd1.cmd)):
+            command()
 
-        dict(
-            desc='Try to update non-existent %r' % sudocmd1,
-            command=('sudocmd_mod', [sudocmd1], dict(description=u'Nope')),
-            expected=errors.NotFound(
-                reason=u'%s: sudo command not found' % sudocmd1),
-        ),
 
+@pytest.mark.tier1
+class TestSudoCmd(XMLRPC_test):
+    def test_create(self, sudocmd1, sudocmd2):
+        """ Create sudocmd and sudocmd with camelcase'd command """
+        sudocmd1.ensure_exists()
+        sudocmd2.ensure_exists()
+
+    def test_create_duplicates(self, sudocmd1, sudocmd2):
+        """ Try to create duplicate sudocmds """
+        sudocmd1.ensure_exists()
+        sudocmd2.ensure_exists()
+        command1 = sudocmd1.make_create_command()
+        command2 = sudocmd2.make_create_command()
+
+        with raises_exact(errors.DuplicateEntry(
+                message=u'sudo command with name "%s" already exists' %
+                sudocmd1.cmd)):
+            command1()
+        with raises_exact(errors.DuplicateEntry(
+                message=u'sudo command with name "%s" already exists' %
+                sudocmd2.cmd)):
+            command2()
+
+    def test_retrieve(self, sudocmd1):
+        """ Retrieve sudocmd """
+        sudocmd1.ensure_exists()
+        sudocmd1.retrieve()
+
+    def test_search(self, sudocmd1, sudocmd2):
+        """ Search for sudocmd """
+        sudocmd1.find()
+        sudocmd2.find()
+
+    def test_update_and_verify(self, sudocmd1):
+        """ Update sudocmd description and verify by retrieve """
+        sudocmd1_desc_new = u'Updated sudo command 1'
+        sudocmd1.update(dict(description=sudocmd1_desc_new),
+                        dict(description=[sudocmd1_desc_new]))
+        sudocmd1.retrieve()
 
-        dict(
-            desc='Try to delete non-existent %r' % sudocmd1,
-            command=('sudocmd_del', [sudocmd1], {}),
-            expected=errors.NotFound(
-                reason=u'%s: sudo command not found' % sudocmd1),
-        ),
 
-        dict(
-            desc='Retrieve %r' % sudocmd1_camelcase,
-            command=('sudocmd_show', [sudocmd1_camelcase], {}),
-            expected=dict(
-                value=sudocmd1_camelcase,
-                summary=None,
-                result=dict(
-                    dn=fuzzy_sudocmddn,
-                    sudocmd=[sudocmd1_camelcase],
-                    description=[u'Test sudo command 2'],
-                ),
-            ),
-        ),
-    ]
+@pytest.mark.tier1
+class TestSudoCmdInSudoRuleLists(XMLRPC_test):
+    def test_add_sudocmd_to_sudorule_allow_list(self, sudocmd1, sudorule1):
+        """ Add sudocmd to sudorule allow list """
+        sudocmd1.ensure_exists()
+        api.Command['sudorule_add'](sudorule1)
+        result = api.Command['sudorule_add_allow_command'](
+            sudorule1, sudocmd=sudocmd1.cmd
+        )
+        assert_deepequal(dict(
+            completed=1,
+            failed=dict(
+                memberallowcmd=dict(sudocmdgroup=(), sudocmd=())),
+            result=lambda result: True,
+        ), result)
+
+    def test_del_dependent_sudocmd_sudorule_allow(self, sudocmd1, sudorule1):
+        """ Try to delete sudocmd that is in sudorule allow list """
+        sudocmd1.ensure_exists()
+        command = sudocmd1.make_delete_command()
+        with raises_exact(errors.DependentEntry(
+                key=sudocmd1.cmd,
+                label='sudorule',
+                dependent=sudorule1)):
+            command()
+
+    def test_remove_sudocmd_from_sudorule_allow(self, sudocmd1, sudorule1):
+        """ Remove sudocmd from sudorule allow list """
+        sudocmd1.ensure_exists()
+        result = api.Command['sudorule_remove_allow_command'](
+            sudorule1, sudocmd=sudocmd1.cmd
+        )
+        assert_deepequal(dict(
+            completed=1,
+            failed=dict(
+                memberallowcmd=dict(sudocmdgroup=(), sudocmd=())),
+            result=lambda result: True),
+            result)
+
+    def test_add_sudocmd_to_sudorule_deny_list(self, sudocmd1, sudorule1):
+        """ Add sudocmd to sudorule deny list """
+        sudocmd1.ensure_exists()
+        result = api.Command['sudorule_add_deny_command'](
+            sudorule1, sudocmd=sudocmd1.cmd
+        )
+        assert_deepequal(dict(
+            completed=1,
+            failed=dict(
+                memberdenycmd=dict(sudocmdgroup=(), sudocmd=())),
+            result=lambda result: True),
+            result)
+
+    def test_del_dependent_sudocmd_sudorule_deny(self, sudocmd1, sudorule1):
+        """ Try to delete sudocmd that is in sudorule deny list """
+        sudocmd1.ensure_exists()
+        command = sudocmd1.make_delete_command()
+        with raises_exact(errors.DependentEntry(
+                key=sudocmd1.cmd,
+                label='sudorule',
+                dependent=sudorule1)):
+            command()
+
+    def test_remove_sudocmd_from_sudorule_deny(self, sudocmd1, sudorule1):
+        """ Remove sudocmd from sudorule deny list """
+        sudocmd1.ensure_exists()
+        result = api.Command['sudorule_remove_deny_command'](
+            sudorule1, sudocmd=sudocmd1.cmd
+        )
+        assert_deepequal(dict(
+            completed=1,
+            failed=dict(
+                memberdenycmd=dict(sudocmdgroup=(), sudocmd=())),
+            result=lambda result: True),
+            result)
diff --git a/ipatests/test_xmlrpc/tracker/sudocmd_plugin.py b/ipatests/test_xmlrpc/tracker/sudocmd_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb72e4ba24c21d0b44bdda92581639737093c016
--- /dev/null
+++ b/ipatests/test_xmlrpc/tracker/sudocmd_plugin.py
@@ -0,0 +1,113 @@
+#
+# Copyright (C) 2015  FreeIPA Contributors see COPYING for license
+#
+
+from ipatests.test_xmlrpc import objectclasses
+from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_uuid, fuzzy_sudocmddn
+
+from ipatests.test_xmlrpc.tracker.base import Tracker
+from ipatests.util import assert_deepequal
+
+
+class SudoCmdTracker(Tracker):
+    """ Class for tracking sudo commands """
+    retrieve_keys = {u'dn', u'sudocmd', u'description',
+                     u'memberof_sudocmdgroup'}
+    retrieve_all_keys = retrieve_keys | {u'ipauniqueid', u'objectclass'}
+
+    create_keys = retrieve_all_keys
+    update_keys = retrieve_keys - {u'dn'}
+
+    def __init__(self, command, description="Test sudo command"):
+        super(SudoCmdTracker, self).__init__(default_version=None)
+        self.cmd = command
+        self.dn = fuzzy_sudocmddn
+        self.description = description
+
+    @property
+    def name(self):
+        """ Property holding the name of the entry in LDAP """
+        return self.cmd
+
+    def make_create_command(self, force=True):
+        """ Make function that creates a sudocmd using 'sudocmd-add' """
+        return self.make_command('sudocmd_add', self.cmd,
+                                 description=self.description)
+
+    def make_delete_command(self):
+        """ Make function that deletes a sudocmd using 'sudocmd-del' """
+        return self.make_command('sudocmd_del', self.cmd)
+
+    def make_retrieve_command(self, all=False, raw=False):
+        """ Make function that retrieves a sudocmd using 'sudocmd-show' """
+        return self.make_command('sudocmd_show', self.cmd, all=all)
+
+    def make_find_command(self, *args, **kwargs):
+        """ Make function that searches for a sudocmd using 'sudocmd-find' """
+        return self.make_command('sudocmd_find', *args, **kwargs)
+
+    def make_update_command(self, updates):
+        """ Make function that updates a sudocmd using 'sudocmd-mod' """
+        return self.make_command('sudocmd_mod', self.cmd, **updates)
+
+    def track_create(self):
+        """ Updates expected state for sudocmd creation"""
+        self.attrs = dict(
+            dn=self.dn,
+            sudocmd=[self.cmd],
+            description=[self.description],
+            ipauniqueid=[fuzzy_uuid],
+            objectclass=objectclasses.sudocmd,
+            )
+        self.exists = True
+
+    def check_create(self, result):
+        """ Checks 'sudocmd_add' command result """
+        assert_deepequal(dict(
+            value=self.cmd,
+            summary=u'Added Sudo Command "%s"' % self.cmd,
+            result=self.filter_attrs(self.create_keys)
+            ), result)
+
+    def check_delete(self, result):
+        """ Checks 'sudocmd_del' command result """
+        assert_deepequal(dict(
+            value=[self.cmd],
+            summary=u'Deleted Sudo Command "%s"' % self.cmd,
+            result=dict(failed=[]),
+            ), result)
+
+    def check_retrieve(self, result, all=False, raw=False):
+        """ Checks 'sudocmd_show' command result """
+        if all:
+            expected = self.filter_attrs(self.retrieve_all_keys)
+        else:
+            expected = self.filter_attrs(self.retrieve_keys)
+
+        assert_deepequal(dict(
+            value=self.cmd,
+            summary=None,
+            result=expected
+            ), result)
+
+    def check_find(self, result, all=False, raw=False):
+        """ Checks 'sudocmd_find' command result """
+        if all:
+            expected = self.filter_attrs(self.retrieve_all_keys)
+        else:
+            expected = self.filter_attrs(self.retrieve_keys)
+
+        assert_deepequal(dict(
+            count=1,
+            truncated=False,
+            summary=u'1 Sudo Command matched',
+            result=[expected],
+        ), result)
+
+    def check_update(self, result, extra_keys={}):
+        """ Checks 'sudocmd_mod' command result """
+        assert_deepequal(dict(
+            value=self.cmd,
+            summary=u'Modified Sudo Command "%s"' % self.cmd,
+            result=self.filter_attrs(self.update_keys | set(extra_keys))
+        ), result)
-- 
2.5.0

-- 
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

Reply via email to