Re: [389-devel] Instance discovery tools in lib389

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 2015-08-13 at 07:49 +0930, William wrote:
> > > 
> > > I hope this demonstrates how I want to tie together the other 
> > > contributions I
> > > have made into tools, but also making it possible to test other 
> > > aspects of
> > > 389ds.
> > > 
> > > Comments and advice welcome.
> > Hi William,
> > 
> > This looks good to me, it's definitely a good start.  I see no reason 
> > not to push the patch once you think it's ready and polished enough. 
> > Don't forget to add the license text to new files. <This actually 
> > needs 
> > to be done for many of the files in lib389 - I'll open a ticket for 
> > that>
> 
> Thanks! I think that the main piece of polish is the helper.py library
> that backs the cli tools to make them a bit easier to write. I'll make
> this a bit nicer now and send this back in for another review.
> 
> Thanks for your time,
> 
> Sincerely,
> 

Here is a slightly more polished CliTools helper, and the addition of a more
"interesting" command line tool example of an attribute query tool. You give it
an attribute type and it will provide a list of object classes that Must or May
take that attribute. I have found this a very useful tool in my work place where
we have a complex custom schema, so it's great to know what objectclass to put
on objects when you need to put certain attributes on them.

The biggest outstanding part for me now is how to best format the output of the
tool. Should I use the same logging tool as lib389? Or print things out in the
current form? Or something else. 

Sincerely,

-- 
William Brown <william@xxxxxxxxxxxxxxxx>
From 007118ee71651e1ba3af0ff502c0351a51111c2d Mon Sep 17 00:00:00 2001
From: William Brown <wililam@xxxxxxxxxxxxxxx>
Date: Thu, 6 Aug 2015 10:11:25 +0930
Subject: [PATCH] Example of commandline tools implementation for listing all
 attribute types and instances

---
 clitools/__init__.py                      | 43 +++++++++++++++++++++++
 clitools/ds_list_instances.py             | 18 ++++++++++
 clitools/ds_schema_attributetype_list.py  | 24 +++++++++++++
 clitools/ds_schema_attributetype_query.py | 31 +++++++++++++++++
 lib389/__init__.py                        | 58 ++++++++++++++++++++++++++-----
 lib389/_constants.py                      | 15 ++++++++
 6 files changed, 180 insertions(+), 9 deletions(-)
 create mode 100644 clitools/__init__.py
 create mode 100644 clitools/ds_list_instances.py
 create mode 100644 clitools/ds_schema_attributetype_list.py
 create mode 100644 clitools/ds_schema_attributetype_query.py

