From 663de835071f48411657a49b844577e547925c5d Mon Sep 17 00:00:00 2001
From: Ilias Stamatis <stamatis.iliass@gmail.com>
Date: Thu, 10 Aug 2017 14:04:10 +0300
Subject: [PATCH] Issue 46 - dsconf support for dynamic schema reload

Description: Add dsconf support for dynamically reloading schema.

Notes: Renamed old-style Schema class to SchemaLegacy.

https://pagure.io/lib389/issue/46

Author: Ilias95

Review by: ???
---
 lib389/__init__.py          |  2 +-
 lib389/cli_conf/schema.py   | 17 ++++++++++++++---
 lib389/schema.py            | 24 +++++++++++++++++++++++-
 lib389/tasks.py             |  7 +++++++
 lib389/tests/schema_test.py | 13 ++++++++++++-
 5 files changed, 57 insertions(+), 6 deletions(-)
 mode change 100644 => 100755 lib389/schema.py

diff --git a/lib389/__init__.py b/lib389/__init__.py
index 43ead6b..359cc15 100644
--- a/lib389/__init__.py
+++ b/lib389/__init__.py
@@ -305,7 +305,7 @@ class DirSrv(SimpleLDAPObject, object):
         from lib389.replica import Replicas
         from lib389.changelog import Changelog
         from lib389.agreement import Agreement
-        from lib389.schema import Schema
+        from lib389.schema import SchemaLegacy as Schema
         from lib389.plugins import Plugins
         from lib389.tasks import Tasks
         from lib389.index import IndexLegacy as Index
diff --git a/lib389/cli_conf/schema.py b/lib389/cli_conf/schema.py
index 0e1f2a4..49596b5 100644
--- a/lib389/cli_conf/schema.py
+++ b/lib389/cli_conf/schema.py
@@ -6,9 +6,9 @@
 # See LICENSE for details.
 # --- END COPYRIGHT BLOCK ---
 
-import argparse
-
 from lib389.cli_base import _get_arg
+from lib389.schema import Schema
+
 
 def list_attributetype(inst, basedn, log, args):
     for attributetype in inst.schema.get_attributetypes():
@@ -16,7 +16,7 @@ def list_attributetype(inst, basedn, log, args):
 
 def query_attributetype(inst, basedn, log, args):
     # Need the query type
-    attr = _get_arg( args.attr , msg="Enter attribute to query" )
+    attr = _get_arg(args.attr, msg="Enter attribute to query")
     attributetype, must, may = inst.schema.query_attributetype(attr)
     print(attributetype)
     print("")
@@ -28,6 +28,14 @@ def query_attributetype(inst, basedn, log, args):
     for oc in may:
         print(oc)
 
+def reload_schema(inst, basedn, log, args):
+    schema = Schema(inst)
+    log.info('Attempting to add task entry... This will fail if Schema Reload plug-in is not enabled.')
+    task = schema.reload(args.schemadir)
+    log.info('Successfully added task entry ' + task.dn)
+    log.info("To verify that the schema reload operation was successful, please check the error logs.")
+
+
 def create_parser(subparsers):
     schema_parser = subparsers.add_parser('schema', help='Query and manipulate schema')
 
@@ -40,3 +48,6 @@ def create_parser(subparsers):
     query_attributetype_parser.set_defaults(func=query_attributetype)
     query_attributetype_parser.add_argument('attr', nargs='?', help='Attribute type to query')
 
+    reload_parser = subcommands.add_parser('reload', help='Dynamically reload schema while server is running')
+    reload_parser.set_defaults(func=reload_schema)
+    reload_parser.add_argument('-d', '--schemadir', help="directory where schema files are located")
diff --git a/lib389/schema.py b/lib389/schema.py
old mode 100644
new mode 100755
index 6e7f65c..6003093
--- a/lib389/schema.py
+++ b/lib389/schema.py
@@ -13,11 +13,33 @@
 import glob
 import ldap
 from ldap.schema.models import AttributeType, ObjectClass, MatchingRule
+
 from lib389._constants import *
+from lib389._constants import DN_SCHEMA
 from lib389.utils import ds_is_newer
+from lib389._mapped_object import DSLdapObject
+from lib389.tasks import SchemaReloadTask
+
+
+class Schema(DSLdapObject):
+    def __init__(self, instance, batch=False):
+        super(Schema, self).__init__(instance=instance, batch=batch)
+        self._dn = DN_SCHEMA
+        self._rdn_attribute = 'cn'
+
+    def reload(self, schema_dir=None):
+        task = SchemaReloadTask(self._instance)
+
+        task_properties = {}
+        if schema_dir is not None:
+            task_properties['schemadir'] = schema_dir
+
+        task.create(properties=task_properties)
+
+        return task
 
 
-class Schema(object):
+class SchemaLegacy(object):
 
     def __init__(self, conn):
         """@param conn - a DirSrv instance"""
diff --git a/lib389/tasks.py b/lib389/tasks.py
index 2c62dce..ec46145 100644
--- a/lib389/tasks.py
+++ b/lib389/tasks.py
@@ -90,6 +90,13 @@ class USNTombstoneCleanupTask(Task):
 
         return super(USNTombstoneCleanupTask, self)._validate(rdn, properties, basedn)
 
+class SchemaReloadTask(Task):
+    def __init__(self, instance, dn=None, batch=False):
+        self.cn = 'schema_reload_' + Task._get_task_date()
+        dn = "cn=" + self.cn + ",cn=schema reload task," + DN_TASKS
+
+        super(SchemaReloadTask, self).__init__(instance, dn, batch)
+
 class Tasks(object):
     proxied_methods = 'search_s getEntry'.split()
 
diff --git a/lib389/tests/schema_test.py b/lib389/tests/schema_test.py
index 20c5a13..28daefe 100644
--- a/lib389/tests/schema_test.py
+++ b/lib389/tests/schema_test.py
@@ -7,8 +7,10 @@
 # --- END COPYRIGHT BLOCK ---
 #
 import pytest
-from lib389._constants import *
+
 from lib389 import DirSrv
+from lib389._constants import SER_HOST, SER_PORT, SER_SERVERID_PROP, LOCALHOST
+from lib389.schema import Schema
 
 INSTANCE_PORT = 54321
 INSTANCE_SERVERID = 'standalone'
@@ -53,9 +55,18 @@ def test_schema(topology):
     assert topology.instance.schema.query_objectclass('account').names == \
         ('account', )
 
+def test_schema_reload(topology):
+    """Test that the appropriate task entry is created when reloading schema."""
+    schema = Schema(topology.instance)
+    task = schema.reload()
+    assert task.exists()
+    task.wait()
+    assert task.get_exit_code() == 0
+
 
 if __name__ == '__main__':
     # Run isolated
     # -s for DEBUG mode
+    import os
     CURRENT_FILE = os.path.realpath(__file__)
     pytest.main("-s %s" % CURRENT_FILE)
-- 
2.13.4

