Commit 5226bf9 Ticket 49712 - lib389 CLI tools should return a result code on failures

16 files Authored and Committed by mreynolds 7 days ago
Ticket 49712 - lib389 CLI tools should return a result code on failures

Description:  I've also included the work for 49775 in this patch since
              there was a lot of overldap.

              For dsctl functions we need to check for True and False in
              order to detect an error.  For dsconf & dsidm we need to
              catch exceptions.  Once an error is detected we return error
              code (1).

              The changes for 49775 was to use the default archive directory
              if one was not specified to db2bak, and use the default ldif
              location for db2ldif.  This how the old tools worked, no
              reason not to carry over this convenience.  Also the format
              used for the file name (Instance name + Date/Time) is the same
              as the old cli tools.

              Also did some pep8 cleanup.

https://pagure.io/389-ds-base/issue/49712

Reviewed by: spichugi(Thanks!)

    
1 @@ -366,6 +366,7 @@
2               slapi_task_log_notice(task, "mkdir(%s) failed; errno %i (%s)",
3                                     directory, errno, msg ? msg : "unknown");
4           }
5 +         return_value = -1;
6           goto err;
7       }
8   
 1 @@ -82,7 +82,7 @@
 2       cli_whoami.create_parser(subparsers)
 3       cli_referint.create_parser(subparsers)
 4       cli_automember.create_parser(subparsers)
 5 -     
 6 + 
 7       args = parser.parse_args()
 8   
 9       log = reset_get_logger('dsconf', args.verbose)
