Add_a_new_attribute

Add_a_new_attribute#

This is a patch from Martin Kosek, annotated to explain how to add an LDAP attribute to IPA. You can find the change as commit 5af2e17 in the IPA source tree.

From: Martin Kosek <mkosek@redhat.com>
Date: Tue, 23 Apr 2013 09:59:24 +0200
Subject: [PATCH] Add userClass attribute for hosts

This new freeform host attribute will allow provisioning systems
to add custom tags for host objects which can be later used for
in automember rules or for additional local interpretation.

Design page: ``\ ```http://www.freeipa.org/page/V3/Integration_with_a_provisioning_systems`` <http://www.freeipa.org/page/V3/Integration_with_a_provisioning_systems>`__
Ticket: ``\ ```https://fedorahosted.org/freeipa/ticket/3583`` <https://fedorahosted.org/freeipa/ticket/3583>`__

---
API.txt                               |  9 ++++++---
VERSION                               |  2 +-
install/share/60basev2.ldif           |  2 +-
install/updates/10-60basev3.update    |  1 +
ipalib/plugins/host.py                |  7 +++++++
tests/test_xmlrpc/test_host_plugin.py | 23 +++++++++++++++++++++++
6 files changed, 39 insertions(+), 5 deletions(-)

First, we need to add the attribute to the LDAP schema, so it is available for new installs. Locate the appropriate objectclass definition in install/share/, and add the desired attribute.

diff --git a/install/share/60basev2.ldif b/install/share/60basev2.ldif
index 3b05e370147f6cace12913e695e02eb6550c6010..8e7174c10ddf73194bfbe634ff34c8c3fd25e264 100644
--- a/install/share/60basev2.ldif
+++ b/install/share/60basev2.ldif
@@ -13,7 +13,7 @@ attributeTypes: (2.16.840.1.113730.3.8.3.24 NAME 'ipaEntitlementId' DESC 'Entitl
 # ipaKrbAuthzData added here. Even though it is a v3 attribute it is updating
 # a v2 objectClass so needs to be here.
 attributeTypes: (2.16.840.1.113730.3.8.11.37 NAME 'ipaKrbAuthzData' DESC 'type of PAC preferred by a service' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v3' )
-objectClasses: (2.16.840.1.113730.3.8.4.1 NAME 'ipaHost' AUXILIARY MUST ( fqdn ) MAY ( userPassword $ ipaClientVersion $ enrolledBy $ memberOf) X-ORIGIN 'IPA v2' )
+objectClasses: (2.16.840.1.113730.3.8.4.1 NAME 'ipaHost' AUXILIARY MUST ( fqdn ) MAY ( userPassword $ ipaClientVersion $ enrolledBy $ memberOf $ userClass ) X-ORIGIN 'IPA v2' )
 objectClasses: (2.16.840.1.113730.3.8.4.12 NAME 'ipaObject' DESC 'IPA objectclass' AUXILIARY MUST ( ipaUniqueId ) X-ORIGIN 'IPA v2' )
 objectClasses: (2.16.840.1.113730.3.8.4.14 NAME 'ipaEntitlement' DESC 'IPA Entitlement object' AUXILIARY MUST ( ipaEntitlementId ) MAY ( userPKCS12 $ userCertificate ) X-ORIGIN 'IPA v2' )
 objectClasses: (2.16.840.1.113730.3.8.4.15 NAME 'ipaPermission' DESC 'IPA Permission objectclass' AUXILIARY MAY ( ipaPermissionType ) X-ORIGIN 'IPA v2' )

The next step is to add a parameter definition to the appropriate framework object. In addition to the big takes_params list, there may also be other lists of parameters (such as default_attributes whose members are shown by *-find and *-show commands by default). Update these lists as appropriate.

diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index c79b9e212feec640a34ac0905d46adacee54060f..e615259174722af645bdad72802d8fca9783f6d3 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -230,6 +230,7 @@ class host(LDAPObject):
         'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname',
         'nshardwareplatform', 'nsosversion', 'usercertificate', 'memberof',
         'managedby', 'memberindirect', 'memberofindirect', 'macaddress',
+        'userclass'
     ]
     uuid_attribute = 'ipauniqueid'
     attribute_members = {
@@ -323,6 +324,12 @@ class host(LDAPObject):
             csv=True,
             flags=['no_search'],
         ),
+        Str('userclass*',
+            cli_name='class',
+            label=_('Class'),
+            doc=_('Host category (semantics placed on this attribute are for '
+                  'local interpretation)'),
+        ),
     ) + ticket_flags_params

     def get_dn(self, *keys, **options):

Since the API was changed, run the make-api script located at the root of te source tree. This will re-generate API.txt, our safeguard against unintentional API changes.