diff --git a/clitools/__init__.py b/clitools/__init__.py
new file mode 100644
index 0000000..ecc708f
--- /dev/null
+++ b/clitools/__init__.py
@@ -0,0 +1,43 @@
+# Probably need some helpers for argparse and instance selection
+
+from argparse import ArgumentParser
+from getpass import getpass
+from lib389 import DirSrv
+from lib389._constants import *
+
+class CliTool(object):
+    def __init__(self, args=None):
+        self.ds = DirSrv()
+        if args is not None:
+            self.args = args
+
+    def populate_instance_dict(self, instance):
+        insts = self.ds.list(serverid=instance)
+        if len(insts) != 1:
+            # Raise an exception here?
+            self.inst = None
+        else:
+            self.inst = insts[0]
+
+    def get_rootdn_pass(self):
+        #There is a dict get key thing somewhere ...
+        if self.inst.get(SER_ROOT_PW, None) is None:
+            self.inst[SER_ROOT_PW] = getpass('Enter password for %s on instance %s: ' % (self.inst[SER_ROOT_DN], self.inst[SER_SERVERID_PROP]))
+        return
+
+    def connect(self):
+        self.get_rootdn_pass()
+        self.ds.allocate(self.inst)
+        self.ds.open()
+
+    def disconnect(self):
+        # Is there a ds unbind / disconnect?
+        self.ds.close()
+
+def _clitools_parser():
+    parser = ArgumentParser(add_help=False)
+    parser.add_argument('--instance', '-i', help='The name of the DS instance to connect to and work upon.')
+    return parser
+
+clitools_parser = _clitools_parser()
+
diff --git a/clitools/ds_list_instances.py b/clitools/ds_list_instances.py
new file mode 100644
index 0000000..8647187
--- /dev/null
+++ b/clitools/ds_list_instances.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+
+from lib389._constants import *
+from clitools import CliTool
+
+class ListTool(CliTool):
+    def list_instances(self):
+        # Remember, the prefix can be set with the os environment
+        instances = self.ds.list(all=True)
+        print('Instances on this system:')
+        for instance in instances:
+            print(instance[CONF_SERVER_ID])
+
+if __name__ == '__main__':
+    listtool = ListTool()
+    listtool.list_instances()
+
+
diff --git a/clitools/ds_schema_attributetype_list.py b/clitools/ds_schema_attributetype_list.py
new file mode 100644
index 0000000..0239ac3
--- /dev/null
+++ b/clitools/ds_schema_attributetype_list.py
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+
+#from clitools import clitools_parser, get_instance_dict, get_rootdn_pass
+from clitools import CliTool, clitools_parser
+#from lib389 import DirSrv
+from lib389._constants import *
+import ldap
+
+class SchemaTool(CliTool):
+    def schema_attributetype_list(self):
+        try:
+            self.populate_instance_dict(self.args.instance)
+            self.connect()
+            for attributetype in self.ds.schema.get_attributetypes():
+                print(attributetype)
+        finally:
+            self.disconnect()
+
+if __name__ == '__main__':
+    # Do some arg parse stuff
+    ## You can always add a child parser here too ...
+    args = clitools_parser.parse_args()
+    schematool = SchemaTool(args)
+    schematool.schema_attributetype_list()
diff --git a/clitools/ds_schema_attributetype_query.py b/clitools/ds_schema_attributetype_query.py
new file mode 100644
index 0000000..b390418
--- /dev/null
+++ b/clitools/ds_schema_attributetype_query.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+from clitools import CliTool, clitools_parser
+from lib389._constants import *
+import ldap
+from argparse import ArgumentParser
+
+class SchemaTool(CliTool):
+    def schema_attributetype_query(self):
+        try:
+            self.populate_instance_dict(self.args.instance)
+            self.connect()
+            attributetype, must, may = self.ds.schema.query_attributetype(self.args.attributetype)
+            print(attributetype)
+            print('MUST')
+            for objectclass in must:
+                print(objectclass)
+            print('MAY')
+            for objectclass in may:
+                print(objectclass)
+        finally:
+            self.disconnect()
+
+if __name__ == '__main__':
+    # Do some arg parse stuff
+    ## You can always add a child parser here too ...
+    parser = ArgumentParser(parents=[clitools_parser])
+    parser.add_argument('--attributetype', '-a', help='The name of the attribute type to query', required=True)
+    args = parser.parse_args()
+    schematool = SchemaTool(args)
+    schematool.schema_attributetype_query()
diff --git a/lib389/__init__.py b/lib389/__init__.py
index 10bf0df..a516992 100644
--- a/lib389/__init__.py
+++ b/lib389/__init__.py
@@ -374,7 +374,7 @@ class DirSrv(SimpleLDAPObject):
         self.timeout = timeout
 
         # Reset the args (py.test reuses the args_instance for each test case)
-        args_instance[SER_DEPLOYED_DIR] = os.environ.get('PREFIX', None)
+        args_instance[SER_DEPLOYED_DIR] = os.environ.get('PREFIX', '/')
         args_instance[SER_BACKUP_INST_DIR] = os.environ.get('BACKUPDIR', DEFAULT_BACKUPDIR)
         args_instance[SER_ROOT_DN] = DN_DM
         args_instance[SER_ROOT_PW] = PW_DM
@@ -386,6 +386,12 @@ class DirSrv(SimpleLDAPObject):
         args_instance[SER_USER_ID] = None
         args_instance[SER_GROUP_ID] = None
 
+        # We allocate a "default" prefix here which allows an un-allocate or un-instantiated DirSrv
+        # instance to be able to do an an instance discovery. For example:
+        #  ds = lib389.DirSrv()
+        #  ds.list(all=True)
+        self.prefix = args_instance[SER_DEPLOYED_DIR]
+
         self.__wrapmethods()
         self.__add_brookers__()
 
@@ -440,7 +446,7 @@ class DirSrv(SimpleLDAPObject):
         self.serverid  = args.get(SER_SERVERID_PROP, None)
         self.groupid   = args.get(SER_GROUP_ID, self.userid)
         self.backupdir = args.get(SER_BACKUP_INST_DIR, DEFAULT_BACKUPDIR)