10 @@ -111,26 +111,26 @@
11       # Connect
12       # We don't need a basedn, because the config objects derive it properly
13       inst = None
14 -     if args.verbose:
15 +     result = False
16 +     try:
17           inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose)
18 -         args.func(inst, None, log, args)
19 -         if not args.json:
20 +         result = args.func(inst, None, log, args)
21 +         if args.verbose:
22               log.info("Command successful.")
23 -     else:
24 -         try:
25 -             inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose)
26 -             args.func(inst, None, log, args)
27 -             if not args.json:
28 -                 log.info("Command successful.")
29 -         except ldap.LDAPError as e:
30 -             #log.debug(e, exc_info=True)
31 -             if args and args.json:
32 -                 print(e)
33 -             else:
34 -                 log.error("Error!: %s" % str(e))
35 +     except Exception as e:
36 +         log.debug(e, exc_info=True)
37 +         if args and args.json:
38 +             print(e)
39 +         else:
40 +             log.error("Error!: %s" % str(e))
41 +         result = False
42 + 
43       disconnect_instance(inst)
44   
45       # Done!
46       log.debug("dsconf is brought to you by the letter H and the number 25.")
47   
48 +     if result is False:
49 +         sys.exit(1)
50 + 
51   
 1 @@ -29,13 +29,15 @@
 2   
 3       fromfile_parser = subparsers.add_parser('fromfile', help="Create an instance of Directory Server from an inf answer file")
 4       fromfile_parser.add_argument('file', help="Inf file to use with prepared answers. You can generate an example of this with 'dscreate example'")
 5 -     fromfile_parser.add_argument('-n', '--dryrun', help="Validate system and configurations only. Do not alter the system.", action='store_true', default=False)
 6 +     fromfile_parser.add_argument('-n', '--dryrun', help="Validate system and configurations only. Do not alter the system.",
 7 +                                  action='store_true', default=False)
 8       fromfile_parser.add_argument('--IsolemnlyswearthatIamuptonogood', dest="ack",
 9 -                         help="""You are here likely here by mistake! You want setup-ds.pl!
10 +                                  help="""You are here likely here by mistake! You want setup-ds.pl!
11   By setting this value you acknowledge and take responsibility for the fact this command is UNTESTED and NOT READY. You are ON YOUR OWN!
12   """,
13 -                         action='store_true', default=False)
14 -     fromfile_parser.add_argument('-c', '--containerised', help="Indicate to the installer that this is running in a container. Used to disable systemd native components, even if they are installed.", action='store_true', default=False)
15 +                                  action='store_true', default=False)
16 +     fromfile_parser.add_argument('-c', '--containerised', help="Indicate to the installer that this is running in a container. Used to disable systemd native components, even if they are installed.",
17 +                                  action='store_true', default=False)
18       fromfile_parser.set_defaults(func=cli_instance.instance_create)
19   
20       example_parser = subparsers.add_parser('example', help="Display an example ini answer file, with comments")
21 @@ -62,19 +64,12 @@
22   
23       result = False
24   
25 -     if args.verbose:
26 +     try:
27           result = args.func(inst, log, args)
28 -     else:
29 -         try:
30 -             result = args.func(inst, log, args)
31 -         except Exception as e:
32 -             log.debug(e, exc_info=True)
33 -             log.error("Error: %s" % str(e))
34 - 
35 -     if result is True:
36 -         log.info('FINISH: Command succeeded')
37 -     elif result is False:
38 -         log.info('FAIL: Command failed. See output for details.')
39 +     except Exception as e:
40 +         log.debug(e, exc_info=True)
41 +         log.error("Error: %s" % str(e))
42 +         result = False
43   
44       # Done!
45       log.debug("dscreate is brought to you by the letter S and the number 22.")
 1 @@ -86,23 +86,14 @@
 2       inst.allocate(insts[0])
 3       log.debug('Instance allocated')
 4   
 5 -     if args.verbose:
 6 +     try:
 7           result = args.func(inst, log, args)
 8 -         log.info("Command successful.")
 9 -     else:
10 -         try:
11 -             result = args.func(inst, log, args)
12 -             log.info("Command successful.")
13 -         except Exception as e:
14 -             log.debug(e, exc_info=True)
15 -             log.error("Error: %s" % str(e))
16 +     except Exception as e:
17 +         log.debug(e, exc_info=True)
18 +         log.error("Error: %s" % str(e))
19 +         result = False
20       disconnect_instance(inst)
21   
22 -     if result is True:
23 -         log.info('FINISH: Command succeeded')
24 -     elif result is False:
25 -         log.info('FAIL: Command failed. See output for details.')
26 - 
27       # Done!
28       log.debug("dsctl is brought to you by the letter R and the number 27.")
29   
 1 @@ -104,21 +104,20 @@
 2   
 3       # Connect
 4       inst = None
 5 -     if args.verbose:
 6 +     result = False
 7 +     try:
 8           inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose)
 9 -         args.func(inst, dsrc_inst['basedn'], log, args)
10 -         log.info("Command successful.")
11 -     else:
12 -         try:
13 -             inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose)
14 -             args.func(inst, dsrc_inst['basedn'], log, args)
15 +         result = args.func(inst, dsrc_inst['basedn'], log, args)
16 +         if args.verbose:
17               log.info("Command successful.")
18 -         except Exception as e:
19 -             log.debug(e, exc_info=True)
20 -             log.error("Error: %s" % str(e))
21 -     disconnect_instance(inst)
22 +     except Exception as e:
23 +         log.debug(e, exc_info=True)
24 +         log.error("Error: %s" % str(e))
25 +         result = False
26   
27 +     disconnect_instance(inst)
28   
29       log.debug("dsidm is brought to you by the letter E and the number 26.")
30   
31 - 
32 +     if result is False:
33 +         sys.exit(1)
  1 @@ -1663,7 +1663,7 @@
  2   
  3       def get_ldaps_uri(self):
  4           """Return what our ldaps (TLS) uri would be for this instance
  5 -         
  6 + 
  7           :returns: The string of the servers ldaps (TLS) uri.
  8           """
  9           return 'ldaps://%s:%s' % (self.host, self.sslport)
 10 @@ -1674,7 +1674,7 @@
 11   
 12       def get_ldap_uri(self):
 13           """Return what our ldap uri would be for this instance
 14 -         
 15 + 
 16           :returns: The string of the servers ldap uri.
 17           """
 18           return 'ldap://%s:%s' % (self.host, self.port)
 19 @@ -2777,8 +2777,13 @@
 20           if encrypt:
 21               cmd.append('-E')
 22   
 23 -         result = subprocess.check_output(cmd)
 24 -         u_result = ensure_str(result)
 25 +         try:
 26 +             result = subprocess.check_output(cmd, stderr=subprocess.STDOUT, encoding='utf-8')
 27 +             u_result = ensure_str(result)
 28 +         except subprocess.CalledProcessError as e:
 29 +             self.log.debug("Command: {} failed with the return code {} and the error {}".format(
 30 +                            format_cmd_list(cmd), e.returncode, e.output))
 31 +             return False
 32   
 33           self.log.debug("ldif2db output: BEGIN")
 34           for line in u_result.split("\n"):
 35 @@ -2830,12 +2835,24 @@
 36               cmd.append('-E')
 37           if repl_data:
 38               cmd.append('-r')
 39 -         if outputfile:
 40 +         if outputfile is not None:
 41               cmd.append('-a')
 42               cmd.append(outputfile)
 43 - 
 44 -         result = subprocess.check_output(cmd)
 45 -         u_result = ensure_str(result)
 46 +         else:
 47 +             # No output file specified.  Use the default ldif location/name
 48 +             cmd.append('-a')
 49 +             if bename:
 50 +                 ldifname = "/" + self.serverid + "-" + bename + "-" + datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
 51 +             else:
 52 +                 ldifname = "/" + self.serverid + "-" + datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
 53 +             cmd.append(self.get_ldif_dir() + ldifname)
 54 +         try:
 55 +             result = subprocess.check_output(cmd, stderr=subprocess.STDOUT, encoding='utf-8')
 56 +             u_result = ensure_str(result)
 57 +         except subprocess.CalledProcessError as e:
 58 +             self.log.debug("Command: {} failed with the return code {} and the error {}".format(
 59 +                            format_cmd_list(cmd), e.returncode, e.output))
 60 +             return False
 61   
 62           self.log.debug("db2ldif output: BEGIN")
 63           for line in u_result.split("\n"):
 64 @@ -2861,13 +2878,18 @@
 65               self.log.error("bak2db: backup directory missing")
 66               return False
 67   
 68 -         result = subprocess.check_output([
 69 -             prog,
 70 -             'archive2db',
 71 -             '-a', archive_dir,
 72 -             '-D', self.get_config_dir()
 73 -         ])
 74 -         u_result = ensure_str(result)
 75 +         try:
 76 +             result = subprocess.check_output([
 77 +                 prog,
 78 +                 'archive2db',
 79 +                 '-a', archive_dir,
 80 +                 '-D', self.get_config_dir()
 81 +             ], stderr=subprocess.STDOUT, encoding='utf-8')
 82 +             u_result = ensure_str(result)
 83 +         except subprocess.CalledProcessError as e:
 84 +             self.log.debug("Command: {} failed with the return code {} and the error {}".format(
 85 +                            format_cmd_list(cmd), e.returncode, e.output))
 86 +             return False
 87   
 88           self.log.debug("bak2db output: BEGIN")
 89           for line in u_result.split("\n"):
 90 @@ -2888,17 +2910,24 @@
 91               self.log.error("db2bak: Can not operate while directory server is running")
 92               return False
 93   
 94 -         if not archive_dir:
 95 -             self.log.error("db2bak: archive directory missing")
 96 -             return False
 97 +         if archive_dir is None:
 98 +             # Use the instance name and date/time as the default backup name
 99 +             archive_dir = self.get_bak_dir() + "/" + self.serverid + "-" + datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
100 +         elif archive_dir[0] != "/":
101 +             # Relative path, append it to the bak directory
102 +             archive_dir = self.get_bak_dir() + "/" + archive_dir
103   
104 -         result = subprocess.check_output([
105 -             prog,
106 -             'db2archive',
107 -             '-a', archive_dir,
108 -             '-D', self.get_config_dir()
109 -         ])
110 -         u_result = ensure_str(result)
111 +         try:
112 +             result = subprocess.check_output([
113 +                 prog,
114 +                 'db2archive',
115 +                 '-a', archive_dir,
116 +                 '-D', self.get_config_dir()
117 +             ], stderr=subprocess.STDOUT, encoding='utf-8')
118 +             u_result = ensure_str(result)
119 +         except subprocess.CalledProcessError as e:
120 +             self.log.debug("Command: {} failed with the return code {} and the error {}".format(
121 +                         format_cmd_list(cmd), e.returncode, e.output))
122   
123           self.log.debug("db2bak output: BEGIN")
124           for line in u_result.split("\n"):
125 @@ -2937,7 +2966,6 @@
126           cmd.append('-D')
127           cmd.append(self.get_config_dir())
128   
129 - 
130           if bename:
131               cmd.append('-n')
132               cmd.append(bename)
133 @@ -2957,15 +2985,20 @@
134               cmd.append('-T')
135               cmd.append(vlvTag)
136   
137 -         result = subprocess.check_output(cmd)
138 -         u_result = ensure_str(result)
139 +         try:
140 +             result = subprocess.check_output(cmd, stderr=subprocess.STDOUT, encoding='utf-8')
141 +             u_result = ensure_str(result)
142 +         except subprocess.CalledProcessError as e:
143 +             self.log.debug("Command: {} failed with the return code {} and the error {}".format(
144 +                            format_cmd_list(cmd), e.returncode, e.output))
145 +             return False
146   
147           self.log.debug("db2index output: BEGIN")
148           for line in u_result.split("\n"):
149               self.log.debug(line)
150           self.log.debug("db2index output: END")
151   
152 -         return result
153 +         return True
154   
155       def dbscan(self, bename=None, index=None, key=None, width=None, isRaw=False):
156           """Wrapper around dbscan tool that analyzes and extracts information
  1 @@ -17,6 +17,7 @@
  2   
  3   MAJOR, MINOR, _, _, _ = sys.version_info
  4   
  5 + 
  6   # REALLY PYTHON 3? REALLY???
  7   def _input(msg):
  8       if MAJOR >= 3:
  9 @@ -43,6 +44,7 @@
 10           else:
 11               return _input("%s : " % msg)
 12   
 13 + 
 14   def _get_args(args, kws):
 15       kwargs = {}
 16       while len(kws) > 0:
 17 @@ -57,6 +59,7 @@
 18                   kwargs[kw] = _input("%s : " % msg)
 19       return kwargs
 20   
 21 + 
 22   # This is really similar to get_args, but generates from an array
 23   def _get_attributes(args, attrs):
 24       kwargs = {}
 25 @@ -81,6 +84,7 @@
 26           raise Exception("Not sure if want")
 27       return data
 28   
 29 + 
 30   # We'll need another of these that does a "connect via instance name?"
 31   def connect_instance(dsrc_inst, verbose):
 32       dsargs = dsrc_inst['args']
 33 @@ -99,14 +103,17 @@
 34               starttls=dsrc_inst['starttls'], connOnly=True)
 35       return ds
 36   
 37 + 
 38   def disconnect_instance(inst):
 39       if inst is not None:
 40           inst.close()
 41   
 42 + 
 43   def populate_attr_arguments(parser, attributes):
 44       for attr in attributes:
 45           parser.add_argument('--%s' % attr, nargs='?', help="Value of %s" % attr)
 46   
 47 + 
 48   def _generic_list(inst, basedn, log, manager_class, args=None):
 49       mc = manager_class(inst, basedn)
 50       ol = mc.list()
 51 @@ -128,30 +135,33 @@
 52           if args and args.json:
 53               print(json.dumps(json_result))
 54   
 55 + 
 56   # Display these entries better!
 57   def _generic_get(inst, basedn, log, manager_class, selector, args=None):
 58       mc = manager_class(inst, basedn)
 59       if args and args.json:
 60            o = mc.get(selector, json=True)
 61 -          print(o);
 62 +          print(o)
 63       else:
 64           o = mc.get(selector)
 65           o_str = o.display()
 66           log.info(o_str)
 67   
 68 + 
 69   def _generic_get_dn(inst, basedn, log, manager_class, dn, args=None):
 70       mc = manager_class(inst, basedn)
 71       o = mc.get(dn=dn)
 72       o_str = o.display()
 73       log.info(o_str)
 74   
 75 + 
 76   def _generic_create(inst, basedn, log, manager_class, kwargs, args=None):
 77       mc = manager_class(inst, basedn)
 78       o = mc.create(properties=kwargs)
 79       o_str = o.__unicode__()
 80 - 
 81       log.info('Successfully created %s' % o_str)
 82   
 83 + 
 84   def _generic_delete(inst, basedn, log, object_class, dn, args=None):
 85       # Load the oc direct
 86       o = object_class(inst, dn)
 87 @@ -190,6 +200,7 @@
 88       def flush(self):
 89           self.outputs = []
 90   
 91 + 
 92   class FakeArgs(object):
 93       def __init__(self):
 94           pass
 95 @@ -207,6 +218,7 @@
 96       logging.Formatter('%(levelname)s: %(message)s')
 97   )
 98   
 99 + 
