From 15cbc0cf03fc29d6ddbb9bac400c62639f91403a Mon Sep 17 00:00:00 2001
From: William Brown <firstyear@redhat.com>
Date: Mon, 15 Aug 2016 11:23:06 +1000
Subject: [PATCH 1/5] Ticket 48951 - dsadm and dsconf base files

Fix Description:  To unite all commands within lib389! Add the base poc of dsadm
and dsconf. dsadm is able to list instances running on the server.

These are based on arg parse, and are extensible, able to be tested with pytest
and take advantage of all the features of lib389.

https://fedorahosted.org/389/ticket/48951

Author: wibrown

Review by: ???
---
 cli/dsadm                         |  83 +++++++++++++++++++++++++++
 cli/dsconf                        |  74 ++++++++++++++++++++++++
 lib389/cli_adm/__init__.py        |   8 +++
 lib389/cli_adm/instance.py        |  61 ++++++++++++++++++++
 lib389/cli_base/__init__.py       | 115 ++++++++++++++++++++++++++++++++++++++
 lib389/cli_conf/__init__.py       |   8 +++
 lib389/clitools/ds_list_instances |  29 ----------
 lib389/clitools/ds_start          |  34 -----------
 lib389/clitools/ds_stop           |  34 -----------
 setup.py                          |  17 +-----
 10 files changed, 351 insertions(+), 112 deletions(-)
 create mode 100755 cli/dsadm
 create mode 100755 cli/dsconf
 create mode 100644 lib389/cli_adm/__init__.py
 create mode 100644 lib389/cli_adm/instance.py
 create mode 100644 lib389/cli_base/__init__.py
 create mode 100644 lib389/cli_conf/__init__.py
 delete mode 100755 lib389/clitools/ds_list_instances
 delete mode 100755 lib389/clitools/ds_start
 delete mode 100755 lib389/clitools/ds_stop

