From 4fd73c5d9a431dec0ac8189d6dee6e592088e3c6 Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Nov 26 2018 17:46:17 +0000 Subject: Ticket 50022, 50012, 49956, and 49800: Various dsctl/dscreate fixes Description: Fix 50022 - Confusing command line switches for dscreate and dsctl Fix 50012 - Add option to dsctl to remove all instances Fix 49956 - dsctl: add an option to list all available instances Fix 49800 - Debug messages "OK user/group dirsrv exists" are emitted when lib389 cli tools are used https://pagure.io/389-ds-base/issue/50022 https://pagure.io/389-ds-base/issue/50012 https://pagure.io/389-ds-base/issue/49956 https://pagure.io/389-ds-base/issue/49800 Reviewed by: spichugi(Thanks!) --- diff --git a/docker/389ds_poc/Dockerfile b/docker/389ds_poc/Dockerfile index 216e1de..d61df8c 100644 --- a/docker/389ds_poc/Dockerfile +++ b/docker/389ds_poc/Dockerfile @@ -33,7 +33,7 @@ RUN dnf install -y 389-ds-base/dist/rpms/*389*.rpm && \ # Create the example setup inf. It's valid for containers! # Build the instance from the new installer tools. -RUN /usr/sbin/dscreate create-template > /root/ds-setup.inf && /usr/sbin/dscreate -v fromfile /root/ds-setup.inf --containerised +RUN /usr/sbin/dscreate create-template > /root/ds-setup.inf && /usr/sbin/dscreate -v from-file /root/ds-setup.inf --containerised # Finally add the volumes, they will inherit the contents of these directories. VOLUME /etc/dirsrv diff --git a/src/cockpit/389-console/src/servers.js b/src/cockpit/389-console/src/servers.js index 9ce92cf..1b72453 100644 --- a/src/cockpit/389-console/src/servers.js +++ b/src/cockpit/389-console/src/servers.js @@ -1405,7 +1405,7 @@ $(document).ready( function() { $("#remove-server-btn").on("click", function () { popup_confirm("Are you sure you want to this remove instance: " + server_id + "", "Confirmation", function (yes) { if (yes) { - var cmd = [DSCTL, server_id, "remove", "--doit"]; + var cmd = [DSCTL, server_id, "remove", "--do-it"]; $("#ds-remove-inst").html(" Removing instance " + server_id + "..."); $("#remove-instance-form").modal('toggle'); log_cmd('#remove-server-btn (click)', 'Remove instance', cmd); @@ -1581,7 +1581,7 @@ $(document).ready( function() { /* * Next, create the instance... */ - cmd = [DSCREATE, 'fromfile', setup_file]; + cmd = [DSCREATE, 'from-file', setup_file]; cockpit.spawn(cmd, { superuser: true, "err": "message", "environ": [ENV] }).fail(function(ex) { // Failed to create the new instance! cockpit.spawn(rm_cmd, { superuser: true }); // Remove Inf file with clear text password diff --git a/src/lib389/cli/dscreate b/src/lib389/cli/dscreate index a482478..d536694 100755 --- a/src/lib389/cli/dscreate +++ b/src/lib389/cli/dscreate @@ -24,7 +24,7 @@ parser.add_argument('-v', '--verbose', action='store_true', default=False, dest='verbose') subparsers = parser.add_subparsers(help="action") -fromfile_parser = subparsers.add_parser('fromfile', help="Create an instance of Directory Server from an inf answer file") +fromfile_parser = subparsers.add_parser('from-file', help="Create an instance of Directory Server from an inf answer file") fromfile_parser.add_argument('file', help="Inf file to use with prepared answers. You can generate an example of this with 'dscreate create-template'") fromfile_parser.add_argument('-n', '--dryrun', help="Validate system and configurations only. Do not alter the system.", action='store_true', default=False) diff --git a/src/lib389/cli/dsctl b/src/lib389/cli/dsctl index 1148565..3c97fdc 100755 --- a/src/lib389/cli/dsctl +++ b/src/lib389/cli/dsctl @@ -14,24 +14,35 @@ import argparse, argcomplete import logging import sys import signal +from lib389.utils import get_instance_list from lib389.cli_base import _get_arg from lib389 import DirSrv from lib389.cli_ctl import instance as cli_instance from lib389.cli_ctl import dbtasks as cli_dbtasks from lib389.cli_base import disconnect_instance, setup_script_logger +from lib389.cli_ctl.instance import instance_remove_all parser = argparse.ArgumentParser() parser.add_argument('-v', '--verbose', help="Display verbose operation tracing during command execution", action='store_true', default=False ) -parser.add_argument('instance', +parser.add_argument('instance', nargs='?', default=False, help="The name of the instance to act upon", ) parser.add_argument('-j', '--json', help="Return result in JSON object", default=False, action='store_true' ) +parser.add_argument('-l', '--list', + help="List available Directory Server instances", + default=False, action='store_true' + ) + +parser.add_argument('--remove-all', nargs="?", default=False, const=None, + help="Remove all instances of Directory Server (you can also provide an optional directory prefix for this argument)", + ) + subparsers = parser.add_subparsers(help="action") cli_instance.create_parser(subparsers) cli_dbtasks.create_parser(subparsers) @@ -56,6 +67,19 @@ if __name__ == '__main__': log.debug("Called with: %s", args) + if args.list: + insts = get_instance_list() + for inst in insts: + print(inst) + sys.exit(0) + elif args.remove_all is not False: + instance_remove_all(log, args) + sys.exit(0) + elif not args.instance: + log.error("error: the following arguments are required: instance") + parser.print_help() + sys.exit(1) + # Assert we have a resources to work on. if not hasattr(args, 'func'): log.error("No action provided, here is some --help.") @@ -63,7 +87,6 @@ if __name__ == '__main__': sys.exit(1) # Connect - # inst = None inst = DirSrv(verbose=args.verbose) result = True diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py index ce205e7..adeb512 100644 --- a/src/lib389/lib389/__init__.py +++ b/src/lib389/lib389/__init__.py @@ -1192,7 +1192,7 @@ class DirSrv(SimpleLDAPObject, object): @return None - @raise None + @raise ValueError ''' if self.status() is True: @@ -1230,7 +1230,7 @@ class DirSrv(SimpleLDAPObject, object): time.sleep(1) pid = pid_from_file(self.ds_paths.pid_file) if pid == 0 or pid is None: - raise ValueError + raise ValueError('Failed to start DS') # Wait while not pid_exists(pid) and count > 0: # It looks like DS changes the value in here at some point ... @@ -1240,7 +1240,7 @@ class DirSrv(SimpleLDAPObject, object): time.sleep(1) count -= 1 if not pid_exists(pid): - raise Exception("Failed to start DS") + raise ValueError("Failed to start DS") if post_open: self.open() @@ -1254,7 +1254,7 @@ class DirSrv(SimpleLDAPObject, object): @return None - @raise None + @raise ValueError ''' if self.status() is False: return @@ -1270,7 +1270,7 @@ class DirSrv(SimpleLDAPObject, object): count = timeout pid = pid_from_file(self.ds_paths.pid_file) if pid == 0 or pid is None: - raise ValueError + raise ValueError("Failed to stop DS") os.kill(pid, signal.SIGTERM) # Wait while pid_exists(pid) and count > 0: @@ -1328,7 +1328,7 @@ class DirSrv(SimpleLDAPObject, object): @return None - @raise None + @raise ValueError ''' self.stop(timeout) time.sleep(1) diff --git a/src/lib389/lib389/cli_ctl/instance.py b/src/lib389/lib389/cli_ctl/instance.py index bd5d37d..4825a61 100644 --- a/src/lib389/lib389/cli_ctl/instance.py +++ b/src/lib389/lib389/cli_ctl/instance.py @@ -8,8 +8,10 @@ from lib389._constants import * +from lib389 import DirSrv from lib389.tools import DirSrvTools from lib389.instance.setup import SetupDs +from lib389.utils import get_instance_list from lib389.instance.remove import remove_ds_instance from getpass import getpass import os @@ -35,21 +37,24 @@ def instance_list(inst, log, args): def instance_restart(inst, log, args): inst.restart(post_open=False) + log.info('Instance "{}" has been restarted'.format(inst.serverid)) def instance_start(inst, log, args): inst.start(post_open=False) + log.info('Instance "{}" has been started'.format(inst.serverid)) def instance_stop(inst, log, args): inst.stop() + log.info('Instance "{}" has been stopped'.format(inst.serverid)) def instance_status(inst, log, args): if inst.status() is True: - log.info("Instance is running") + log.info('Instance "{}" is running'.format(inst.serverid)) else: - log.info("Instance is not running") + log.info('Instance "{}" is not running'.format(inst.serverid)) def instance_create_interactive(inst, log, args): @@ -124,25 +129,54 @@ def instance_example(inst, log, args): return True +def instance_remove_all(log, args): + """Remove all instances - clean sweep! + """ + + inst_names = get_instance_list(args.remove_all) + if len(inst_names) > 0: + answer = input("Are you sure you want to remove all the Directory Server instances? (Yes/no): ") + if answer != 'Yes': + print("Aborted removal of all instances") + return + + # Do it! + list_inst = DirSrv(verbose=args.verbose) + insts = list_inst.list(all=True, serverid=inst_names[0]) + for inst in insts: + remove_inst = DirSrv(verbose=args.verbose) + remove_inst.allocate(inst) + try: + log.info("Removing instance: slapd-" + str(remove_inst.serverid)) + remove_ds_instance(remove_inst) + except Exception as e: + log.fatal('Failed to remove all instances: ' + str(e)) + sys.exit(1) + log.info('All instances have been successfully removed') + else: + print("No instances to remove") + + def instance_remove(inst, log, args): if not args.ack: - log.info("""Not removing: if you are sure, add --doit""") + # Some day do some type of dry-run validation? + log.info("""Not removing: if you are sure, add --do-it""") return True else: log.info(""" -About to remove instance %s! +About to remove instance (%s)! If this is not what you want, press ctrl-c now ... """ % inst.serverid) - for i in range(1, 6): - log.info('%s ...' % (5 - int(i))) - time.sleep(1) - log.info('Removing instance ...') - try: - remove_ds_instance(inst) - log.info('Completed instance removal') - except: - log.fatal('Instance removal failed') - return False + for i in range(1, 6): + log.info('%s ...' % (6 - int(i))) + time.sleep(1) + log.info('Removing instance ...') + try: + remove_ds_instance(inst) + log.info('Completed instance removal') + except: + log.fatal('Instance removal failed') + return False def create_parser(subcommands): @@ -163,8 +197,9 @@ def create_parser(subcommands): status_parser.set_defaults(func=instance_status) remove_parser = subcommands.add_parser('remove', help="Destroy an instance of Directory Server, and remove all data.") - remove_parser.add_argument('--doit', dest="ack", help="By default we do a dry run. This actually initiates the removal.", - action='store_true', default=False) remove_parser.set_defaults(func=instance_remove) + remove_parser.add_argument('--do-it', dest="ack", help="By default we do a dry run. This actually initiates the removal of the instance.", + action='store_true', default=False) + diff --git a/src/lib389/lib389/paths.py b/src/lib389/lib389/paths.py index df6334c..ef0ace9 100644 --- a/src/lib389/lib389/paths.py +++ b/src/lib389/lib389/paths.py @@ -86,6 +86,7 @@ CONFIG_MAP = { SECTION = 'slapd' + class Paths(object): def __init__(self, serverid=None, instance=None): """ diff --git a/src/lib389/lib389/tools.py b/src/lib389/lib389/tools.py index e37ca0b..919dc85 100644 --- a/src/lib389/lib389/tools.py +++ b/src/lib389/lib389/tools.py @@ -811,9 +811,9 @@ class DirSrvTools(object): """ try: grp.getgrnam(group) - print("OK group %s exists" % group) + log.debug("OK group %s exists" % group) except KeyError: - print("Adding group %s" % group) + log.debug("Adding group %s" % group) cmd = [GROUPADD, '-r', group] subprocess.Popen(cmd) @@ -824,9 +824,9 @@ class DirSrvTools(object): """ try: pwd.getpwnam(user) - print("OK user %s exists" % user) + log.debug("OK user %s exists" % user) except KeyError: - print("Adding user %s" % user) + log.debug("Adding user %s" % user) cmd = [USERADD, '-g', group, '-c', DEFAULT_USER_COMMENT, '-r', '-d', home, '-s', NOLOGIN, user] subprocess.Popen(cmd) diff --git a/src/lib389/lib389/utils.py b/src/lib389/lib389/utils.py index 02967b9..bfa5616 100644 --- a/src/lib389/lib389/utils.py +++ b/src/lib389/lib389/utils.py @@ -1110,3 +1110,19 @@ def get_ldapurl_from_serverid(instance): else: # Use LDAP return ("ldap://{}:{}".format(host, port), None) + + +def get_instance_list(prefix=None): + # List all server instances + conf_dir = (prefix or "") + "/etc/dirsrv/" + insts = [] + try: + for inst in os.listdir(conf_dir): + if inst.startswith('slapd-') and not inst.endswith('.removed'): + insts.append(inst) + except OSError as e: + log.error("Failed to check directory: {} - {}".format(conf_dir, str(e))) + insts.sort() + return insts + +