-        self.prefix    = args.get(SER_DEPLOYED_DIR) or '/'
+        self.prefix    = args.get(SER_DEPLOYED_DIR)
 
         # Those variables needs to be revisited (sroot for 64 bits)
         #self.sroot     = os.path.join(self.prefix, "lib/dirsrv")
@@ -471,7 +477,7 @@ class DirSrv(SimpleLDAPObject):
 
         return server
 
-    def list(self, all=False):
+    def list(self, all=False, serverid=None):
         """
             Returns a list dictionary. For a created instance that is on the local file system
             (e.g. <prefix>/etc/dirsrv/slapd-*), it exists a file describing its properties
@@ -489,6 +495,8 @@ class DirSrv(SimpleLDAPObject):
 
             @param all - True or False . default is [False]
 
+            @param instance - The name of the instance to retrieve or None for the current instance
+
             @return list of dictionaries, each of them containing instance properities
 
             @raise IOError - if the file containing the properties is not foundable or readable
@@ -503,7 +511,7 @@ class DirSrv(SimpleLDAPObject):
                 return 1
             return 0
 
-        def _parse_configfile(filename=None):
+        def _parse_configfile(filename=None, serverid=None):
             '''
                 This method read 'filename' and build a dictionary with
                 CONF_* properties
@@ -515,6 +523,9 @@ class DirSrv(SimpleLDAPObject):
                 raise IOError('invalid file name or rights: %s' % filename)
 
             prop = {}
+            prop[CONF_SERVER_ID] = serverid
+            prop[SER_SERVERID_PROP] = serverid
+            prop[SER_DEPLOYED_DIR] = self.prefix
             myfile = open(filename, 'r')
             for line in myfile:
                 # retrieve the value in line:: <PROPNAME>=<string> [';' export <PROPNAME>]
@@ -545,6 +556,31 @@ class DirSrv(SimpleLDAPObject):
                     if test_and_set(prop, property, variable, value):
                         break
 
+            # Now, we have passed the sysconfig environment file.
+            #  read in and parse the dse.ldif to determine our SER_* values.
+            # probably should use path join?
+            dsefile = '%s/dse.ldif' % prop[CONF_CONFIG_DIR]
+            if os.path.exists(dsefile):
+                ldifconn = LDIFConn(dsefile)
+                configentry = ldifconn.get(DN_CONFIG)
+                for key in args_dse_keys:
+                    prop[key] = configentry.getValue(args_dse_keys[key])
+                #SER_HOST            (host) nsslapd-localhost
+                #SER_PORT            (port) nsslapd-port
+                #SER_SECURE_PORT     (sslport) nsslapd-secureport
+                #SER_ROOT_DN         (binddn) nsslapd-rootdn
+                #SER_ROOT_PW         (bindpw) We can't do this
+                #SER_CREATION_SUFFIX (creation_suffix) nsslapd-defaultnamingcontext
+                #SER_USER_ID         (userid) nsslapd-localuser
+                #SER_SERVERID_PROP   (serverid) Already have this
+                #SER_GROUP_ID        (groupid) ???
+                #SER_DEPLOYED_DIR    (prefix) Already provided to do the discovery
+                #SER_BACKUP_INST_DIR (backupdir) nsslapd-bakdir <<-- maybe?
+                # We need to convert these two to int
+                #  because other routines get confused if we don't
+                for intkey in [SER_PORT, SER_SECURE_PORT]:
+                    if prop[intkey] is not None:
+                        prop[intkey] = int(prop[intkey])
             return prop
 
         def search_dir(instances, pattern, stop_value=None):
@@ -565,6 +601,7 @@ class DirSrv(SimpleLDAPObject):
                                         not find it.
             '''
             added = False
+            print(pattern)
             for instance in glob.glob(pattern):
                 serverid = os.path.basename(instance)[len(DEFAULT_ENV_HEAD):]
 
@@ -575,7 +612,7 @@ class DirSrv(SimpleLDAPObject):
                 # it is found, store its properties in the list
                 if stop_value:
                     if stop_value == serverid:
-                        instances.append(_parse_configfile(instance))
+                        instances.append(_parse_configfile(instance, serverid))
                         added = True
                         break
                     else:
@@ -583,7 +620,7 @@ class DirSrv(SimpleLDAPObject):
                         continue
                 else:
                     # we are not looking for a specific value, just add it
-                    instances.append(_parse_configfile(instance))
+                    instances.append(_parse_configfile(instance, serverid))
 
             return added
 
@@ -600,7 +637,10 @@ class DirSrv(SimpleLDAPObject):
         #    inst: <prefix>/etc/dirsrv/slapd-<serverid>  (conf)
         #
 
-        prefix = self.prefix or '/'
+        # Don't need a default value now since it's set in init.
+        prefix = self.prefix
+        if serverid is None and hasattr(self, 'serverid'):
+            serverid = self.serverid
 
         # first identify the directories we will scan
         confdir = os.getenv('INITCONFIGDIR')
@@ -632,7 +672,7 @@ class DirSrv(SimpleLDAPObject):
             # first check the private repository
             if privconfig_head:
                 pattern = "%s*" % os.path.join(privconfig_head, DEFAULT_ENV_HEAD)
-                found = search_dir(instances, pattern, self.serverid)
+                found = search_dir(instances, pattern, serverid)
                 if len(instances) > 0:
                     self.log.info("List from %s" % privconfig_head)
                     for instance in instances:
@@ -647,7 +687,7 @@ class DirSrv(SimpleLDAPObject):
             # second, if not already found, search the system repository
             if not found:
                 pattern = "%s*" % os.path.join(sysconfig_head, DEFAULT_ENV_HEAD)
-                search_dir(instances, pattern, self.serverid)
+                search_dir(instances, pattern, serverid)
                 if len(instances) > 0:
                     self.log.info("List from %s" % privconfig_head)
                     for instance in instances:
diff --git a/lib389/_constants.py b/lib389/_constants.py
index 2b4c6d5..96b008e 100644
--- a/lib389/_constants.py
+++ b/lib389/_constants.py
@@ -78,6 +78,7 @@ ENV_SYSCONFIG_DIR = '/etc/sysconfig'
 ENV_LOCAL_DIR = '.dirsrv'
 
 # CONFIG file (<prefix>/etc/sysconfig/dirsrv-* or $HOME/.dirsrv/dirsrv-*) keywords
+CONF_SERVER_ID     = 'SERVER_ID'
 CONF_SERVER_DIR    = 'SERVER_DIR'
 CONF_SERVERBIN_DIR = 'SERVERBIN_DIR'
 CONF_CONFIG_DIR    = 'CONFIG_DIR'
@@ -428,3 +429,17 @@ args_instance = {
                    SER_SERVERID_PROP: "template",
                    SER_CREATION_SUFFIX: DEFAULT_SUFFIX}
 
+# Helper for linking dse.ldif values to the parse_config function
+args_dse_keys = {
+                SER_HOST: 'nsslapd-localhost',
+                SER_PORT: 'nsslapd-port',
+                SER_SECURE_PORT: 'nsslapd-secureport',
+                SER_ROOT_DN: 'nsslapd-rootdn',
+                #SER_ROOT_PW         (bindpw) We can't do this
+                SER_CREATION_SUFFIX: 'nsslapd-defaultnamingcontext',
+                SER_USER_ID: 'nsslapd-localuser',
+                #SER_SERVERID_PROP   (serverid) Already have this set in other areas.
+                #SER_GROUP_ID        (groupid) ???
+                #SER_DEPLOYED_DIR    (prefix) Already provided to do the discovery
+                #SER_BACKUP_INST_DIR (backupdir) nsslapd-bakdir <<-- maybe?
+}
-- 
2.4.3

--
389-devel mailing list
389-devel@xxxxxxxxxxxxxxxxxxxxxxx
https://admin.fedoraproject.org/mailman/listinfo/389-devel

[Index of Archives]     [Fedora Directory Announce]     [Fedora Users]     [Older Fedora Users Mail]     [Fedora Advisory Board]     [Fedora Security]     [Fedora Devel Java]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Mentors]     [Fedora Package Review]     [Fedora Art]     [Fedora Music]     [Fedora Packaging]     [CentOS]     [Fedora SELinux]     [Big List of Linux Books]     [KDE Users]     [Fedora Art]     [Fedora Docs]

  Powered by Linux