diff --git a/API.txt b/API.txt
index 3e01fdc3611b5bc71e1a4ee185af63f7c4b07c06..c2400e901345a70e0236d1c02979220c19ece9a5 100644
--- a/API.txt
+++ b/API.txt
@@ -1723,7 +1723,7 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('value', <type 'bool'>, None)
 output: Output('warning', (<type 'list'>, <type 'tuple'>, <type 'NoneType'>), None)
 command: host_add
-args: 1,20,3
+args: 1,21,3
 arg: Str('fqdn', attribute=True, cli_name='hostname', multivalue=False, primary_key=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
@@ -1743,6 +1743,7 @@ option: Flag('random', attribute=False, autofill=True, cli_name='random', defaul
 option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
 option: Str('setattr*', cli_name='setattr', exclude='webui')
 option: Bytes('usercertificate', attribute=True, cli_name='certificate', multivalue=False, required=False)
+option: Str('userclass', attribute=True, cli_name='class', multivalue=True, required=False)
 option: Str('userpassword', attribute=True, cli_name='password', multivalue=False, required=False)
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
@@ -1774,7 +1775,7 @@ output: Output('result', <type 'bool'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('value', <type 'unicode'>, None)
 command: host_find
-args: 1,31,4
+args: 1,32,4
 arg: Str('criteria?', noextrawhitespace=False)
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
 option: Str('description', attribute=True, autofill=False, cli_name='desc', multivalue=False, query=True, required=False)
@@ -1805,6 +1806,7 @@ option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui
 option: Int('sizelimit?', autofill=False, minvalue=0)
 option: Int('timelimit?', autofill=False, minvalue=0)
 option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=False, query=True, required=False)
+option: Str('userclass', attribute=True, autofill=False, cli_name='class', multivalue=True, query=True, required=False)
 option: Str('userpassword', attribute=True, autofill=False, cli_name='password', multivalue=False, query=True, required=False)
 option: Str('version?', exclude='webui')
 output: Output('count', <type 'int'>, None)
@@ -1812,7 +1814,7 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Output('truncated', <type 'bool'>, None)
 command: host_mod
-args: 1,21,3
+args: 1,22,3
 arg: Str('fqdn', attribute=True, cli_name='hostname', multivalue=False, primary_key=True, query=True, required=True)
 option: Str('addattr*', cli_name='addattr', exclude='webui')
 option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
@@ -1833,6 +1835,7 @@ option: Flag('rights', autofill=True, default=False)
 option: Str('setattr*', cli_name='setattr', exclude='webui')
 option: Flag('updatedns?', autofill=True, default=False)
 option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=False, required=False)
+option: Str('userclass', attribute=True, autofill=False, cli_name='class', multivalue=True, required=False)
 option: Str('userpassword', attribute=True, autofill=False, cli_name='password', multivalue=False, required=False)
 option: Str('version?', exclude='webui')
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))

With every update of the API, you must bump the API version. When adding parameters calls, only bump the minor version number.

diff --git a/VERSION b/VERSION
index 9208237cbedf23d71c5c579fcc10207380cc9712..4bee01b981d818de21f0be1b16d5668a7f453baf 100644
--- a/VERSION
+++ b/VERSION
@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=57
+IPA_API_VERSION_MINOR=58

And of course, every code change should be accompanied by a test.

diff --git a/tests/test_xmlrpc/test_host_plugin.py b/tests/test_xmlrpc/test_host_plugin.py
index f788dc6bc6d55f46856ada4b816997bfb517d8c4..07faf77607284b2193716854b287208f563d9472 100644
--- a/tests/test_xmlrpc/test_host_plugin.py
+++ b/tests/test_xmlrpc/test_host_plugin.py
@@ -700,6 +700,7 @@ class test_host(Declarative):
                 dict(
                     description=u'Test host 2',
                     l=u'Undisclosed location 2',
+                    userclass=[u'webserver', u'mailserver'],
                     force=True,
                 ),
             ),
@@ -715,6 +716,7 @@ class test_host(Declarative):
                     objectclass=objectclasses.host,
                     ipauniqueid=[fuzzy_uuid],
                     managedby_host=[fqdn2],
+                    userclass=[u'webserver', u'mailserver'],
                     has_keytab=False,
                     has_password=False,
                 ),
@@ -722,6 +724,27 @@ class test_host(Declarative):
         ),


+        dict(
+            desc='Retrieve %r' % fqdn2,
+            command=('host_show', [fqdn2], {}),
+            expected=dict(
+                value=fqdn2,
+                summary=None,
+                result=dict(
+                    dn=dn2,
+                    fqdn=[fqdn2],
+                    description=[u'Test host 2'],
+                    l=[u'Undisclosed location 2'],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn2, api.env.realm)],
+                    has_keytab=False,
+                    has_password=False,
+                    managedby_host=[fqdn2],
+                    userclass=[u'webserver', u'mailserver'],
+                ),
+            ),
+        ),
+
+
         # This test will only succeed when running against lite-server.py
         # on same box as IPA install.
         dict(
--
1.8.1.4