diff --git a/cli/dsadm b/cli/dsadm
new file mode 100755
index 0000000..7923ac4
--- /dev/null
+++ b/cli/dsadm
@@ -0,0 +1,83 @@
+#!/usr/bin/python3
+
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+import argparse
+import logging
+import sys
+
+from lib389 import DirSrv
+from lib389.cli_adm import instance as cli_instance
+from lib389.cli_base import disconnect_instance
+
+logging.basicConfig()
+log = logging.getLogger("dsadm")
+
+if __name__ == '__main__':
+
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument('-v', '--verbose',
+            help="Display verbose operation tracing during command execution",
+            action='store_true', default=False
+        )
+    # Should this actually be in the sub modules? That way they can set the requirements.
+    parser.add_argument('-Z', '--instance',
+            help="The name of the instance to act upon",
+            default=None,
+        )
+
+    subparsers = parser.add_subparsers(help="resources to act upon")
+
+    # We stack our needed options in via submodules.
+
+    cli_instance.create_parser(subparsers)
+
+    # Then we tell it to execute.
+
+    args = parser.parse_args()
+
+    if args.verbose:
+        log.setLevel(logging.DEBUG)
+    else:
+        log.setLevel(logging.INFO)
+
+    log.debug("389 Directory Server Project: dsadm")
+    # Leave this comment here: UofA let me take this code with me provided
+    # I gave attribution. -- wibrown
+    log.debug("Inspired by works of: ITS, The University of Adelaide")
+
+    log.debug("Called with: %s", args)
+
+    # Assert we have a resources to work on.
+    if not hasattr(args, 'func'):
+        log.error("No resource provided to act upon")
+        log.error("USAGE: dsadm [options] <resource> <action> [action options]")
+        sys.exit(1)
+
+    # Connect
+    # inst = None
+    inst = DirSrv(verbose=args.verbose)
+
+    if args.verbose:
+        # inst = connect_instance(args.ldapurl, args.binddn, args.verbose, args.starttls)
+        args.func(inst, log, args)
+    else:
+        try:
+            # inst = connect_instance(args.ldapurl, args.binddn, args.verbose, args.starttls)
+            args.func(inst, log, args)
+        except Exception as e:
+            log.debug(e, exc_info=True)
+            log.error("Error: %s" % e.message)
+    disconnect_instance(inst)
+
+    # Done!
+    log.debug("dsadm is brought to you by the letter R and the number 27.")
+
+
diff --git a/cli/dsconf b/cli/dsconf
new file mode 100755
index 0000000..fc1d1ff
--- /dev/null
+++ b/cli/dsconf
@@ -0,0 +1,74 @@
+#!/usr/bin/python3
+
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+import argparse
+import logging
+
+logging.basicConfig()
+log = logging.getLogger("dsconf")
+
+if __name__ == '__main__':
+
+    defbase = ldap.get_option(ldap.OPT_DEFBASE)
+
+    parser = argparse.ArgumentParser()
+    # Build the base ldap options, this keeps in unified.
+
+    parser.add_argument('-D', '--binddn',
+            help="The account to bind as for executing operations",
+            default=None,
+        )
+    parser.add_argument('-H', '--ldapurl',
+            help="The LDAP url to connect to, IE ldap://mai.example.com:389",
+            default=None,
+        )
+    parser.add_argument('-Z', '--starttls',
+            help="Connect with StartTLS",
+            default=False, action='store_true'
+        )
+    parser.add_argument('-v', '--verbose',
+            help="Display verbose operation tracing during command execution",
+            action='store_true', default=False
+        )
+
+    subparsers = parser.add_subparsers(help="resources to act upon")
+
+    args = parser.parse_args()
+
+    if args.verbose:
+        log.setLevel(logging.DEBUG)
+    else:
+        log.setLevel(logging.INFO)
+
+    log.debug("389 Directory Server Project: dsconf")
+    # Leave this comment here: UofA let me take this code with me provided
+    # I gave attribution. -- wibrown
+    log.debug("Inspired by works of: ITS, The University of Adelaide")
+
+    log.debug("Called with: %s", args)
+
+    # Connect
+    inst = None
+    if args.verbose:
+        inst = connect_instance(args.ldapurl, args.binddn, args.verbose, args.starttls)
+        args.func(inst, args.basedn, log, args)
+    else:
+        try:
+            inst = connect_instance(args.ldapurl, args.binddn, args.verbose, args.starttls)
+            args.func(inst, args.basedn, log, args)
+        except Exception as e:
+            log.debug(e, exc_info=True)
+            log.error("Error: %s" % e.message)
+    disconnect_instance(inst)
+
+    # Done!
+    log.debug("dsadm is brought to you by the letter H and the number 25.")
+
+
diff --git a/lib389/cli_adm/__init__.py b/lib389/cli_adm/__init__.py
new file mode 100644
index 0000000..d57ac33
--- /dev/null
+++ b/lib389/cli_adm/__init__.py
@@ -0,0 +1,8 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
diff --git a/lib389/cli_adm/instance.py b/lib389/cli_adm/instance.py
new file mode 100644
index 0000000..a5f6d87
--- /dev/null
+++ b/lib389/cli_adm/instance.py
@@ -0,0 +1,61 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+from lib389._constants import *
+from lib389.cli_base import _get_arg
+
+from lib389.tools import DirSrvTools
+
+def instance_list(inst, log, args):
+    instances = inst.list(all=True)
+
+    try:
+        for instance in instances:
+            print(instance[CONF_SERVER_ID])
+    except IOError as e:
+        log.info(e)
+        log.info("Perhaps you need to be a different user?")
+
+def instance_action(inst, log, args, act):
+    # Get the instance name if needed.
+    inst_id = _get_arg( args.instance, msg="Directory Server instance name to %s" % act)
+
+    # Allocate the instance based on name
+    insts = inst.list(serverid=inst_id)
+    if len(insts) != 1:
+        raise ValueError("No such instance %s" % inst_id)
+
+    inst.allocate(insts[0])
+
+    # Start it!
+    DirSrvTools.serverCmd(inst, act, True)
+
+def instance_start(inst, log, args):
+    instance_action(inst, log, args, "start")
+
+def instance_stop(inst, log, args):
+    instance_action(inst, log, args, "stop")
+
+def instance_create(inst, log, args):
+    pass
+
+def create_parser(subparsers):
+    instance_parser = subparsers.add_parser('instance', help="Manager instances of Directory Server")
+
+    subcommands = instance_parser.add_subparsers(help="action")
+
+    list_parser = subcommands.add_parser('list', help="List installed instances of Directory Server")
+    list_parser.set_defaults(func=instance_list)
+
+    start_parser = subcommands.add_parser('start', help="Start an instance of Directory Server, if it is not currently running")
+    start_parser.add_argument('instance', nargs=1, help="The name of the instance to start.")
+    start_parser.set_defaults(func=instance_start)
+
+    stop_parser = subcommands.add_parser('stop', help="Stop an instance of Directory Server, if it is currently running")
+    stop_parser.add_argument('instance', nargs=1, help="The name of the instance to stop.")
+    stop_parser.set_defaults(func=instance_stop)
diff --git a/lib389/cli_base/__init__.py b/lib389/cli_base/__init__.py
new file mode 100644
index 0000000..44c2b89
--- /dev/null
+++ b/lib389/cli_base/__init__.py
@@ -0,0 +1,115 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+from getpass import getpass
+from lib389 import DirSrv
+from lib389.properties import SER_LDAP_URL, SER_ROOT_DN, SER_ROOT_PW
+
+
+def _get_arg(args, msg=None):
+    if args is not None and len(args) > 0:
+        if type(args) is list:
+            return args[0]
+        else:
+            return args
+    else:
+        return raw_input("%s : " % msg)
+
+def _get_args(args, kws):
+    kwargs = {}
+    while len(kws) > 0:
+        kw, msg, priv = kws.pop(0)
+
+        if args is not None and len(args) > 0:
+            kwargs[kw] = args.pop(0)
+        else:
+            if priv:
+                kwargs[kw] = getpass("%s : " % msg)
+            else:
+                kwargs[kw] = raw_input("%s : " % msg)
+    return kwargs
+
+# This is really similar to get_args, but generates from an array
+def _get_attributes(args, attrs):
+    kwargs = {}
+    for attr in attrs:
+        if args is not None and len(args) > 0:
+            kwargs[attr] = args.pop(0)
+        else:
+            if attr.lower() == 'userpassword':
+                kwargs[attr] = getpass("Enter value for %s : " % attr)
+            else:
+                kwargs[attr] = raw_input("Enter value for %s : " % attr)
+    return kwargs
+
+
+def _warn(data, msg=None):
+    if msg is not None:
+        print("%s :" % msg)
+    if 'Yes I am sure' != raw_input("Type 'Yes I am sure' to continue: "):
+        raise Exception("Not sure if want")
+    return data
+
+# We'll need another of these that does a "connect via instance name?"
+def connect_instance(ldapurl, binddn, verbose, starttls):
+    dsargs = {
+        SER_LDAP_URL: ldapurl,
+        SER_ROOT_DN: binddn
+    }
+    ds = DirSrv(verbose=verbose)
+    ds.allocate(dsargs)
+    if not ds.can_autobind() and binddn is not None:
+        dsargs[SER_ROOT_PW] = getpass("Enter password for %s on %s : " % (binddn, ldapurl))
+    elif binddn is None:
+        raise Exception("Must provide a binddn to connect with")
+    ds.allocate(dsargs)
+    ds.open(starttls=starttls)
+    print("")
+    return ds
+
+def disconnect_instance(inst):
+    if inst is not None:
+        inst.close()
+
+def _generic_list(inst, basedn, log, manager_class, **kwargs):
+    mc = manager_class(inst, basedn)
+    ol = mc.list()
+    if len(ol) == 0:
+        print("No objects to display")
+    elif len(ol) > 0:
+        # We might sort this in the future
+        for o in ol:
+            o_str = o.__unicode__()
+            print(o_str)
+
+# Display these entries better!
+def _generic_get(inst, basedn, log, manager_class, selector):
+    mc = manager_class(inst, basedn)
+    o = mc.get(selector)
+    o_str = o.__unicode__()
+    print(o_str)
+
+def _generic_get_dn(inst, basedn, log, manager_class, dn):
+    mc = manager_class(inst, basedn)
+    o = mc.get(dn=dn)
+    o_str = o.__unicode__()
+    print(o_str)
+
+def _generic_create(inst, basedn, log, manager_class, kwargs):
+    mc = manager_class(inst, basedn)
+    o = mc.create(properties=kwargs)
+    o_str = o.__unicode__()
+    print('Sucessfully created %s' % o_str)
+
+def _generic_delete(inst, basedn, log, object_class, dn):
+    # Load the oc direct
+    o = object_class(inst, dn)
+    o.delete()
+    print('Sucessfully deleted %s' % dn)
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
diff --git a/lib389/cli_conf/__init__.py b/lib389/cli_conf/__init__.py
new file mode 100644
index 0000000..d57ac33
--- /dev/null
+++ b/lib389/cli_conf/__init__.py
@@ -0,0 +1,8 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2016 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
diff --git a/lib389/clitools/ds_list_instances b/lib389/clitools/ds_list_instances
deleted file mode 100755
index 5d5c07c..0000000
--- a/lib389/clitools/ds_list_instances
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/python
-
-# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2015 Red Hat, Inc.
-# All rights reserved.
-#
-# License: GPL (version 3 or any later version).
-# See LICENSE for details.
-# --- END COPYRIGHT BLOCK ---
-
-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
-        try:
-            instances = self.ds.list(all=True)
-            print('Instances on this system:')
-            for instance in instances:
-                print(instance[CONF_SERVER_ID])
-        except IOError as e:
-            print(e)
-            print("Perhaps you need to be a different user?")
-
-if __name__ == '__main__':
-    listtool = ListTool()
-    listtool.list_instances()
diff --git a/lib389/clitools/ds_start b/lib389/clitools/ds_start
deleted file mode 100755
index 143051c..0000000
--- a/lib389/clitools/ds_start
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/python
-
-# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2015 Red Hat, Inc.
-# All rights reserved.
-#
-# License: GPL (version 3 or any later version).
-# See LICENSE for details.
-# --- END COPYRIGHT BLOCK ---
-
-# from clitools import clitools_parser, get_instance_dict, get_rootdn_pass
-from lib389.clitools import CliTool, clitools_parser
-# from lib389 import DirSrv
-from lib389._constants import *
-from lib389.tools import DirSrvTools
-
-
-class StartTool(CliTool):
-    def start(self):
-        try:
-            self.populate_instance_dict(self.args.instance)
-            self.ds.allocate(self.inst)
-
-            DirSrvTools.serverCmd(self.ds, "start", True)
-        finally:
-            pass
-            # self.disconnect()
-
-if __name__ == '__main__':
-    # Do some arg parse stuff
-    # You can always add a child parser here too ...
-    args = clitools_parser.parse_args()
-    tool = StartTool(args)
-    tool.start()
diff --git a/lib389/clitools/ds_stop b/lib389/clitools/ds_stop
deleted file mode 100755
index 7988c8b..0000000
--- a/lib389/clitools/ds_stop
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/python
-
-# --- BEGIN COPYRIGHT BLOCK ---
-# Copyright (C) 2015 Red Hat, Inc.
-# All rights reserved.
-#
-# License: GPL (version 3 or any later version).
-# See LICENSE for details.
-# --- END COPYRIGHT BLOCK ---
-
-# from clitools import clitools_parser, get_instance_dict, get_rootdn_pass
-from lib389.clitools import CliTool, clitools_parser
-# from lib389 import DirSrv
-from lib389._constants import *
-from lib389.tools import DirSrvTools
-
-
-class StartTool(CliTool):
-    def start(self):
-        try:
-            self.populate_instance_dict(self.args.instance)
-            self.ds.allocate(self.inst)
-
-            DirSrvTools.serverCmd(self.ds, "stop", True)
-        finally:
-            pass
-            # self.disconnect()
-
-if __name__ == '__main__':
-    # Do some arg parse stuff
-    # You can always add a child parser here too ...
-    args = clitools_parser.parse_args()
-    tool = StartTool(args)
-    tool.start()
diff --git a/setup.py b/setup.py
index ae656f3..0582b25 100644
--- a/setup.py
+++ b/setup.py
@@ -53,21 +53,8 @@ setup(
     data_files=[
         ('/usr/sbin/', [
             # 'lib389/clitools/ds_setup',
-            'lib389/clitools/ds_aci_lint',
-            'lib389/clitools/ds_list_instances',
-            'lib389/clitools/ds_monitor_backend',
-            'lib389/clitools/ds_monitor_server',
-            'lib389/clitools/ds_schema_attributetype_list',
-            'lib389/clitools/ds_schema_attributetype_query',
-            'lib389/clitools/ds_start',
-            'lib389/clitools/ds_stop',
-            'lib389/clitools/ds_krb_create_keytab',
-            'lib389/clitools/ds_krb_create_principal',
-            'lib389/clitools/ds_krb_create_realm',
-            'lib389/clitools/ds_krb_destroy_realm',
-            'lib389/clitools/ds_backend_getattr',
-            'lib389/clitools/ds_backend_setattr',
-            'lib389/clitools/ds_backend_list',
+            'cli/dsadm',
+            'cli/dsconf',
             ]),
     ],
 
-- 
1.8.3.1

