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

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

 



> > > 
> > 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.
> This will be very useful.  Things like missing objectclasses for 
> "memberOf", and such things, always seem to pop up, so this can 
> definitely be used on that front.

Certainly. At my workplace, everyone always forgets what objectClass can hold
the attribute "userClass" which we use for some applications. It's great to be
able to look these up.

> > 
> > 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.
> I "think" the current form is fine, the lib389 logging is more for how 
> the tool is "running" (for troubleshooting, etc), and not what the tool 
> should be "doing".  Does that make sense :-)  Perhaps others on this 
> list might have a different opinion.
> 
> I'll give this some time, and if no one has any concerns/comments I'll 
> push the patch.

Okay, if this looks good I'll leave it. I'll certainly polish and improve many
aspects of it in the future, but if you are happy with it as is, I'll leave the
decision to commit to you.

I have attached an updated version that is rebased against current master.

Sincerely,


-- 
William Brown <william@xxxxxxxxxxxxxxxx>
From 5307b955ceebda838cc3724cd6beb4adbb68ed31 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                        | 47 ++++++++++++++++++++++++++-----
 lib389/_constants.py                      | 15 ++++++++++
 6 files changed, 171 insertions(+), 7 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 111b74e..b6eed04 100644
--- a/lib389/__init__.py
+++ b/lib389/__init__.py
@@ -478,7 +478,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
@@ -496,6 +496,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
@@ -510,7 +512,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
@@ -522,6 +524,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>]
@@ -552,6 +557,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):
@@ -572,6 +602,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):]
 
@@ -582,7 +613,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:
@@ -590,7 +621,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
 
@@ -609,6 +640,8 @@ class DirSrv(SimpleLDAPObject):
 
         # 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')
@@ -640,7 +673,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:
@@ -655,7 +688,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:
@@ -2355,4 +2388,4 @@ class DirSrv(SimpleLDAPObject):
                 os.chown(ldif_file, uid, gid)
         except OSError as e:
             log.exception('Failed to create ldif file (%s): error %d - %s' % (ldif_file, e.errno, e.strerror))
-            raise e
\ No newline at end of file
+            raise e
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