|
|
5873fa |
From 21ed5224d63e3118a39ddd5ea438367532541a8f Mon Sep 17 00:00:00 2001
|
|
|
5873fa |
From: Matus Honek <mhonek@redhat.com>
|
|
|
5873fa |
Date: Mon, 2 Dec 2019 14:53:31 +0100
|
|
|
5873fa |
Subject: [PATCH 11/12] Issue 50746 - Add option to healthcheck to list all the
|
|
|
5873fa |
lint reports
|
|
|
5873fa |
|
|
|
5873fa |
Bug Description:
|
|
|
5873fa |
Healthcheck lacks a way to find out what checks are available.
|
|
|
5873fa |
|
|
|
5873fa |
Fix Description:
|
|
|
5873fa |
Add dsctl healthcheck options to list available checks, known error
|
|
|
5873fa |
codes, and ability to run cehcks selectively. The checks are rather
|
|
|
5873fa |
hierarchically structured and in some cases matchable by patterns (by
|
|
|
5873fa |
use of asterisk).
|
|
|
5873fa |
|
|
|
5873fa |
Fixes https://pagure.io/389-ds-base/issue/50746
|
|
|
5873fa |
|
|
|
5873fa |
Author: Matus Honek <mhonek@redhat.com>
|
|
|
5873fa |
|
|
|
5873fa |
Review by: Mark, William, Simon (thanks for the patience!)
|
|
|
5873fa |
|
|
|
5873fa |
(cherry picked from commit 4a55322c7bdb0b9ff57428ad0dc2e4d943572a69)
|
|
|
5873fa |
---
|
|
|
5873fa |
src/lib389/cli/dsctl | 1 +
|
|
|
5873fa |
src/lib389/lib389/_mapped_object.py | 34 +---
|
|
|
5873fa |
src/lib389/lib389/_mapped_object_lint.py | 157 ++++++++++++++++++
|
|
|
5873fa |
src/lib389/lib389/backend.py | 13 +-
|
|
|
5873fa |
src/lib389/lib389/cli_ctl/health.py | 116 +++++++++----
|
|
|
5873fa |
src/lib389/lib389/config.py | 13 +-
|
|
|
5873fa |
src/lib389/lib389/dseldif.py | 29 +---
|
|
|
5873fa |
src/lib389/lib389/encrypted_attributes.py | 1 -
|
|
|
5873fa |
src/lib389/lib389/index.py | 3 -
|
|
|
5873fa |
src/lib389/lib389/lint.py | 125 ++++++++------
|
|
|
5873fa |
src/lib389/lib389/monitor.py | 5 +-
|
|
|
5873fa |
src/lib389/lib389/nss_ssl.py | 23 ++-
|
|
|
5873fa |
src/lib389/lib389/plugins.py | 5 +-
|
|
|
5873fa |
src/lib389/lib389/replica.py | 10 +-
|
|
|
5873fa |
.../lib389/tests/mapped_object_lint_test.py | 78 +++++++++
|
|
|
5873fa |
15 files changed, 448 insertions(+), 165 deletions(-)
|
|
|
5873fa |
create mode 100644 src/lib389/lib389/_mapped_object_lint.py
|
|
|
5873fa |
create mode 100644 src/lib389/lib389/tests/mapped_object_lint_test.py
|
|
|
5873fa |
|
|
|
5873fa |
diff --git a/src/lib389/cli/dsctl b/src/lib389/cli/dsctl
|
|
|
5873fa |
index fd9bd87c1..9deda7039 100755
|
|
|
5873fa |
--- a/src/lib389/cli/dsctl
|
|
|
5873fa |
+++ b/src/lib389/cli/dsctl
|
|
|
5873fa |
@@ -64,6 +64,7 @@ cli_dbgen.create_parser(subparsers)
|
|
|
5873fa |
|
|
|
5873fa |
argcomplete.autocomplete(parser)
|
|
|
5873fa |
|
|
|
5873fa |
+
|
|
|
5873fa |
# handle a control-c gracefully
|
|
|
5873fa |
def signal_handler(signal, frame):
|
|
|
5873fa |
print('\n\nExiting...')
|
|
|
5873fa |
diff --git a/src/lib389/lib389/_mapped_object.py b/src/lib389/lib389/_mapped_object.py
|
|
|
5873fa |
index ce0ebfeb8..c60837601 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/_mapped_object.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/_mapped_object.py
|
|
|
5873fa |
@@ -15,6 +15,7 @@ import json
|
|
|
5873fa |
from functools import partial
|
|
|
5873fa |
from lib389._entry import Entry
|
|
|
5873fa |
from lib389._constants import DIRSRV_STATE_ONLINE
|
|
|
5873fa |
+from lib389._mapped_object_lint import DSLint, DSLints
|
|
|
5873fa |
from lib389.utils import (
|
|
|
5873fa |
ensure_bytes, ensure_str, ensure_int, ensure_list_bytes, ensure_list_str,
|
|
|
5873fa |
ensure_list_int, display_log_value, display_log_data
|
|
|
5873fa |
@@ -82,7 +83,7 @@ class DSLogging(object):
|
|
|
5873fa |
self._log.setLevel(logging.INFO)
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
-class DSLdapObject(DSLogging):
|
|
|
5873fa |
+class DSLdapObject(DSLogging, DSLint):
|
|
|
5873fa |
"""A single instance of DSLdapObjects
|
|
|
5873fa |
|
|
|
5873fa |
:param instance: An instance
|
|
|
5873fa |
@@ -107,7 +108,6 @@ class DSLdapObject(DSLogging):
|
|
|
5873fa |
self._must_attributes = None
|
|
|
5873fa |
# attributes, we don't want to compare
|
|
|
5873fa |
self._compare_exclude = ['entryid', 'modifytimestamp', 'nsuniqueid']
|
|
|
5873fa |
- self._lint_functions = None
|
|
|
5873fa |
self._server_controls = None
|
|
|
5873fa |
self._client_controls = None
|
|
|
5873fa |
self._object_filter = '(objectClass=*)'
|
|
|
5873fa |
@@ -985,38 +985,10 @@ class DSLdapObject(DSLogging):
|
|
|
5873fa |
"""
|
|
|
5873fa |
return self._create(rdn, properties, basedn, ensure=True)
|
|
|
5873fa |
|
|
|
5873fa |
- def lint(self):
|
|
|
5873fa |
- """Override this to create a linter for a type. This means that we can detect
|
|
|
5873fa |
- and report common administrative errors in the server from our cli and
|
|
|
5873fa |
- rest tools.
|
|
|
5873fa |
-
|
|
|
5873fa |
- The structure of a result is::
|
|
|
5873fa |
-
|
|
|
5873fa |
- {
|
|
|
5873fa |
- dsle: '<identifier>'. dsle == ds lint error. Will be a code unique to
|
|
|
5873fa |
- this module for the error, IE DSBLE0001.
|
|
|
5873fa |
- severity: '[HIGH:MEDIUM:LOW]'. severity of the error.
|
|
|
5873fa |
- items: '(dn,dn,dn)'. List of affected DNs or names.
|
|
|
5873fa |
- detail: 'msg ...'. An explination of the error.
|
|
|
5873fa |
- fix: 'msg ...'. Steps to resolve the error.
|
|
|
5873fa |
- }
|
|
|
5873fa |
-
|
|
|
5873fa |
- :returns: An array of these dicts, on None if there are no errors.
|
|
|
5873fa |
- """
|
|
|
5873fa |
-
|
|
|
5873fa |
- if not self._lint_functions:
|
|
|
5873fa |
- return None
|
|
|
5873fa |
- results = []
|
|
|
5873fa |
- for fn in self._lint_functions:
|
|
|
5873fa |
- for result in fn():
|
|
|
5873fa |
- if result is not None:
|
|
|
5873fa |
- results.append(result)
|
|
|
5873fa |
- return results
|
|
|
5873fa |
-
|
|
|
5873fa |
|
|
|
5873fa |
# A challenge of this, is how do we manage indexes? They have two naming attributes....
|
|
|
5873fa |
|
|
|
5873fa |
-class DSLdapObjects(DSLogging):
|
|
|
5873fa |
+class DSLdapObjects(DSLogging, DSLints):
|
|
|
5873fa |
"""The object represents the next idea: "Everything is an instance of something
|
|
|
5873fa |
that exists in this way", i.e. we unite LDAP entries by some
|
|
|
5873fa |
set of parameters with the object.
|
|
|
5873fa |
diff --git a/src/lib389/lib389/_mapped_object_lint.py b/src/lib389/lib389/_mapped_object_lint.py
|
|
|
5873fa |
new file mode 100644
|
|
|
5873fa |
index 000000000..2d03de98f
|
|
|
5873fa |
--- /dev/null
|
|
|
5873fa |
+++ b/src/lib389/lib389/_mapped_object_lint.py
|
|
|
5873fa |
@@ -0,0 +1,157 @@
|
|
|
5873fa |
+from abc import ABC, abstractmethod
|
|
|
5873fa |
+from functools import partial
|
|
|
5873fa |
+from inspect import signature
|
|
|
5873fa |
+from typing import (
|
|
|
5873fa |
+ Callable,
|
|
|
5873fa |
+ List,
|
|
|
5873fa |
+ Optional,
|
|
|
5873fa |
+ Tuple,
|
|
|
5873fa |
+ Union,
|
|
|
5873fa |
+ Type,
|
|
|
5873fa |
+ Generator,
|
|
|
5873fa |
+ Any
|
|
|
5873fa |
+)
|
|
|
5873fa |
+
|
|
|
5873fa |
+
|
|
|
5873fa |
+DSLintSpec = Tuple[str, Callable]
|
|
|
5873fa |
+DSLintParsedSpec = Tuple[Optional[str], Optional[str]]
|
|
|
5873fa |
+DSLintClassSpec = Generator[DSLintSpec, None, None]
|
|
|
5873fa |
+DSLintMethodSpec = Union[str, None, Type[List]]
|
|
|
5873fa |
+DSLintResults = Generator[Any, None, None]
|
|
|
5873fa |
+
|
|
|
5873fa |
+
|
|
|
5873fa |
+class DSLint():
|
|
|
5873fa |
+ """In a super-class, create a method with name beginning with `_lint_`
|
|
|
5873fa |
+ which would yield results (as described below). Such a method will
|
|
|
5873fa |
+ then be available to the `lint()` method of the class.
|
|
|
5873fa |
+
|
|
|
5873fa |
+ `lint_list`: takes a spec and yields available lints, recursively
|
|
|
5873fa |
+ `lint`: takes a spac and runs lints according to it, yielding results if any
|
|
|
5873fa |
+
|
|
|
5873fa |
+ `spec`: is a colon-separated string, with prefix matching a method name and suffix
|
|
|
5873fa |
+ being passed down to the method.
|
|
|
5873fa |
+
|
|
|
5873fa |
+ A class inheriting from hereby class shall implement a method named `lint_uid()` which
|
|
|
5873fa |
+ returns a pretty name of the object. This is to be used by a higher level code.
|
|
|
5873fa |
+
|
|
|
5873fa |
+ Each lint method has to have a name prefix with _lint_. It may accept an optional
|
|
|
5873fa |
+ parameter `spec` in which case:
|
|
|
5873fa |
+ - it has to accept typing.List class as a parameter, in which case it shall yield
|
|
|
5873fa |
+ all possible lint specs for that method
|
|
|
5873fa |
+ - it receives the suffix provided to the `spec` of hereby `lint` method (as mentioned above)
|
|
|
5873fa |
+
|
|
|
5873fa |
+ This means that we can detect and report common administrative errors
|
|
|
5873fa |
+ in the server from our cli and rest tools.
|
|
|
5873fa |
+
|
|
|
5873fa |
+ The structure of a result shall be:
|
|
|
5873fa |
+
|
|
|
5873fa |
+ {
|
|
|
5873fa |
+ dsle: '<identifier>'. dsle == ds lint error. Will be a code unique to
|
|
|
5873fa |
+ this module for the error, IE DSBLE0001.
|
|
|
5873fa |
+ severity: '[HIGH:MEDIUM:LOW]'. severity of the error.
|
|
|
5873fa |
+ items: '(dn,dn,dn)'. List of affected DNs or names.
|
|
|
5873fa |
+ detail: 'msg ...'. An explination of the error.
|
|
|
5873fa |
+ fix: 'msg ...'. Steps to resolve the error.
|
|
|
5873fa |
+ }
|
|
|
5873fa |
+ """
|
|
|
5873fa |
+
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def _dslint_fname(cls, method: Callable) -> Optional[str]:
|
|
|
5873fa |
+ """Return a pretty name for a method."""
|
|
|
5873fa |
+ if callable(method) and method.__name__.startswith('_lint_'):
|
|
|
5873fa |
+ return method.__name__[len('_lint_'):]
|
|
|
5873fa |
+ else:
|
|
|
5873fa |
+ return None
|
|
|
5873fa |
+
|
|
|
5873fa |
+ @staticmethod
|
|
|
5873fa |
+ def _dslint_parse_spec(spec: Optional[str]) -> DSLintParsedSpec:
|
|
|
5873fa |
+ """Split `spec` to prefix and suffix."""
|
|
|
5873fa |
+ wanted, *rest = spec.split(':', 1) if spec else (None, None)
|
|
|
5873fa |
+ return (wanted if wanted not in [None, '*'] else None,
|
|
|
5873fa |
+ rest[0] if rest else None)
|
|
|
5873fa |
+
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def _dslint_make_spec(cls, method: Callable, spec: Optional[str] = None) -> str:
|
|
|
5873fa |
+ """Build a new spec from prefix (`method` name) and suffix (`spec`)."""
|
|
|
5873fa |
+ fname = cls._dslint_fname(method)
|
|
|
5873fa |
+ return f'{fname}:{spec}' if spec else fname
|
|
|
5873fa |
+
|
|
|
5873fa |
+ def lint_list(self, spec: Optional[str] = None) -> DSLintClassSpec:
|
|
|
5873fa |
+ """Yield specs the object provides.
|
|
|
5873fa |
+
|
|
|
5873fa |
+ This yields from each lint method yielding all specs it can provide.
|
|
|
5873fa |
+ """
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert hasattr(self, 'lint_uid')
|
|
|
5873fa |
+
|
|
|
5873fa |
+ # Find _lint_ methods
|
|
|
5873fa |
+ # NOTE: There is a caveat: don't you dare try to getattr on a @property, or
|
|
|
5873fa |
+ # you get it executed. That's why the following line's complexity.
|
|
|
5873fa |
+ fs = [getattr(self, f) for f in dir(self)
|
|
|
5873fa |
+ if f.startswith('_lint_') and self._dslint_fname(getattr(self, f))]
|
|
|
5873fa |
+
|
|
|
5873fa |
+ # Filter acording to the `spec`
|
|
|
5873fa |
+ wanted, rest = self._dslint_parse_spec(spec)
|
|
|
5873fa |
+ if wanted:
|
|
|
5873fa |
+ try:
|
|
|
5873fa |
+ fs = [next(filter(lambda f: self._dslint_fname(f) == wanted, fs))]
|
|
|
5873fa |
+ except StopIteration:
|
|
|
5873fa |
+ raise ValueError('there is no such lint function')
|
|
|
5873fa |
+
|
|
|
5873fa |
+ # Yield known specs
|
|
|
5873fa |
+ for f in fs:
|
|
|
5873fa |
+ fspec_t = signature(f).parameters.get('spec', None)
|
|
|
5873fa |
+ if fspec_t:
|
|
|
5873fa |
+ assert fspec_t.annotation == DSLintMethodSpec
|
|
|
5873fa |
+ for fspec in [rest] if rest else f(spec=List):
|
|
|
5873fa |
+ yield self._dslint_make_spec(f, fspec), partial(f, spec=fspec)
|
|
|
5873fa |
+ else:
|
|
|
5873fa |
+ yield self._dslint_make_spec(f, rest), f
|
|
|
5873fa |
+
|
|
|
5873fa |
+ def lint(self, spec: DSLintMethodSpec = None) -> DSLintResults:
|
|
|
5873fa |
+ """Lint the object according to the `spec`."""
|
|
|
5873fa |
+
|
|
|
5873fa |
+ if spec == List:
|
|
|
5873fa |
+ yield from self.lint_list()
|
|
|
5873fa |
+ else:
|
|
|
5873fa |
+ for fn, f in self.lint_list(spec):
|
|
|
5873fa |
+ yield from f()
|
|
|
5873fa |
+
|
|
|
5873fa |
+
|
|
|
5873fa |
+class DSLints():
|
|
|
5873fa |
+ """This is a meta class to provide lint functionality to classes that provide
|
|
|
5873fa |
+ method `list` which returns list of objects that inherit from DSLint.
|
|
|
5873fa |
+
|
|
|
5873fa |
+ Calling `lint` or `lint_list` method yields from respective object's methods.
|
|
|
5873fa |
+
|
|
|
5873fa |
+ The `spec` is a colon-separated string. Its prefix matches the respective object's
|
|
|
5873fa |
+ `lint_uid` (or all when asterisk); the suffix is passed down to the respective
|
|
|
5873fa |
+ object's method.
|
|
|
5873fa |
+ """
|
|
|
5873fa |
+
|
|
|
5873fa |
+ def lint_list(self, spec: Optional[str] = None) -> DSLintClassSpec:
|
|
|
5873fa |
+ """Yield specs the objects returned by `list` method provide."""
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert hasattr(self, 'list')
|
|
|
5873fa |
+
|
|
|
5873fa |
+ # Filter acording to the `spec`
|
|
|
5873fa |
+ wanted, rest_spec = DSLint._dslint_parse_spec(spec)
|
|
|
5873fa |
+ if wanted in [None, '*']:
|
|
|
5873fa |
+ clss = self.list()
|
|
|
5873fa |
+ else:
|
|
|
5873fa |
+ clss = (cls for cls in self.list() if cls.lint_uid() == wanted)
|
|
|
5873fa |
+
|
|
|
5873fa |
+ # Yield known specs
|
|
|
5873fa |
+ for cls in clss:
|
|
|
5873fa |
+ for fn, f in cls.lint_list(spec=rest_spec):
|
|
|
5873fa |
+ yield (f'{cls.lint_uid()}:{fn}',
|
|
|
5873fa |
+ partial(f, rest_spec) if rest_spec else f)
|
|
|
5873fa |
+
|
|
|
5873fa |
+ def lint(self, spec: DSLintMethodSpec = None) -> DSLintResults:
|
|
|
5873fa |
+ """Lint the objects returned by `list` method according to the `spec`."""
|
|
|
5873fa |
+
|
|
|
5873fa |
+ if spec == List:
|
|
|
5873fa |
+ yield from self.lint_list()
|
|
|
5873fa |
+ else:
|
|
|
5873fa |
+ for obj in self.list():
|
|
|
5873fa |
+ yield from obj.lint()
|
|
|
5873fa |
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
|
|
|
5873fa |
index 4f752f414..8863ad1a8 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/backend.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/backend.py
|
|
|
5873fa |
@@ -393,6 +393,10 @@ class BackendLegacy(object):
|
|
|
5873fa |
replace = [(ldap.MOD_REPLACE, 'nsslapd-require-index', 'on')]
|
|
|
5873fa |
self.modify_s(dn, replace)
|
|
|
5873fa |
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'backends'
|
|
|
5873fa |
+
|
|
|
5873fa |
|
|
|
5873fa |
class Backend(DSLdapObject):
|
|
|
5873fa |
"""Backend DSLdapObject with:
|
|
|
5873fa |
@@ -413,10 +417,12 @@ class Backend(DSLdapObject):
|
|
|
5873fa |
self._must_attributes = ['nsslapd-suffix', 'cn']
|
|
|
5873fa |
self._create_objectclasses = ['top', 'extensibleObject', BACKEND_OBJECTCLASS_VALUE]
|
|
|
5873fa |
self._protected = False
|
|
|
5873fa |
- self._lint_functions = [self._lint_mappingtree, self._lint_search, self._lint_virt_attrs]
|
|
|
5873fa |
# Check if a mapping tree for this suffix exists.
|
|
|
5873fa |
self._mts = MappingTrees(self._instance)
|
|
|
5873fa |
|
|
|
5873fa |
+ def lint_uid(self):
|
|
|
5873fa |
+ return self.get_attr_val_utf8_l('cn').lower()
|
|
|
5873fa |
+
|
|
|
5873fa |
def _lint_virt_attrs(self):
|
|
|
5873fa |
"""Check if any virtual attribute are incorrectly indexed"""
|
|
|
5873fa |
indexes = self.get_indexes()
|
|
|
5873fa |
@@ -497,7 +503,6 @@ class Backend(DSLdapObject):
|
|
|
5873fa |
result = DSBLE0001
|
|
|
5873fa |
result['items'] = [bename, ]
|
|
|
5873fa |
yield result
|
|
|
5873fa |
- return None
|
|
|
5873fa |
|
|
|
5873fa |
def create_sample_entries(self, version):
|
|
|
5873fa |
"""Creates sample entries under nsslapd-suffix value
|
|
|
5873fa |
@@ -848,6 +853,10 @@ class Backends(DSLdapObjects):
|
|
|
5873fa |
self._childobject = Backend
|
|
|
5873fa |
self._basedn = DN_LDBM
|
|
|
5873fa |
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'backends'
|
|
|
5873fa |
+
|
|
|
5873fa |
def import_ldif(self, be_name, ldifs, chunk_size=None, encrypted=False, gen_uniq_id=None, only_core=False,
|
|
|
5873fa |
include_suffixes=None, exclude_suffixes=None):
|
|
|
5873fa |
"""Do an import of the suffix"""
|
|
|
5873fa |
diff --git a/src/lib389/lib389/cli_ctl/health.py b/src/lib389/lib389/cli_ctl/health.py
|
|
|
5873fa |
index 3d15ad85e..6333a753a 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/cli_ctl/health.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/cli_ctl/health.py
|
|
|
5873fa |
@@ -7,6 +7,9 @@
|
|
|
5873fa |
# --- END COPYRIGHT BLOCK ---
|
|
|
5873fa |
|
|
|
5873fa |
import json
|
|
|
5873fa |
+import re
|
|
|
5873fa |
+from lib389._mapped_object import DSLdapObjects
|
|
|
5873fa |
+from lib389._mapped_object_lint import DSLint
|
|
|
5873fa |
from lib389.cli_base import connect_instance, disconnect_instance
|
|
|
5873fa |
from lib389.cli_base.dsrc import dsrc_to_ldap, dsrc_arg_concat
|
|
|
5873fa |
from lib389.backend import Backends
|
|
|
5873fa |
@@ -15,17 +18,17 @@ from lib389.monitor import MonitorDiskSpace
|
|
|
5873fa |
from lib389.replica import Replica, Changelog5
|
|
|
5873fa |
from lib389.nss_ssl import NssSsl
|
|
|
5873fa |
from lib389.dseldif import FSChecks, DSEldif
|
|
|
5873fa |
+from lib389 import lint
|
|
|
5873fa |
from lib389 import plugins
|
|
|
5873fa |
from lib389._constants import DSRC_HOME
|
|
|
5873fa |
+from functools import partial
|
|
|
5873fa |
+from typing import Iterable
|
|
|
5873fa |
|
|
|
5873fa |
-# These get all instances, then check them all.
|
|
|
5873fa |
-CHECK_MANY_OBJECTS = [
|
|
|
5873fa |
- Backends,
|
|
|
5873fa |
-]
|
|
|
5873fa |
|
|
|
5873fa |
# These get single instances and check them.
|
|
|
5873fa |
CHECK_OBJECTS = [
|
|
|
5873fa |
Config,
|
|
|
5873fa |
+ Backends,
|
|
|
5873fa |
Encryption,
|
|
|
5873fa |
FSChecks,
|
|
|
5873fa |
plugins.ReferentialIntegrityPlugin,
|
|
|
5873fa |
@@ -52,44 +55,51 @@ def _format_check_output(log, result, idx):
|
|
|
5873fa |
log.info(result['fix'])
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
-def health_check_run(inst, log, args):
|
|
|
5873fa |
- """Connect to the local server using LDAPI, and perform various health checks
|
|
|
5873fa |
- """
|
|
|
5873fa |
+def _list_targets(inst):
|
|
|
5873fa |
+ for c in CHECK_OBJECTS:
|
|
|
5873fa |
+ o = c(inst)
|
|
|
5873fa |
+ yield o.lint_uid(), o
|
|
|
5873fa |
+
|
|
|
5873fa |
+
|
|
|
5873fa |
+def _list_errors(log):
|
|
|
5873fa |
+ for r in map(partial(getattr, lint),
|
|
|
5873fa |
+ filter(partial(re.match, r'^DS'),
|
|
|
5873fa |
+ dir(lint))):
|
|
|
5873fa |
+ log.info(f"{r['dsle']} :: {r['description']}")
|
|
|
5873fa |
|
|
|
5873fa |
- # update the args for connect_instance()
|
|
|
5873fa |
- args.basedn = None
|
|
|
5873fa |
- args.binddn = None
|
|
|
5873fa |
- args.bindpw = None
|
|
|
5873fa |
- args.starttls = None
|
|
|
5873fa |
- args.pwdfile = None
|
|
|
5873fa |
- args.prompt = False
|
|
|
5873fa |
- dsrc_inst = dsrc_to_ldap(DSRC_HOME, args.instance, log.getChild('dsrc'))
|
|
|
5873fa |
- dsrc_inst = dsrc_arg_concat(args, dsrc_inst)
|
|
|
5873fa |
- try:
|
|
|
5873fa |
- inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose, args=args)
|
|
|
5873fa |
- except Exception as e:
|
|
|
5873fa |
- raise ValueError('Failed to connect to Directory Server instance: ' + str(e))
|
|
|
5873fa |
|
|
|
5873fa |
+def _list_checks(inst, specs: Iterable[str]):
|
|
|
5873fa |
+ o_uids = dict(_list_targets(inst))
|
|
|
5873fa |
+ for s in specs:
|
|
|
5873fa |
+ wanted, rest = DSLint._dslint_parse_spec(s)
|
|
|
5873fa |
+ if wanted == '*':
|
|
|
5873fa |
+ raise ValueError('Unexpected spec selector asterisk')
|
|
|
5873fa |
+
|
|
|
5873fa |
+ if wanted in o_uids:
|
|
|
5873fa |
+ for l in o_uids[wanted].lint_list(rest):
|
|
|
5873fa |
+ yield o_uids[wanted], l
|
|
|
5873fa |
+ else:
|
|
|
5873fa |
+ raise ValueError('No such object specifier')
|
|
|
5873fa |
+
|
|
|
5873fa |
+
|
|
|
5873fa |
+def _print_checks(inst, specs: Iterable[str]) -> None:
|
|
|
5873fa |
+ for o, s in _list_checks(inst, specs):
|
|
|
5873fa |
+ print(f'{o.lint_uid()}:{s[0]}')
|
|
|
5873fa |
+
|
|
|
5873fa |
+
|
|
|
5873fa |
+def _run(inst, log, args, checks):
|
|
|
5873fa |
if not args.json:
|
|
|
5873fa |
log.info("Beginning lint report, this could take a while ...")
|
|
|
5873fa |
+
|
|
|
5873fa |
report = []
|
|
|
5873fa |
- for lo in CHECK_MANY_OBJECTS:
|
|
|
5873fa |
+ for o, s in checks:
|
|
|
5873fa |
if not args.json:
|
|
|
5873fa |
- log.info("Checking %s ..." % lo.__name__)
|
|
|
5873fa |
- lo_inst = lo(inst)
|
|
|
5873fa |
- for clo in lo_inst.list():
|
|
|
5873fa |
- result = clo.lint()
|
|
|
5873fa |
- if result is not None:
|
|
|
5873fa |
- report += result
|
|
|
5873fa |
- for lo in CHECK_OBJECTS:
|
|
|
5873fa |
- if not args.json:
|
|
|
5873fa |
- log.info("Checking %s ..." % lo.__name__)
|
|
|
5873fa |
- lo_inst = lo(inst)
|
|
|
5873fa |
- result = lo_inst.lint()
|
|
|
5873fa |
- if result is not None:
|
|
|
5873fa |
- report += result
|
|
|
5873fa |
+ log.info(f"Checking {o.lint_uid()}:{s[0]} ...")
|
|
|
5873fa |
+ report += o.lint(s[0]) or []
|
|
|
5873fa |
+
|
|
|
5873fa |
if not args.json:
|
|
|
5873fa |
log.info("Healthcheck complete.")
|
|
|
5873fa |
+
|
|
|
5873fa |
count = len(report)
|
|
|
5873fa |
if count == 0:
|
|
|
5873fa |
if not args.json:
|
|
|
5873fa |
@@ -110,6 +120,37 @@ def health_check_run(inst, log, args):
|
|
|
5873fa |
else:
|
|
|
5873fa |
log.info(json.dumps(report, indent=4))
|
|
|
5873fa |
|
|
|
5873fa |
+
|
|
|
5873fa |
+def health_check_run(inst, log, args):
|
|
|
5873fa |
+ """Connect to the local server using LDAPI, and perform various health checks
|
|
|
5873fa |
+ """
|
|
|
5873fa |
+
|
|
|
5873fa |
+ if args.list_errors:
|
|
|
5873fa |
+ _list_errors(log)
|
|
|
5873fa |
+ return
|
|
|
5873fa |
+
|
|
|
5873fa |
+ # update the args for connect_instance()
|
|
|
5873fa |
+ args.basedn = None
|
|
|
5873fa |
+ args.binddn = None
|
|
|
5873fa |
+ args.bindpw = None
|
|
|
5873fa |
+ args.starttls = None
|
|
|
5873fa |
+ args.pwdfile = None
|
|
|
5873fa |
+ args.prompt = False
|
|
|
5873fa |
+ dsrc_inst = dsrc_to_ldap(DSRC_HOME, args.instance, log.getChild('dsrc'))
|
|
|
5873fa |
+ dsrc_inst = dsrc_arg_concat(args, dsrc_inst)
|
|
|
5873fa |
+ try:
|
|
|
5873fa |
+ inst = connect_instance(dsrc_inst=dsrc_inst, verbose=args.verbose, args=args)
|
|
|
5873fa |
+ except Exception as e:
|
|
|
5873fa |
+ raise ValueError('Failed to connect to Directory Server instance: ' + str(e))
|
|
|
5873fa |
+
|
|
|
5873fa |
+ checks = args.check or dict(_list_targets(inst)).keys()
|
|
|
5873fa |
+
|
|
|
5873fa |
+ if args.list_checks or args.dry_run:
|
|
|
5873fa |
+ _print_checks(inst, checks)
|
|
|
5873fa |
+ return
|
|
|
5873fa |
+
|
|
|
5873fa |
+ _run(inst, log, args, _list_checks(inst, checks))
|
|
|
5873fa |
+
|
|
|
5873fa |
disconnect_instance(inst)
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
@@ -120,4 +161,9 @@ def create_parser(subparsers):
|
|
|
5873fa |
"remote Directory Server as this tool needs access to local resources, "
|
|
|
5873fa |
"otherwise the report may be inaccurate.")
|
|
|
5873fa |
run_healthcheck_parser.set_defaults(func=health_check_run)
|
|
|
5873fa |
-
|
|
|
5873fa |
+ run_healthcheck_parser.add_argument('--list-checks', action='store_true', help='List of known checks')
|
|
|
5873fa |
+ run_healthcheck_parser.add_argument('--list-errors', action='store_true', help='List of known error codes')
|
|
|
5873fa |
+ run_healthcheck_parser.add_argument('--dry-run', action='store_true', help='Do not execute the actual check, only list what would be done')
|
|
|
5873fa |
+ run_healthcheck_parser.add_argument('--check', nargs='+', default=None,
|
|
|
5873fa |
+ help='Areas to check. These can be obtained by --list-checks. Every element on the left of the colon (:)'
|
|
|
5873fa |
+ ' may be replaced by an asterisk if multiple options on the right are available.')
|
|
|
5873fa |
diff --git a/src/lib389/lib389/config.py b/src/lib389/lib389/config.py
|
|
|
5873fa |
index a29d0244c..aa4c92beb 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/config.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/config.py
|
|
|
5873fa |
@@ -54,7 +54,6 @@ class Config(DSLdapObject):
|
|
|
5873fa |
]
|
|
|
5873fa |
self._compare_exclude = self._compare_exclude + config_compare_exclude
|
|
|
5873fa |
self._rdn_attribute = 'cn'
|
|
|
5873fa |
- self._lint_functions = [self._lint_hr_timestamp, self._lint_passwordscheme]
|
|
|
5873fa |
|
|
|
5873fa |
@property
|
|
|
5873fa |
def dn(self):
|
|
|
5873fa |
@@ -197,6 +196,10 @@ class Config(DSLdapObject):
|
|
|
5873fa |
fields = 'nsslapd-security nsslapd-ssl-check-hostname'.split()
|
|
|
5873fa |
return self._instance.getEntry(DN_CONFIG, attrlist=fields)
|
|
|
5873fa |
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'config'
|
|
|
5873fa |
+
|
|
|
5873fa |
def _lint_hr_timestamp(self):
|
|
|
5873fa |
hr_timestamp = self.get_attr_val('nsslapd-logging-hr-timestamps-enabled')
|
|
|
5873fa |
if ensure_bytes('on') != hr_timestamp:
|
|
|
5873fa |
@@ -242,20 +245,22 @@ class Encryption(DSLdapObject):
|
|
|
5873fa |
self._rdn_attribute = 'cn'
|
|
|
5873fa |
self._must_attributes = ['cn']
|
|
|
5873fa |
self._protected = True
|
|
|
5873fa |
- self._lint_functions = [self._lint_check_tls_version]
|
|
|
5873fa |
|
|
|
5873fa |
def create(self, rdn=None, properties={'cn': 'encryption', 'nsSSLClientAuth': 'allowed'}):
|
|
|
5873fa |
if rdn is not None:
|
|
|
5873fa |
self._log.debug("dn on cn=encryption is not None. This is a mistake.")
|
|
|
5873fa |
super(Encryption, self).create(properties=properties)
|
|
|
5873fa |
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'encryption'
|
|
|
5873fa |
+
|
|
|
5873fa |
def _lint_check_tls_version(self):
|
|
|
5873fa |
tls_min = self.get_attr_val('sslVersionMin')
|
|
|
5873fa |
if tls_min is not None and tls_min < ensure_bytes('TLS1.1'):
|
|
|
5873fa |
report = copy.deepcopy(DSELE0001)
|
|
|
5873fa |
report['fix'] = report['fix'].replace('YOUR_INSTANCE', self._instance.serverid)
|
|
|
5873fa |
yield report
|
|
|
5873fa |
- yield None
|
|
|
5873fa |
|
|
|
5873fa |
@property
|
|
|
5873fa |
def ciphers(self):
|
|
|
5873fa |
@@ -487,7 +492,6 @@ class LDBMConfig(DSLdapObject):
|
|
|
5873fa |
self._dn = DN_CONFIG_LDBM
|
|
|
5873fa |
# config_compare_exclude = []
|
|
|
5873fa |
self._rdn_attribute = 'cn'
|
|
|
5873fa |
- self._lint_functions = []
|
|
|
5873fa |
self._protected = True
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
@@ -506,5 +510,4 @@ class BDB_LDBMConfig(DSLdapObject):
|
|
|
5873fa |
self._dn = DN_CONFIG_LDBM_BDB
|
|
|
5873fa |
self._config_compare_exclude = []
|
|
|
5873fa |
self._rdn_attribute = 'cn'
|
|
|
5873fa |
- self._lint_functions = []
|
|
|
5873fa |
self._protected = True
|
|
|
5873fa |
diff --git a/src/lib389/lib389/dseldif.py b/src/lib389/lib389/dseldif.py
|
|
|
5873fa |
index 5378e6ee9..96c9af9d1 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/dseldif.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/dseldif.py
|
|
|
5873fa |
@@ -16,6 +16,7 @@ from datetime import timedelta
|
|
|
5873fa |
from stat import ST_MODE
|
|
|
5873fa |
# from lib389.utils import print_nice_time
|
|
|
5873fa |
from lib389.paths import Paths
|
|
|
5873fa |
+from lib389._mapped_object_lint import DSLint
|
|
|
5873fa |
from lib389.lint import (
|
|
|
5873fa |
DSPERMLE0001,
|
|
|
5873fa |
DSPERMLE0002,
|
|
|
5873fa |
@@ -25,7 +26,7 @@ from lib389.lint import (
|
|
|
5873fa |
)
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
-class DSEldif(object):
|
|
|
5873fa |
+class DSEldif(DSLint):
|
|
|
5873fa |
"""A class for working with dse.ldif file
|
|
|
5873fa |
|
|
|
5873fa |
:param instance: An instance
|
|
|
5873fa |
@@ -58,15 +59,10 @@ class DSEldif(object):
|
|
|
5873fa |
processed_line = line
|
|
|
5873fa |
else:
|
|
|
5873fa |
processed_line = processed_line[:-1] + line[1:]
|
|
|
5873fa |
- self._lint_functions = [self._lint_nsstate]
|
|
|
5873fa |
|
|
|
5873fa |
- def lint(self):
|
|
|
5873fa |
- results = []
|
|
|
5873fa |
- for fn in self._lint_functions:
|
|
|
5873fa |
- for result in fn():
|
|
|
5873fa |
- if result is not None:
|
|
|
5873fa |
- results.append(result)
|
|
|
5873fa |
- return results
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'dseldif'
|
|
|
5873fa |
|
|
|
5873fa |
def _lint_nsstate(self):
|
|
|
5873fa |
suffixes = self.readNsState()
|
|
|
5873fa |
@@ -320,7 +316,7 @@ class DSEldif(object):
|
|
|
5873fa |
return states
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
-class FSChecks(object):
|
|
|
5873fa |
+class FSChecks(DSLint):
|
|
|
5873fa |
"""This is for the healthcheck feature, check commonly used system config files the
|
|
|
5873fa |
server uses. This is here for lack of a better place to add this class.
|
|
|
5873fa |
"""
|
|
|
5873fa |
@@ -344,17 +340,10 @@ class FSChecks(object):
|
|
|
5873fa |
'report': DSPERMLE0002
|
|
|
5873fa |
},
|
|
|
5873fa |
]
|
|
|
5873fa |
- self._lint_functions = [self._lint_file_perms]
|
|
|
5873fa |
|
|
|
5873fa |
- def lint(self):
|
|
|
5873fa |
- """Run a lint/healthcheck for this class
|
|
|
5873fa |
- """
|
|
|
5873fa |
- results = []
|
|
|
5873fa |
- for fn in self._lint_functions:
|
|
|
5873fa |
- for result in fn():
|
|
|
5873fa |
- if result is not None:
|
|
|
5873fa |
- results.append(result)
|
|
|
5873fa |
- return results
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'fschecks'
|
|
|
5873fa |
|
|
|
5873fa |
def _lint_file_perms(self):
|
|
|
5873fa |
"""Test file permissions are safe
|
|
|
5873fa |
diff --git a/src/lib389/lib389/encrypted_attributes.py b/src/lib389/lib389/encrypted_attributes.py
|
|
|
5873fa |
index 9afd2e66b..2fa26cef9 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/encrypted_attributes.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/encrypted_attributes.py
|
|
|
5873fa |
@@ -27,7 +27,6 @@ class EncryptedAttr(DSLdapObject):
|
|
|
5873fa |
self._must_attributes = ['cn', 'nsEncryptionAlgorithm']
|
|
|
5873fa |
self._create_objectclasses = ['top', 'nsAttributeEncryption']
|
|
|
5873fa |
self._protected = False
|
|
|
5873fa |
- self._lint_functions = []
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
class EncryptedAttrs(DSLdapObjects):
|
|
|
5873fa |
diff --git a/src/lib389/lib389/index.py b/src/lib389/lib389/index.py
|
|
|
5873fa |
index 6932883b7..a3d019d27 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/index.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/index.py
|
|
|
5873fa |
@@ -41,7 +41,6 @@ class Index(DSLdapObject):
|
|
|
5873fa |
self._must_attributes = ['cn', 'nsSystemIndex', 'nsIndexType']
|
|
|
5873fa |
self._create_objectclasses = ['top', 'nsIndex']
|
|
|
5873fa |
self._protected = False
|
|
|
5873fa |
- self._lint_functions = []
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
class Indexes(DSLdapObjects):
|
|
|
5873fa |
@@ -77,7 +76,6 @@ class VLVSearch(DSLdapObject):
|
|
|
5873fa |
self._must_attributes = ['cn', 'vlvbase', 'vlvscope', 'vlvfilter']
|
|
|
5873fa |
self._create_objectclasses = ['top', 'vlvSearch']
|
|
|
5873fa |
self._protected = False
|
|
|
5873fa |
- self._lint_functions = []
|
|
|
5873fa |
self._be_name = None
|
|
|
5873fa |
|
|
|
5873fa |
def get_sorts(self):
|
|
|
5873fa |
@@ -163,7 +161,6 @@ class VLVIndex(DSLdapObject):
|
|
|
5873fa |
self._must_attributes = ['cn', 'vlvsort']
|
|
|
5873fa |
self._create_objectclasses = ['top', 'vlvIndex']
|
|
|
5873fa |
self._protected = False
|
|
|
5873fa |
- self._lint_functions = []
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
class VLVIndexes(DSLdapObjects):
|
|
|
5873fa |
diff --git a/src/lib389/lib389/lint.py b/src/lib389/lib389/lint.py
|
|
|
5873fa |
index b5a305bc3..a103feec7 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/lint.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/lint.py
|
|
|
5873fa |
@@ -14,8 +14,9 @@
|
|
|
5873fa |
DSBLE0001 = {
|
|
|
5873fa |
'dsle': 'DSBLE0001',
|
|
|
5873fa |
'severity': 'MEDIUM',
|
|
|
5873fa |
- 'items' : [],
|
|
|
5873fa |
- 'detail' : """This backend may be missing the correct mapping tree references. Mapping Trees allow
|
|
|
5873fa |
+ 'description': 'Possibly incorrect mapping tree.',
|
|
|
5873fa |
+ 'items': [],
|
|
|
5873fa |
+ 'detail': """This backend may be missing the correct mapping tree references. Mapping Trees allow
|
|
|
5873fa |
the directory server to determine which backend an operation is routed to in the
|
|
|
5873fa |
abscence of other information. This is extremely important for correct functioning
|
|
|
5873fa |
of LDAP ADD for example.
|
|
|
5873fa |
@@ -32,7 +33,7 @@ objectClass: extensibleObject
|
|
|
5873fa |
objectClass: nsMappingTree
|
|
|
5873fa |
|
|
|
5873fa |
""",
|
|
|
5873fa |
- 'fix' : """Either you need to create the mapping tree, or you need to repair the related
|
|
|
5873fa |
+ 'fix': """Either you need to create the mapping tree, or you need to repair the related
|
|
|
5873fa |
mapping tree. You will need to do this by hand by editing cn=config, or stopping
|
|
|
5873fa |
the instance and editing dse.ldif.
|
|
|
5873fa |
"""
|
|
|
5873fa |
@@ -41,25 +42,28 @@ the instance and editing dse.ldif.
|
|
|
5873fa |
DSBLE0002 = {
|
|
|
5873fa |
'dsle': 'DSBLE0002',
|
|
|
5873fa |
'severity': 'HIGH',
|
|
|
5873fa |
- 'items' : [],
|
|
|
5873fa |
- 'detail' : """Unable to query the backend. LDAP error (ERROR)""",
|
|
|
5873fa |
- 'fix' : """Check the server's error and access logs for more information."""
|
|
|
5873fa |
+ 'description': 'Unable to query backend.',
|
|
|
5873fa |
+ 'items': [],
|
|
|
5873fa |
+ 'detail': """Unable to query the backend. LDAP error (ERROR)""",
|
|
|
5873fa |
+ 'fix': """Check the server's error and access logs for more information."""
|
|
|
5873fa |
}
|
|
|
5873fa |
|
|
|
5873fa |
DSBLE0003 = {
|
|
|
5873fa |
'dsle': 'DSBLE0003',
|
|
|
5873fa |
'severity': 'LOW',
|
|
|
5873fa |
- 'items' : [],
|
|
|
5873fa |
- 'detail' : """The backend database has not been initialized yet""",
|
|
|
5873fa |
- 'fix' : """You need to import an LDIF file, or create the suffix entry, in order to initialize the database."""
|
|
|
5873fa |
+ 'description': 'Uninitialized backend database.',
|
|
|
5873fa |
+ 'items': [],
|
|
|
5873fa |
+ 'detail': """The backend database has not been initialized yet""",
|
|
|
5873fa |
+ 'fix': """You need to import an LDIF file, or create the suffix entry, in order to initialize the database."""
|
|
|
5873fa |
}
|
|
|
5873fa |
|
|
|
5873fa |
# Config checks
|
|
|
5873fa |
DSCLE0001 = {
|
|
|
5873fa |
- 'dsle' : 'DSCLE0001',
|
|
|
5873fa |
- 'severity' : 'LOW',
|
|
|
5873fa |
+ 'dsle': 'DSCLE0001',
|
|
|
5873fa |
+ 'severity': 'LOW',
|
|
|
5873fa |
+ 'description': 'Different log timestamp format.',
|
|
|
5873fa |
'items': ['cn=config', ],
|
|
|
5873fa |
- 'detail' : """nsslapd-logging-hr-timestamps-enabled changes the log format in directory server from
|
|
|
5873fa |
+ 'detail': """nsslapd-logging-hr-timestamps-enabled changes the log format in directory server from
|
|
|
5873fa |
|
|
|
5873fa |
[07/Jun/2017:17:15:58 +1000]
|
|
|
5873fa |
|
|
|
5873fa |
@@ -70,7 +74,7 @@ to
|
|
|
5873fa |
This actually provides a performance improvement. Additionally, this setting will be
|
|
|
5873fa |
removed in a future release.
|
|
|
5873fa |
""",
|
|
|
5873fa |
- 'fix' : """Set nsslapd-logging-hr-timestamps-enabled to on.
|
|
|
5873fa |
+ 'fix': """Set nsslapd-logging-hr-timestamps-enabled to on.
|
|
|
5873fa |
You can use 'dsconf' to set this attribute. Here is an example:
|
|
|
5873fa |
|
|
|
5873fa |
# dsconf slapd-YOUR_INSTANCE config replace nsslapd-logging-hr-timestamps-enabled=on"""
|
|
|
5873fa |
@@ -79,8 +83,9 @@ You can use 'dsconf' to set this attribute. Here is an example:
|
|
|
5873fa |
DSCLE0002 = {
|
|
|
5873fa |
'dsle': 'DSCLE0002',
|
|
|
5873fa |
'severity': 'HIGH',
|
|
|
5873fa |
- 'items' : ['cn=config', ],
|
|
|
5873fa |
- 'detail' : """Password storage schemes in Directory Server define how passwords are hashed via a
|
|
|
5873fa |
+ 'description': 'Weak passwordStorageScheme.',
|
|
|
5873fa |
+ 'items': ['cn=config', ],
|
|
|
5873fa |
+ 'detail': """Password storage schemes in Directory Server define how passwords are hashed via a
|
|
|
5873fa |
one-way mathematical function for storage. Knowing the hash it is difficult to gain
|
|
|
5873fa |
the input, but knowing the input you can easily compare the hash.
|
|
|
5873fa |
|
|
|
5873fa |
@@ -112,14 +117,15 @@ You can also use 'dsconf' to replace these values. Here is an example:
|
|
|
5873fa |
DSELE0001 = {
|
|
|
5873fa |
'dsle': 'DSELE0001',
|
|
|
5873fa |
'severity': 'MEDIUM',
|
|
|
5873fa |
- 'items' : ['cn=encryption,cn=config', ],
|
|
|
5873fa |
+ 'description': 'Weak TLS protocol version.',
|
|
|
5873fa |
+ 'items': ['cn=encryption,cn=config', ],
|
|
|
5873fa |
'detail': """This Directory Server may not be using strong TLS protocol versions. TLS1.0 is known to
|
|
|
5873fa |
have a number of issues with the protocol. Please see:
|
|
|
5873fa |
|
|
|
5873fa |
https://tools.ietf.org/html/rfc7457
|
|
|
5873fa |
|
|
|
5873fa |
It is advised you set this value to the maximum possible.""",
|
|
|
5873fa |
- 'fix' : """There are two options for setting the TLS minimum version allowed. You,
|
|
|
5873fa |
+ 'fix': """There are two options for setting the TLS minimum version allowed. You,
|
|
|
5873fa |
can set "sslVersionMin" in "cn=encryption,cn=config" to a version greater than "TLS1.0"
|
|
|
5873fa |
You can also use 'dsconf' to set this value. Here is an example:
|
|
|
5873fa |
|
|
|
5873fa |
@@ -137,7 +143,8 @@ minimum version, but doing this affects the entire system:
|
|
|
5873fa |
DSRILE0001 = {
|
|
|
5873fa |
'dsle': 'DSRILE0001',
|
|
|
5873fa |
'severity': 'LOW',
|
|
|
5873fa |
- 'items' : ['cn=referential integrity postoperation,cn=plugins,cn=config', ],
|
|
|
5873fa |
+ 'description': 'Referential integrity plugin may be slower.',
|
|
|
5873fa |
+ 'items': ['cn=referential integrity postoperation,cn=plugins,cn=config', ],
|
|
|
5873fa |
'detail': """The referential integrity plugin has an asynchronous processing mode.
|
|
|
5873fa |
This is controlled by the update-delay flag. When this value is 0, referential
|
|
|
5873fa |
integrity plugin processes these changes inside of the operation that modified
|
|
|
5873fa |
@@ -151,7 +158,7 @@ delays to your server by batching changes rather than smaller updates during syn
|
|
|
5873fa |
|
|
|
5873fa |
We advise that you set this value to 0, and enable referint on all masters as it provides a more predictable behaviour.
|
|
|
5873fa |
""",
|
|
|
5873fa |
- 'fix' : """Set referint-update-delay to 0.
|
|
|
5873fa |
+ 'fix': """Set referint-update-delay to 0.
|
|
|
5873fa |
|
|
|
5873fa |
You can use 'dsconf' to set this value. Here is an example:
|
|
|
5873fa |
|
|
|
5873fa |
@@ -164,12 +171,13 @@ You must restart the Directory Server for this change to take effect."""
|
|
|
5873fa |
DSRILE0002 = {
|
|
|
5873fa |
'dsle': 'DSRILE0002',
|
|
|
5873fa |
'severity': 'HIGH',
|
|
|
5873fa |
- 'items' : ['cn=referential integrity postoperation,cn=plugins,cn=config'],
|
|
|
5873fa |
+ 'description': 'Referential integrity plugin configured with unindexed attribute.',
|
|
|
5873fa |
+ 'items': ['cn=referential integrity postoperation,cn=plugins,cn=config'],
|
|
|
5873fa |
'detail': """The referential integrity plugin is configured to use an attribute (ATTR)
|
|
|
5873fa |
that does not have an "equality" index in backend (BACKEND).
|
|
|
5873fa |
Failure to have the proper indexing will lead to unindexed searches which
|
|
|
5873fa |
cause high CPU and can significantly slow the server down.""",
|
|
|
5873fa |
- 'fix' : """Check the attributes set in "referint-membership-attr" to make sure they have
|
|
|
5873fa |
+ 'fix': """Check the attributes set in "referint-membership-attr" to make sure they have
|
|
|
5873fa |
an index defined that has at least the equality "eq" index type. You will
|
|
|
5873fa |
need to reindex the database after adding the missing index type. Here is an
|
|
|
5873fa |
example using dsconf:
|
|
|
5873fa |
@@ -182,12 +190,13 @@ example using dsconf:
|
|
|
5873fa |
DSDSLE0001 = {
|
|
|
5873fa |
'dsle': 'DSDSLE0001',
|
|
|
5873fa |
'severity': 'HIGH',
|
|
|
5873fa |
- 'items' : ['Server', 'cn=config'],
|
|
|
5873fa |
+ 'description': 'Low disk space.',
|
|
|
5873fa |
+ 'items': ['Server', 'cn=config'],
|
|
|
5873fa |
'detail': """The disk partition used by the server (PARTITION), either for the database, the
|
|
|
5873fa |
configuration files, or the logs is over 90% full. If the partition becomes
|
|
|
5873fa |
completely filled serious problems can occur with the database or the server's
|
|
|
5873fa |
stability.""",
|
|
|
5873fa |
- 'fix' : """Attempt to free up disk space. Also try removing old rotated logs, or disable any
|
|
|
5873fa |
+ 'fix': """Attempt to free up disk space. Also try removing old rotated logs, or disable any
|
|
|
5873fa |
verbose logging levels that might have been set. You might consider enabling
|
|
|
5873fa |
the "Disk Monitoring" feature in cn=config to help prevent a disorderly shutdown
|
|
|
5873fa |
of the server:
|
|
|
5873fa |
@@ -210,9 +219,10 @@ Please see the Administration guide for more information:
|
|
|
5873fa |
DSREPLLE0001 = {
|
|
|
5873fa |
'dsle': 'DSREPLLE0001',
|
|
|
5873fa |
'severity': 'HIGH',
|
|
|
5873fa |
- 'items' : ['Replication', 'Agreement'],
|
|
|
5873fa |
+ 'description': 'Replication agreement not set to be synchronized.',
|
|
|
5873fa |
+ 'items': ['Replication', 'Agreement'],
|
|
|
5873fa |
'detail': """The replication agreement (AGMT) under "SUFFIX" is not in synchronization.""",
|
|
|
5873fa |
- 'fix' : """You may need to reinitialize this replication agreement. Please check the errors
|
|
|
5873fa |
+ 'fix': """You may need to reinitialize this replication agreement. Please check the errors
|
|
|
5873fa |
log for more information. If you do need to reinitialize the agreement you can do so
|
|
|
5873fa |
using dsconf. Here is an example:
|
|
|
5873fa |
|
|
|
5873fa |
@@ -223,9 +233,10 @@ using dsconf. Here is an example:
|
|
|
5873fa |
DSREPLLE0002 = {
|
|
|
5873fa |
'dsle': 'DSREPLLE0002',
|
|
|
5873fa |
'severity': 'LOW',
|
|
|
5873fa |
- 'items' : ['Replication', 'Conflict Entries'],
|
|
|
5873fa |
+ 'description': 'Replication conflict entries found.',
|
|
|
5873fa |
+ 'items': ['Replication', 'Conflict Entries'],
|
|
|
5873fa |
'detail': "There were COUNT conflict entries found under the replication suffix \"SUFFIX\".",
|
|
|
5873fa |
- 'fix' : """While conflict entries are expected to occur in an MMR environment, they
|
|
|
5873fa |
+ 'fix': """While conflict entries are expected to occur in an MMR environment, they
|
|
|
5873fa |
should be resolved. In regards to conflict entries there is always the original/counterpart
|
|
|
5873fa |
entry that has a normal DN, and then the conflict version of that entry. Technically both
|
|
|
5873fa |
entries are valid, you as the administrator, needs to decide which entry you want to keep.
|
|
|
5873fa |
@@ -253,38 +264,42 @@ can use the CLI tool "dsconf" to resolve the conflict. Here is an example:
|
|
|
5873fa |
DSREPLLE0003 = {
|
|
|
5873fa |
'dsle': 'DSREPLLE0003',
|
|
|
5873fa |
'severity': 'MEDIUM',
|
|
|
5873fa |
- 'items' : ['Replication', 'Agreement'],
|
|
|
5873fa |
+ 'description': 'Unsynchronized replication agreement.',
|
|
|
5873fa |
+ 'items': ['Replication', 'Agreement'],
|
|
|
5873fa |
'detail': """The replication agreement (AGMT) under "SUFFIX" is not in synchronization.
|
|
|
5873fa |
Status message: MSG""",
|
|
|
5873fa |
- 'fix' : """Replication is not in synchronization but it may recover. Continue to
|
|
|
5873fa |
+ 'fix': """Replication is not in synchronization but it may recover. Continue to
|
|
|
5873fa |
monitor this agreement."""
|
|
|
5873fa |
}
|
|
|
5873fa |
|
|
|
5873fa |
DSREPLLE0004 = {
|
|
|
5873fa |
'dsle': 'DSREPLLE0004',
|
|
|
5873fa |
'severity': 'MEDIUM',
|
|
|
5873fa |
- 'items' : ['Replication', 'Agreement'],
|
|
|
5873fa |
+ 'description': 'Unable to get replication agreement status.',
|
|
|
5873fa |
+ 'items': ['Replication', 'Agreement'],
|
|
|
5873fa |
'detail': """Failed to get the agreement status for agreement (AGMT) under "SUFFIX". Error (ERROR).""",
|
|
|
5873fa |
- 'fix' : """None"""
|
|
|
5873fa |
+ 'fix': """None"""
|
|
|
5873fa |
}
|
|
|
5873fa |
|
|
|
5873fa |
DSREPLLE0005 = {
|
|
|
5873fa |
'dsle': 'DSREPLLE0005',
|
|
|
5873fa |
'severity': 'MEDIUM',
|
|
|
5873fa |
- 'items' : ['Replication', 'Agreement'],
|
|
|
5873fa |
+ 'description': 'Replication consumer not reachable.',
|
|
|
5873fa |
+ 'items': ['Replication', 'Agreement'],
|
|
|
5873fa |
'detail': """The replication agreement (AGMT) under "SUFFIX" is not in synchronization,
|
|
|
5873fa |
because the consumer server is not reachable.""",
|
|
|
5873fa |
- 'fix' : """Check if the consumer is running, and also check the errors log for more information."""
|
|
|
5873fa |
+ 'fix': """Check if the consumer is running, and also check the errors log for more information."""
|
|
|
5873fa |
}
|
|
|
5873fa |
|
|
|
5873fa |
# Replication changelog
|
|
|
5873fa |
DSCLLE0001 = {
|
|
|
5873fa |
'dsle': 'DSCLLE0001',
|
|
|
5873fa |
'severity': 'LOW',
|
|
|
5873fa |
- 'items' : ['Replication', 'Changelog'],
|
|
|
5873fa |
+ 'description': 'Changelog trimming not configured.',
|
|
|
5873fa |
+ 'items': ['Replication', 'Changelog'],
|
|
|
5873fa |
'detail': """The replication changelog does have any kind of trimming configured. This will
|
|
|
5873fa |
lead to the changelog size growing indefinitely.""",
|
|
|
5873fa |
- 'fix' : """Configure changelog trimming, preferably by setting the maximum age of a changelog
|
|
|
5873fa |
+ 'fix': """Configure changelog trimming, preferably by setting the maximum age of a changelog
|
|
|
5873fa |
record. Here is an example:
|
|
|
5873fa |
|
|
|
5873fa |
# dsconf slapd-YOUR_INSTANCE replication set-changelog --max-age 30d"""
|
|
|
5873fa |
@@ -294,27 +309,30 @@ record. Here is an example:
|
|
|
5873fa |
DSCERTLE0001 = {
|
|
|
5873fa |
'dsle': 'DSCERTLE0001',
|
|
|
5873fa |
'severity': 'MEDIUM',
|
|
|
5873fa |
- 'items' : ['Expiring Certificate'],
|
|
|
5873fa |
+ 'description': 'Certificate about to expire.',
|
|
|
5873fa |
+ 'items': ['Expiring Certificate'],
|
|
|
5873fa |
'detail': """The certificate (CERT) will expire in less than 30 days""",
|
|
|
5873fa |
- 'fix' : """Renew the certificate before it expires to prevent disruptions with TLS connections."""
|
|
|
5873fa |
+ 'fix': """Renew the certificate before it expires to prevent disruptions with TLS connections."""
|
|
|
5873fa |
}
|
|
|
5873fa |
|
|
|
5873fa |
DSCERTLE0002 = {
|
|
|
5873fa |
'dsle': 'DSCERTLE0002',
|
|
|
5873fa |
'severity': 'HIGH',
|
|
|
5873fa |
- 'items' : ['Expired Certificate'],
|
|
|
5873fa |
+ 'description': 'Certificate expired.',
|
|
|
5873fa |
+ 'items': ['Expired Certificate'],
|
|
|
5873fa |
'detail': """The certificate (CERT) has expired""",
|
|
|
5873fa |
- 'fix' : """Renew or remove the certificate."""
|
|
|
5873fa |
+ 'fix': """Renew or remove the certificate."""
|
|
|
5873fa |
}
|
|
|
5873fa |
|
|
|
5873fa |
# Virtual Attrs & COS. Note - ATTR and SUFFIX are replaced by the reporting function
|
|
|
5873fa |
DSVIRTLE0001 = {
|
|
|
5873fa |
'dsle': 'DSVIRTLE0001',
|
|
|
5873fa |
'severity': 'HIGH',
|
|
|
5873fa |
- 'items' : ['Virtual Attributes'],
|
|
|
5873fa |
+ 'description': 'Virtual attribute indexed.',
|
|
|
5873fa |
+ 'items': ['Virtual Attributes'],
|
|
|
5873fa |
'detail': """You should not index virtual attributes, and as this will break searches that
|
|
|
5873fa |
use the attribute in a filter.""",
|
|
|
5873fa |
- 'fix' : """Remove the index for this attribute from the backend configuration.
|
|
|
5873fa |
+ 'fix': """Remove the index for this attribute from the backend configuration.
|
|
|
5873fa |
Here is an example using 'dsconf' to remove an index:
|
|
|
5873fa |
|
|
|
5873fa |
# dsconf slapd-YOUR_INSTANCE backend index delete --attr ATTR SUFFIX"""
|
|
|
5873fa |
@@ -324,10 +342,11 @@ Here is an example using 'dsconf' to remove an index:
|
|
|
5873fa |
DSPERMLE0001 = {
|
|
|
5873fa |
'dsle': 'DSPERMLE0001',
|
|
|
5873fa |
'severity': 'MEDIUM',
|
|
|
5873fa |
- 'items' : ['File Permissions'],
|
|
|
5873fa |
+ 'description': 'Incorrect file permissions.',
|
|
|
5873fa |
+ 'items': ['File Permissions'],
|
|
|
5873fa |
'detail': """The file "FILE" does not have the expected permissions (PERMS). This
|
|
|
5873fa |
can cause issues with replication and chaining.""",
|
|
|
5873fa |
- 'fix' : """Change the file permissions:
|
|
|
5873fa |
+ 'fix': """Change the file permissions:
|
|
|
5873fa |
|
|
|
5873fa |
# chmod PERMS FILE"""
|
|
|
5873fa |
}
|
|
|
5873fa |
@@ -336,10 +355,11 @@ can cause issues with replication and chaining.""",
|
|
|
5873fa |
DSPERMLE0002 = {
|
|
|
5873fa |
'dsle': 'DSPERMLE0002',
|
|
|
5873fa |
'severity': 'HIGH',
|
|
|
5873fa |
- 'items' : ['File Permissions'],
|
|
|
5873fa |
+ 'description': 'Incorrect security database file permissions.',
|
|
|
5873fa |
+ 'items': ['File Permissions'],
|
|
|
5873fa |
'detail': """The file "FILE" does not have the expected permissions (PERMS). The
|
|
|
5873fa |
security database pin/password files should only be readable by Directory Server user.""",
|
|
|
5873fa |
- 'fix' : """Change the file permissions:
|
|
|
5873fa |
+ 'fix': """Change the file permissions:
|
|
|
5873fa |
|
|
|
5873fa |
# chmod PERMS FILE"""
|
|
|
5873fa |
}
|
|
|
5873fa |
@@ -348,11 +368,12 @@ security database pin/password files should only be readable by Directory Server
|
|
|
5873fa |
DSSKEWLE0001 = {
|
|
|
5873fa |
'dsle': 'DSSKEWLE0001',
|
|
|
5873fa |
'severity': 'Low',
|
|
|
5873fa |
- 'items' : ['Replication'],
|
|
|
5873fa |
+ 'description': 'Medium time skew.',
|
|
|
5873fa |
+ 'items': ['Replication'],
|
|
|
5873fa |
'detail': """The time skew is over 6 hours. If this time skew continues to increase
|
|
|
5873fa |
to 24 hours then replication can potentially stop working. Please continue to
|
|
|
5873fa |
monitor the time skew offsets for increasing values.""",
|
|
|
5873fa |
- 'fix' : """Monitor the time skew and avoid making changes to the system time.
|
|
|
5873fa |
+ 'fix': """Monitor the time skew and avoid making changes to the system time.
|
|
|
5873fa |
Also look at https://access.redhat.com/documentation/en-us/red_hat_directory_server/11/html/administration_guide/managing_replication-troubleshooting_replication_related_problems
|
|
|
5873fa |
and find the paragraph "Too much time skew"."""
|
|
|
5873fa |
}
|
|
|
5873fa |
@@ -360,13 +381,14 @@ and find the paragraph "Too much time skew"."""
|
|
|
5873fa |
DSSKEWLE0002 = {
|
|
|
5873fa |
'dsle': 'DSSKEWLE0002',
|
|
|
5873fa |
'severity': 'Medium',
|
|
|
5873fa |
- 'items' : ['Replication'],
|
|
|
5873fa |
+ 'description': 'Major time skew.',
|
|
|
5873fa |
+ 'items': ['Replication'],
|
|
|
5873fa |
'detail': """The time skew is over 12 hours. If this time skew continues to increase
|
|
|
5873fa |
to 24 hours then replication can potentially stop working. Please continue to
|
|
|
5873fa |
monitor the time skew offsets for increasing values. Setting nsslapd-ignore-time-skew
|
|
|
5873fa |
to "on" on each replica will allow replication to continue, but if the time skew
|
|
|
5873fa |
continues to increase other more serious replication problems can occur.""",
|
|
|
5873fa |
- 'fix' : """Monitor the time skew and avoid making changes to the system time.
|
|
|
5873fa |
+ 'fix': """Monitor the time skew and avoid making changes to the system time.
|
|
|
5873fa |
If you get close to 24 hours of time skew replication may stop working.
|
|
|
5873fa |
In that case configure the server to ignore the time skew until the system
|
|
|
5873fa |
times can be fixed/synchronized:
|
|
|
5873fa |
@@ -380,12 +402,13 @@ and find the paragraph "Too much time skew"."""
|
|
|
5873fa |
DSSKEWLE0003 = {
|
|
|
5873fa |
'dsle': 'DSSKEWLE0003',
|
|
|
5873fa |
'severity': 'High',
|
|
|
5873fa |
- 'items' : ['Replication'],
|
|
|
5873fa |
+ 'description': 'Extensive time skew.',
|
|
|
5873fa |
+ 'items': ['Replication'],
|
|
|
5873fa |
'detail': """The time skew is over 24 hours. Setting nsslapd-ignore-time-skew
|
|
|
5873fa |
to "on" on each replica will allow replication to continue, but if the
|
|
|
5873fa |
time skew continues to increase other serious replication problems can
|
|
|
5873fa |
occur.""",
|
|
|
5873fa |
- 'fix' : """Avoid making changes to the system time, and make sure the clocks
|
|
|
5873fa |
+ 'fix': """Avoid making changes to the system time, and make sure the clocks
|
|
|
5873fa |
on all the replicas are correct. If you haven't set the server's
|
|
|
5873fa |
"ignore time skew" setting then do the following on all the replicas
|
|
|
5873fa |
until the time issues have been resolved:
|
|
|
5873fa |
diff --git a/src/lib389/lib389/monitor.py b/src/lib389/lib389/monitor.py
|
|
|
5873fa |
index 73750c3c2..4ac7d7174 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/monitor.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/monitor.py
|
|
|
5873fa |
@@ -358,7 +358,10 @@ class MonitorDiskSpace(DSLdapObject):
|
|
|
5873fa |
def __init__(self, instance, dn=None):
|
|
|
5873fa |
super(MonitorDiskSpace, self).__init__(instance=instance, dn=dn)
|
|
|
5873fa |
self._dn = "cn=disk space,cn=monitor"
|
|
|
5873fa |
- self._lint_functions = [self._lint_disk_space]
|
|
|
5873fa |
+
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'monitor-disk-space'
|
|
|
5873fa |
|
|
|
5873fa |
def _lint_disk_space(self):
|
|
|
5873fa |
partitions = self.get_attr_vals_utf8_l("dsDisk")
|
|
|
5873fa |
diff --git a/src/lib389/lib389/nss_ssl.py b/src/lib389/lib389/nss_ssl.py
|
|
|
5873fa |
index d14e7ce6f..e257424fd 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/nss_ssl.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/nss_ssl.py
|
|
|
5873fa |
@@ -21,6 +21,7 @@ import subprocess
|
|
|
5873fa |
from datetime import datetime, timedelta
|
|
|
5873fa |
from subprocess import check_output, run, PIPE
|
|
|
5873fa |
from lib389.passwd import password_generate
|
|
|
5873fa |
+from lib389._mapped_object_lint import DSLint
|
|
|
5873fa |
from lib389.lint import DSCERTLE0001, DSCERTLE0002
|
|
|
5873fa |
from lib389.utils import ensure_str, format_cmd_list
|
|
|
5873fa |
import uuid
|
|
|
5873fa |
@@ -42,7 +43,7 @@ VALID_MIN = 61 # Days
|
|
|
5873fa |
log = logging.getLogger(__name__)
|
|
|
5873fa |
|
|
|
5873fa |
|
|
|
5873fa |
-class NssSsl(object):
|
|
|
5873fa |
+class NssSsl(DSLint):
|
|
|
5873fa |
def __init__(self, dirsrv=None, dbpassword=None, dbpath=None):
|
|
|
5873fa |
self.dirsrv = dirsrv
|
|
|
5873fa |
self._certdb = dbpath
|
|
|
5873fa |
@@ -56,18 +57,14 @@ class NssSsl(object):
|
|
|
5873fa |
else:
|
|
|
5873fa |
self.dbpassword = dbpassword
|
|
|
5873fa |
|
|
|
5873fa |
- self.db_files = {"dbm_backend": ["%s/%s" % (self._certdb, f) for f in ("key3.db", "cert8.db", "secmod.db")],
|
|
|
5873fa |
- "sql_backend": ["%s/%s" % (self._certdb, f) for f in ("key4.db", "cert9.db", "pkcs11.txt")],
|
|
|
5873fa |
- "support": ["%s/%s" % (self._certdb, f) for f in ("noise.txt", PIN_TXT, PWD_TXT)]}
|
|
|
5873fa |
- self._lint_functions = [self._lint_certificate_expiration,]
|
|
|
5873fa |
-
|
|
|
5873fa |
- def lint(self):
|
|
|
5873fa |
- results = []
|
|
|
5873fa |
- for fn in self._lint_functions:
|
|
|
5873fa |
- for result in fn():
|
|
|
5873fa |
- if result is not None:
|
|
|
5873fa |
- results.append(result)
|
|
|
5873fa |
- return results
|
|
|
5873fa |
+ self.db_files = {group: [f"{self._certdb}/{f}" for f in files]
|
|
|
5873fa |
+ for group, files in {"dbm_backend": ("key3.db", "cert8.db", "secmod.db"),
|
|
|
5873fa |
+ "sql_backend": ("key4.db", "cert9.db", "pkcs11.txt"),
|
|
|
5873fa |
+ "support": ("noise.txt", PIN_TXT, PWD_TXT)}.items()}
|
|
|
5873fa |
+
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'ssl'
|
|
|
5873fa |
|
|
|
5873fa |
def _lint_certificate_expiration(self):
|
|
|
5873fa |
"""Check all the certificates in the db if they will expire within 30 days
|
|
|
5873fa |
diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py
|
|
|
5873fa |
index f68a1d114..89e660287 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/plugins.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/plugins.py
|
|
|
5873fa |
@@ -431,7 +431,6 @@ class ReferentialIntegrityPlugin(Plugin):
|
|
|
5873fa |
'referint-logfile',
|
|
|
5873fa |
'referint-membership-attr',
|
|
|
5873fa |
])
|
|
|
5873fa |
- self._lint_functions = [self._lint_update_delay, self._lint_attr_indexes]
|
|
|
5873fa |
|
|
|
5873fa |
def create(self, rdn=None, properties=None, basedn=None):
|
|
|
5873fa |
"""Create an instance of the plugin"""
|
|
|
5873fa |
@@ -443,6 +442,10 @@ class ReferentialIntegrityPlugin(Plugin):
|
|
|
5873fa |
properties['referint-logfile'] = referint_log
|
|
|
5873fa |
return super(ReferentialIntegrityPlugin, self).create(rdn, properties, basedn)
|
|
|
5873fa |
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'refint'
|
|
|
5873fa |
+
|
|
|
5873fa |
def _lint_update_delay(self):
|
|
|
5873fa |
if self.status():
|
|
|
5873fa |
delay = self.get_attr_val_int("referint-update-delay")
|
|
|
5873fa |
diff --git a/src/lib389/lib389/replica.py b/src/lib389/lib389/replica.py
|
|
|
5873fa |
index f8adb3ce2..f575e58d5 100644
|
|
|
5873fa |
--- a/src/lib389/lib389/replica.py
|
|
|
5873fa |
+++ b/src/lib389/lib389/replica.py
|
|
|
5873fa |
@@ -1049,7 +1049,10 @@ class Changelog5(DSLdapObject):
|
|
|
5873fa |
'extensibleobject',
|
|
|
5873fa |
]
|
|
|
5873fa |
self._protected = False
|
|
|
5873fa |
- self._lint_functions = [self._lint_cl_trimming]
|
|
|
5873fa |
+
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'changelog'
|
|
|
5873fa |
|
|
|
5873fa |
def _lint_cl_trimming(self):
|
|
|
5873fa |
"""Check that cl trimming is at least defined to prevent unbounded growth"""
|
|
|
5873fa |
@@ -1120,7 +1123,10 @@ class Replica(DSLdapObject):
|
|
|
5873fa |
self._create_objectclasses.append('extensibleobject')
|
|
|
5873fa |
self._protected = False
|
|
|
5873fa |
self._suffix = None
|
|
|
5873fa |
- self._lint_functions = [self._lint_agmts_status, self._lint_conflicts]
|
|
|
5873fa |
+
|
|
|
5873fa |
+ @classmethod
|
|
|
5873fa |
+ def lint_uid(cls):
|
|
|
5873fa |
+ return 'replication'
|
|
|
5873fa |
|
|
|
5873fa |
def _lint_agmts_status(self):
|
|
|
5873fa |
replicas = Replicas(self._instance).list()
|
|
|
5873fa |
diff --git a/src/lib389/lib389/tests/mapped_object_lint_test.py b/src/lib389/lib389/tests/mapped_object_lint_test.py
|
|
|
5873fa |
new file mode 100644
|
|
|
5873fa |
index 000000000..a4ca0ea3c
|
|
|
5873fa |
--- /dev/null
|
|
|
5873fa |
+++ b/src/lib389/lib389/tests/mapped_object_lint_test.py
|
|
|
5873fa |
@@ -0,0 +1,78 @@
|
|
|
5873fa |
+from typing import List
|
|
|
5873fa |
+
|
|
|
5873fa |
+import pytest
|
|
|
5873fa |
+
|
|
|
5873fa |
+from lib389._mapped_object_lint import (
|
|
|
5873fa |
+ DSLint,
|
|
|
5873fa |
+ DSLints,
|
|
|
5873fa |
+ DSLintMethodSpec
|
|
|
5873fa |
+)
|
|
|
5873fa |
+
|
|
|
5873fa |
+
|
|
|
5873fa |
+def test_dslint():
|
|
|
5873fa |
+ class DS(DSLint):
|
|
|
5873fa |
+ def lint_uid(self) -> str:
|
|
|
5873fa |
+ return self.param
|
|
|
5873fa |
+
|
|
|
5873fa |
+ def __init__(self, param):
|
|
|
5873fa |
+ self.param = param
|
|
|
5873fa |
+ self.suffixes = ['suffixA', 'suffixB']
|
|
|
5873fa |
+
|
|
|
5873fa |
+ def _lint_nsstate(self, spec: DSLintMethodSpec = None):
|
|
|
5873fa |
+ if spec == List:
|
|
|
5873fa |
+ yield from self.suffixes
|
|
|
5873fa |
+ else:
|
|
|
5873fa |
+ to_lint = [spec] if spec else self._lint_nsstate(spec=List)
|
|
|
5873fa |
+ for tl in to_lint:
|
|
|
5873fa |
+ if tl == 'suffixA':
|
|
|
5873fa |
+ pass
|
|
|
5873fa |
+ elif tl == 'suffixB':
|
|
|
5873fa |
+ yield 'suffixB is bad'
|
|
|
5873fa |
+ else:
|
|
|
5873fa |
+ raise ValueError('There is no such suffix')
|
|
|
5873fa |
+
|
|
|
5873fa |
+ def _lint_second(self):
|
|
|
5873fa |
+ yield from ()
|
|
|
5873fa |
+
|
|
|
5873fa |
+ def _lint_third(self):
|
|
|
5873fa |
+ yield from ['this is a fail']
|
|
|
5873fa |
+
|
|
|
5873fa |
+ class DSs(DSLints):
|
|
|
5873fa |
+ def list(self):
|
|
|
5873fa |
+ for i in [DS("ma"), DS("mb")]:
|
|
|
5873fa |
+ yield i
|
|
|
5873fa |
+
|
|
|
5873fa |
+ # single
|
|
|
5873fa |
+ inst = DS("a")
|
|
|
5873fa |
+ inst_lints = {'nsstate:suffixA', 'nsstate:suffixB', 'second', 'third'}
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert inst.param == "a"
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert set(dict(inst.lint_list()).keys()) == inst_lints
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert set(dict(inst.lint_list('nsstate')).keys()) \
|
|
|
5873fa |
+ == {f'nsstate:suffix{s}' for s in "AB"}
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert list(inst._lint_nsstate(spec=List)) == ['suffixA', 'suffixB']
|
|
|
5873fa |
+ assert list(inst.lint()) == ['suffixB is bad', 'this is a fail']
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert list(inst.lint('nsstate')) == ['suffixB is bad']
|
|
|
5873fa |
+ assert list(inst.lint('nsstate:suffixA')) == []
|
|
|
5873fa |
+ assert list(inst.lint('nsstate:suffixB')) == ['suffixB is bad']
|
|
|
5873fa |
+ with pytest.raises(ValueError):
|
|
|
5873fa |
+ list(inst.lint('nonexistent'))
|
|
|
5873fa |
+
|
|
|
5873fa |
+ # multiple
|
|
|
5873fa |
+ insts = DSs()
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert insts.lint_list
|
|
|
5873fa |
+ assert insts.lint
|
|
|
5873fa |
+
|
|
|
5873fa |
+ assert set(dict(insts.lint_list()).keys()) \
|
|
|
5873fa |
+ == {f'{m}:{s}' for m in ['ma', 'mb'] for s in inst_lints}
|
|
|
5873fa |
+ assert set(dict(insts.lint_list('*')).keys()) \
|
|
|
5873fa |
+ == {f'{m}:{s}' for m in ['ma', 'mb'] for s in inst_lints}
|
|
|
5873fa |
+ assert set(dict(insts.lint_list('*:nsstate')).keys()) \
|
|
|
5873fa |
+ == {f'{m}:nsstate:suffix{s}' for m in ['ma', 'mb'] for s in "AB"}
|
|
|
5873fa |
+ assert set(dict(insts.lint_list('mb:nsstate')).keys()) \
|
|
|
5873fa |
+ == {f'mb:nsstate:suffix{s}' for s in "AB"}
|
|
|
5873fa |
--
|
|
|
5873fa |
2.26.2
|
|
|
5873fa |
|