100   def reset_get_logger(name, verbose=False):
101       """Reset the python logging system for STDOUT, and attach a new
102       console logger with cli expected formatting.
 1 @@ -7,8 +7,6 @@
 2   # --- END COPYRIGHT BLOCK ---
 3   
 4   from lib389.backend import Backend, Backends
 5 - import argparse
 6 - 
 7   from lib389.cli_base import (
 8       populate_attr_arguments,
 9       _generic_list,
10 @@ -26,24 +24,28 @@
11   MANY = Backends
12   RDN = 'cn'
13   
14 + 
15   def backend_list(inst, basedn, log, args):
16       _generic_list(inst, basedn, log.getChild('backend_list'), MANY, args)
17   
18 + 
19   def backend_get(inst, basedn, log, args):
20 -     rdn = _get_arg( args.selector, msg="Enter %s to retrieve" % RDN)
21 +     rdn = _get_arg(args.selector, msg="Enter %s to retrieve" % RDN)
22       _generic_get(inst, basedn, log.getChild('backend_get'), MANY, rdn, args)
23   
24 + 
25   def backend_get_dn(inst, basedn, log, args):
26 -     dn = _get_arg( args.dn, msg="Enter dn to retrieve")
27 +     dn = _get_arg(args.dn, msg="Enter dn to retrieve")
28       _generic_get_dn(inst, basedn, log.getChild('backend_get_dn'), MANY, dn, args)
29   
30 + 
31   def backend_create(inst, basedn, log, args):
32 -     args.json = True
33       kwargs = _get_attributes(args, Backend._must_attributes)
34       _generic_create(inst, basedn, log.getChild('backend_create'), MANY, kwargs, args)
35   
36 + 
37   def backend_delete(inst, basedn, log, args, warn=True):
38 -     dn = _get_arg( args.dn, msg="Enter dn to delete")
39 +     dn = _get_arg(args.dn, msg="Enter dn to delete")
40       if warn:
41           _warn(dn, msg="Deleting %s %s" % (SINGULAR.__name__, dn))
42       _generic_delete(inst, basedn, log.getChild('backend_delete'), SINGULAR, dn, args)
 1 @@ -7,19 +7,17 @@
 2   # --- END COPYRIGHT BLOCK ---
 3   
 4   from lib389.idm.directorymanager import DirectoryManager
 5 - 
 6   from lib389.cli_base import _get_arg
 7   
 8 - import argparse
 9   
10   def password_change(inst, basedn, log, args):
11       # Due to an issue, we can't use extended op, so we have to
12       # submit the password directly to the field.
13 - 
14       password = _get_arg(args.password, msg="Enter new directory manager password", hidden=True, confirm=True)
15       dm = DirectoryManager(inst)
16       dm.change_password(password)
17   
18 + 
19   def create_parsers(subparsers):
20       directory_manager_parser = subparsers.add_parser('directory_manager', help="Manage the directory manager account")
21   
 1 @@ -6,8 +6,6 @@
 2   # See LICENSE for details.
 3   # --- END COPYRIGHT BLOCK ---
 4   
 5 - import argparse
 6 - 
 7   from lib389.backend import Backend, Backends
 8   from lib389.config import Encryption, Config
 9   from lib389 import plugins
10 @@ -24,6 +22,7 @@
11       plugins.ReferentialIntegrityPlugin
12   ]
13   
14 + 
15   def _format_check_output(log, result):
16       log.info("==== DS Lint Error: %s ====" % result['dsle'])
17       log.info(" Severity: %s " % result['severity'])
18 @@ -35,6 +34,7 @@
19       log.info(" Resolution:")
20       log.info(result['fix'])
21   
22 + 
23   def health_check_run(inst, basedn, log, args):
24       log.info("Beginning lint report, this could take a while ...")
25       report = []
26 @@ -55,6 +55,7 @@
27       for item in report:
28           _format_check_output(log, item)
29   
30 + 
31   def create_parser(subparsers):
32       run_healthcheck_parser = subparsers.add_parser('healthcheck', help="Run a healthcheck report on your Directory Server instance. This is a safe, read only operation.")
33       run_healthcheck_parser.set_defaults(func=health_check_run)
 1 @@ -7,8 +7,6 @@
 2   # --- END COPYRIGHT BLOCK ---
 3   
 4   from lib389.plugins import Plugin, Plugins
 5 - import argparse
 6 - 
 7   from lib389.cli_base import (
 8       _generic_list,
 9       _generic_get,
10 @@ -29,26 +27,30 @@
11   def plugin_list(inst, basedn, log, args):
12       _generic_list(inst, basedn, log.getChild('plugin_list'), MANY, args)
13   
14 + 
15   def plugin_get(inst, basedn, log, args):
16 -     rdn = _get_arg( args.selector, msg="Enter %s to retrieve" % RDN)
17 +     rdn = _get_arg(args.selector, msg="Enter %s to retrieve" % RDN)
18       _generic_get(inst, basedn, log.getChild('plugin_get'), MANY, rdn, args)
19   
20 + 
21   def plugin_get_dn(inst, basedn, log, args):
22 -     dn = _get_arg( args.dn, msg="Enter dn to retrieve")
23 +     dn = _get_arg(args.dn, msg="Enter dn to retrieve")
24       _generic_get_dn(inst, basedn, log.getChild('plugin_get_dn'), MANY, dn, args)
25   
26 + 
27   # Plugin enable
28   def plugin_enable(inst, basedn, log, args):
29 -     dn = _get_arg( args.dn, msg="Enter plugin dn to enable")
30 +     dn = _get_arg(args.dn, msg="Enter plugin dn to enable")
31       mc = MANY(inst, basedn)
32       o = mc.get(dn=dn)
33       o.enable()
34       o_str = o.display()
35       log.info('Enabled %s', o_str)
36   
37 + 
38   # Plugin disable
39   def plugin_disable(inst, basedn, log, args, warn=True):
40 -     dn = _get_arg( args.dn, msg="Enter plugin dn to disable")
41 +     dn = _get_arg(args.dn, msg="Enter plugin dn to disable")
42       if warn:
43           _warn(dn, msg="Disabling %s %s" % (SINGULAR.__name__, dn))
44       mc = MANY(inst, basedn)
45 @@ -57,32 +59,38 @@
46       o_str = o.display()
47       log.info('Disabled %s', o_str)
48   
49 + 
50   # Plugin configure?
51   def plugin_configure(inst, basedn, log, args):
52       pass
53   
54 + 
55   def generic_show(inst, basedn, log, args):
56       """Display plugin configuration."""
57       plugin = args.plugin_cls(inst)
58       log.info(plugin.display())
59   
60 + 
61   def generic_enable(inst, basedn, log, args):
62       plugin = args.plugin_cls(inst)
63       plugin.enable()
64       log.info("Enabled %s", plugin.rdn)
65   
66 + 
67   def generic_disable(inst, basedn, log, args):
68       plugin = args.plugin_cls(inst)
69       plugin.disable()
70       log.info("Disabled %s", plugin.rdn)
71   
72 + 
73   def generic_status(inst, basedn, log, args):
74       plugin = args.plugin_cls(inst)
75 -     if plugin.status() == True:
76 +     if plugin.status() is True:
77           log.info("%s is enabled", plugin.rdn)
78       else:
79           log.info("%s is disabled", plugin.rdn)
80   
81 + 
82   def add_generic_plugin_parsers(subparser, plugin_cls):
83       show_parser = subparser.add_parser('show', help='display plugin configuration')
84       show_parser.set_defaults(func=generic_show, plugin_cls=plugin_cls)
 1 @@ -6,26 +6,50 @@
 2   # See LICENSE for details.
 3   # --- END COPYRIGHT BLOCK ---
 4   
 5 + 
 6   def dbtasks_db2index(inst, log, args):
 7 -     inst.db2index(bename=args.backend)
 8 +     if not inst.db2index(bename=args.backend):
 9 +         log.fatal("db2index failed")
10 +         return False
11 +     else:
12 +         log.info("db2index successful")
13 + 
14   
15   def dbtasks_db2bak(inst, log, args):
16       # Needs an output name?
17 -     inst.db2bak(args.archive)
18 -     log.info("db2bak successful")
19 +     if not inst.db2bak(args.archive):
20 +         log.fatal("db2bak failed")
21 +         return False
22 +     else:
23 +         log.info("db2bak successful")
24 + 
25   
26   def dbtasks_bak2db(inst, log, args):
27       # Needs the archive to restore.
28 -     inst.bak2db(args.archive)
29 -     log.info("bak2db successful")
30 +     if not inst.bak2db(args.archive):
31 +         log.fatal("bak2db failed")
32 +         return False
33 +     else:
34 +         log.info("bak2db successful")
35 + 
36   
37   def dbtasks_db2ldif(inst, log, args):
38 -     inst.db2ldif(bename=args.backend, encrypt=args.encrypted, repl_data=args.replication, outputfile=args.ldif, suffixes=None, excludeSuffixes=None)
39 -     log.info("db2ldif successful")
40 +     if not inst.db2ldif(bename=args.backend, encrypt=args.encrypted, repl_data=args.replication,
41 +                         outputfile=args.ldif, suffixes=None, excludeSuffixes=None):
42 +         log.fatal("db2ldif failed")
43 +         return False
44 +     else:
45 +         log.info("db2ldif successful")
46 + 
47   
48   def dbtasks_ldif2db(inst, log, args):
49 -     inst.ldif2db(bename=args.backend, encrypt=args.encrypted, import_file=args.ldif, suffixes=None, excludeSuffixes=None)
50 -     log.info("ldif2db successful")
51 +     if not inst.ldif2db(bename=args.backend, encrypt=args.encrypted, import_file=args.ldif,
52 +                         suffixes=None, excludeSuffixes=None):
53 +         log.fatal("ldif2db failed")
54 +         return False
55 +     else:
56 +         log.info("ldif2db successful")
57 + 
58   
59   def create_parser(subcommands):
60       db2index_parser = subcommands.add_parser('db2index', help="Initialise a reindex of the server database. The server must be stopped for this to proceed.")
61 @@ -34,13 +58,15 @@
62       db2index_parser.set_defaults(func=dbtasks_db2index)
63   
64       db2bak_parser = subcommands.add_parser('db2bak', help="Initialise a BDB backup of the database. The server must be stopped for this to proceed.")
65 -     db2bak_parser.add_argument('archive', help="The destination for the archive. This will be created during the db2bak process.")
66 +     db2bak_parser.add_argument('archive', help="The destination for the archive. This will be created during the db2bak process.",
67 +                                nargs='?', default=None)
68       db2bak_parser.set_defaults(func=dbtasks_db2bak)
69   
70       db2ldif_parser = subcommands.add_parser('db2ldif', help="Initialise an LDIF dump of the database. The server must be stopped for this to proceed.")
71       db2ldif_parser.add_argument('backend', help="The backend to output as an LDIF. IE userRoot")
72 -     db2ldif_parser.add_argument('ldif', help="The path to the ldif output location.")
73 -     db2ldif_parser.add_argument('--replication', help="Export replication information, suitable for importing on a new consumer or backups.", default=False, action='store_true')
74 +     db2ldif_parser.add_argument('ldif', help="The path to the ldif output location.", nargs='?', default=None)
75 +     db2ldif_parser.add_argument('--replication', help="Export replication information, suitable for importing on a new consumer or backups.",
76 +                                 default=False, action='store_true')
77       db2ldif_parser.add_argument('--encrypted', help="Export encrypted attributes", default=False, action='store_true')
78       db2ldif_parser.set_defaults(func=dbtasks_db2ldif)
79   
 1 @@ -18,6 +18,7 @@
 2   
 3   from lib389.instance.options import General2Base, Slapd2Base
 4   
 5 + 
 6   def instance_list(inst, log, args):
 7       instances = inst.list(all=True)
 8   
 9 @@ -31,26 +32,31 @@
10           log.info(e)
11           log.info("Perhaps you need to be a different user?")
12   
13 + 
14   def instance_restart(inst, log, args):
15       inst.restart(post_open=False)
16   
17 + 
18   def instance_start(inst, log, args):
19       inst.start(post_open=False)
20   
21 + 
22   def instance_stop(inst, log, args):
23       inst.stop()
24   
25 + 
26   def instance_status(inst, log, args):
27       if inst.status() is True:
28           log.info("Instance is running")
29       else:
30           log.info("Instance is not running")
31   
32 + 
33   def instance_create(inst, log, args):
34       if args.containerised:
35           log.debug("Containerised features requested.")
36       sd = SetupDs(args.verbose, args.dryrun, log, args.containerised)
37 -     ### If args.file is not set, we need to interactively get answers!
38 +     ### TODO - If args.file is not set, we need to interactively get answers!
39       if sd.create_from_inf(args.file):
40           # print("Sucessfully created instance")
41           return True
42 @@ -58,6 +64,7 @@
43           # print("Failed to create instance")
44           return False
45   
46 + 
47   def instance_example(inst, log, args):
48       print("""
49   ; --- BEGIN COPYRIGHT BLOCK ---
50 @@ -85,22 +92,28 @@
51       s2b = Slapd2Base(log)
52       print(g2b.collect_help())
53       print(s2b.collect_help())
54 +     return True
55 + 
56   
57   def instance_remove(inst, log, args):
58       if not args.ack:
59           log.info("""Not removing: if you are sure, add --doit""")
60 -         sys.exit(0)
61 +         return True
62       else:
63           log.info("""
64   About to remove instance %s!
65   If this is not what you want, press ctrl-c now ...
66           """ % inst.serverid)
67 -     for i in range(1,6):
68 +     for i in range(1, 6):
69           log.info('%s ...' % (5 - int(i)))
70           time.sleep(1)
71       log.info('Removing instance ...')
72 -     remove_ds_instance(inst)
73 -     log.info('Completed instance removal')
74 +     try:
75 +         remove_ds_instance(inst)
76 +         log.info('Completed instance removal')
77 +     except:
78 +         log.fatal('Instance removal failed')
79 +         return False
80   
81   
82   def create_parser(subcommands):
83 @@ -121,7 +134,8 @@
84       status_parser.set_defaults(func=instance_status)
85   
86       remove_parser = subcommands.add_parser('remove', help="Destroy an instance of Directory Server, and remove all data.")
87 -     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)
88 +     remove_parser.add_argument('--doit', dest="ack", help="By default we do a dry run. This actually initiates the removal.",
89 +                                action='store_true', default=False)
90       remove_parser.set_defaults(func=instance_remove)
91   
92   
1 @@ -89,7 +89,7 @@
2       mc = manager_class(inst, basedn)
3       if args and args.json:
4           o = mc.get(selector, json=True)
5 -         print(o);
6 +         print(o)
7       else:
8           o = mc.get(selector)
9           o_str = o.display()
 1 @@ -42,18 +42,20 @@
 2   
 3   ds_paths = Paths()
 4   
 5 + 
 6   class Options2(object):
 7       # This stores the base options in a self._options dict.
 8       # It provides a number of options for:
 9       # - dict overlay
10       # - parsing the config parser types.
11 + 
12       def __init__(self, log):
13           # 'key' : (default, helptext, valid_func )
14 -         self._options = {} # this takes the default
15 -         self._type = {} # Lists the type the item should be.
16 -         self._helptext = {} # help text for the option, MANDATORY.
17 -         self._example_comment = {} # If this is a commented value in the example.
18 -         self._valid_func = {} # options verification function.
19 +         self._options = {}  # this takes the default
20 +         self._type = {}  # Lists the type the item should be.
21 +         self._helptext = {}  # help text for the option, MANDATORY.
22 +         self._example_comment = {}  # If this is a commented value in the example.
23 +         self._valid_func = {}  # options verification function.
24           self._section = None
25           self.log = log
26   
27 @@ -102,6 +104,7 @@
28   # Base, example dicts of the general, backend (userRoot) options.
29   #
30   
31 + 
32   class General2Base(Options2):
33       def __init__(self, log):
34           super(General2Base, self).__init__(log)
35 @@ -139,9 +142,10 @@
36   
37   #
38   # This module contains the base options and configs for Director Server
39 - # setup and install. This allows 
40 + # setup and install. This allows
41   #
42   
43 + 
44   class Slapd2Base(Options2):
45       def __init__(self, log):
46           super(Slapd2Base, self).__init__(log)
1 @@ -10,6 +10,7 @@
2   import shutil
3   import subprocess
4   
5 + 
6   def remove_ds_instance(dirsrv):
7       """
8       This will delete the instance as it is define. This must be a local instance.