Blame SOURCES/0018-Issue-50791-Healthcheck-should-look-for-notes-A-F-in.patch

d69b2b
From a6a52365df26edd4f6b0028056395d943344d787 Mon Sep 17 00:00:00 2001
d69b2b
From: Mark Reynolds <mreynolds@redhat.com>
d69b2b
Date: Thu, 11 Jun 2020 15:30:28 -0400
d69b2b
Subject: [PATCH] Issue 50791 - Healthcheck should look for notes=A/F in access
d69b2b
 log
d69b2b
d69b2b
Description:  Add checks for notes=A (fully unindexed search) and
d69b2b
              notes=F (Unknown attribute in search filter) in the
d69b2b
              current access log.
d69b2b
d69b2b
relates: https://pagure.io/389-ds-base/issue/50791
d69b2b
d69b2b
Reviewed by: firstyear(Thanks!)
d69b2b
---
d69b2b
 src/lib389/lib389/cli_ctl/health.py |  4 +-
d69b2b
 src/lib389/lib389/dirsrv_log.py     | 72 +++++++++++++++++++++++++++--
d69b2b
 src/lib389/lib389/lint.py           | 26 ++++++++++-
d69b2b
 3 files changed, 96 insertions(+), 6 deletions(-)
d69b2b
d69b2b
diff --git a/src/lib389/lib389/cli_ctl/health.py b/src/lib389/lib389/cli_ctl/health.py
d69b2b
index 6333a753a..89484a11b 100644
d69b2b
--- a/src/lib389/lib389/cli_ctl/health.py
d69b2b
+++ b/src/lib389/lib389/cli_ctl/health.py
d69b2b
@@ -1,5 +1,5 @@
d69b2b
 # --- BEGIN COPYRIGHT BLOCK ---
d69b2b
-# Copyright (C) 2019 Red Hat, Inc.
d69b2b
+# Copyright (C) 2020 Red Hat, Inc.
d69b2b
 # All rights reserved.
d69b2b
 #
d69b2b
 # License: GPL (version 3 or any later version).
d69b2b
@@ -18,6 +18,7 @@ from lib389.monitor import MonitorDiskSpace
d69b2b
 from lib389.replica import Replica, Changelog5
d69b2b
 from lib389.nss_ssl import NssSsl
d69b2b
 from lib389.dseldif import FSChecks, DSEldif
d69b2b
+from lib389.dirsrv_log import DirsrvAccessLog
d69b2b
 from lib389 import lint
d69b2b
 from lib389 import plugins
d69b2b
 from lib389._constants import DSRC_HOME
d69b2b
@@ -37,6 +38,7 @@ CHECK_OBJECTS = [
d69b2b
     Changelog5,
d69b2b
     DSEldif,
d69b2b
     NssSsl,
d69b2b
+    DirsrvAccessLog,
d69b2b
 ]
d69b2b
 
d69b2b
 
d69b2b
diff --git a/src/lib389/lib389/dirsrv_log.py b/src/lib389/lib389/dirsrv_log.py
d69b2b
index baac2a3c9..7bed4bb17 100644
d69b2b
--- a/src/lib389/lib389/dirsrv_log.py
d69b2b
+++ b/src/lib389/lib389/dirsrv_log.py
d69b2b
@@ -1,5 +1,5 @@
d69b2b
 # --- BEGIN COPYRIGHT BLOCK ---
d69b2b
-# Copyright (C) 2016 Red Hat, Inc.
d69b2b
+# Copyright (C) 2020 Red Hat, Inc.
d69b2b
 # All rights reserved.
d69b2b
 #
d69b2b
 # License: GPL (version 3 or any later version).
d69b2b
@@ -9,12 +9,17 @@
d69b2b
 """Helpers for managing the directory server internal logs.
d69b2b
 """
d69b2b
 
d69b2b
+import copy
d69b2b
 import re
d69b2b
 import gzip
d69b2b
 from dateutil.parser import parse as dt_parse
d69b2b
 from glob import glob
d69b2b
 from lib389.utils import ensure_bytes
d69b2b
-
d69b2b
+from lib389._mapped_object_lint import DSLint
d69b2b
+from lib389.lint import (
d69b2b
+    DSLOGNOTES0001,  # Unindexed search
d69b2b
+    DSLOGNOTES0002,  # Unknown attr in search filter
d69b2b
+)
d69b2b
 
d69b2b
 # Because many of these settings can change live, we need to check for certain
d69b2b
 # attributes all the time.
d69b2b
@@ -35,7 +40,7 @@ MONTH_LOOKUP = {
d69b2b
 }
d69b2b
 
d69b2b
 
d69b2b
-class DirsrvLog(object):
d69b2b
+class DirsrvLog(DSLint):
d69b2b
     """Class of functions to working with the various DIrectory Server logs
d69b2b
     """
d69b2b
     def __init__(self, dirsrv):
d69b2b
@@ -189,6 +194,67 @@ class DirsrvAccessLog(DirsrvLog):
d69b2b
         self.full_regexs = [self.prog_m1, self.prog_con, self.prog_discon]
d69b2b
         self.result_regexs = [self.prog_notes, self.prog_repl,
d69b2b
                               self.prog_result]
d69b2b
+    @classmethod
d69b2b
+    def lint_uid(cls):
d69b2b
+        return 'logs'
d69b2b
+
d69b2b
+    def _log_get_search_stats(self, conn, op):
d69b2b
+        lines = self.match(f".* conn={conn} op={op} SRCH base=.*")
d69b2b
+        if len(lines) != 1:
d69b2b
+            return None
d69b2b
+
d69b2b
+        quoted_vals = re.findall('"([^"]*)"', lines[0])
d69b2b
+        return {
d69b2b
+            'base': quoted_vals[0],
d69b2b
+            'filter': quoted_vals[1],
d69b2b
+            'timestamp': re.findall('\[(.*)\]', lines[0])[0],
d69b2b
+            'scope': lines[0].split(' scope=', 1)[1].split(' ',1)[0]
d69b2b
+        }
d69b2b
+
d69b2b
+    def _lint_notes(self):
d69b2b
+        """
d69b2b
+        Check for notes=A (fully unindexed searches), and
d69b2b
+        notes=F (unknown attribute in filter)
d69b2b
+        """
d69b2b
+        for pattern, lint_report in [(".* notes=A", DSLOGNOTES0001), (".* notes=F", DSLOGNOTES0002)]:
d69b2b
+            lines = self.match(pattern)
d69b2b
+            if len(lines) > 0:
d69b2b
+                count = 0
d69b2b
+                searches = []
d69b2b
+                for line in lines:
d69b2b
+                    if ' RESULT err=' in line:
d69b2b
+                        # Looks like a valid notes=A/F
d69b2b
+                        conn = line.split(' conn=', 1)[1].split(' ',1)[0]
d69b2b
+                        op = line.split(' op=', 1)[1].split(' ',1)[0]
d69b2b
+                        etime = line.split(' etime=', 1)[1].split(' ',1)[0]
d69b2b
+                        stats = self._log_get_search_stats(conn, op)
d69b2b
+                        if stats is not None:
d69b2b
+                            timestamp = stats['timestamp']
d69b2b
+                            base = stats['base']
d69b2b
+                            scope = stats['scope']
d69b2b
+                            srch_filter = stats['filter']
d69b2b
+                            count += 1
d69b2b
+                            if lint_report == DSLOGNOTES0001:
d69b2b
+                                searches.append(f'\n  [{count}] Unindexed Search\n'
d69b2b
+                                                f'      - date:    {timestamp}\n'
d69b2b
+                                                f'      - conn/op: {conn}/{op}\n'
d69b2b
+                                                f'      - base:    {base}\n'
d69b2b
+                                                f'      - scope:   {scope}\n'
d69b2b
+                                                f'      - filter:  {srch_filter}\n'
d69b2b
+                                                f'      - etime:   {etime}\n')
d69b2b
+                            else:
d69b2b
+                                searches.append(f'\n  [{count}] Invalid Attribute in Filter\n'
d69b2b
+                                                f'      - date:    {timestamp}\n'
d69b2b
+                                                f'      - conn/op: {conn}/{op}\n'
d69b2b
+                                                f'      - filter:  {srch_filter}\n')
d69b2b
+                if len(searches) > 0:
d69b2b
+                    report = copy.deepcopy(lint_report)
d69b2b
+                    report['items'].append(self._get_log_path())
d69b2b
+                    report['detail'] = report['detail'].replace('NUMBER', str(count))
d69b2b
+                    for srch in searches:
d69b2b
+                        report['detail'] += srch
d69b2b
+                    yield report
d69b2b
+
d69b2b
 
d69b2b
     def _get_log_path(self):
d69b2b
         """Return the current log file location"""
d69b2b
diff --git a/src/lib389/lib389/lint.py b/src/lib389/lib389/lint.py
d69b2b
index a103feec7..4b1700b92 100644
d69b2b
--- a/src/lib389/lib389/lint.py
d69b2b
+++ b/src/lib389/lib389/lint.py
d69b2b
@@ -1,5 +1,5 @@
d69b2b
 # --- BEGIN COPYRIGHT BLOCK ---
d69b2b
-# Copyright (C) 2019 Red Hat, Inc.
d69b2b
+# Copyright (C) 2020 Red Hat, Inc.
d69b2b
 # All rights reserved.
d69b2b
 #
d69b2b
 # License: GPL (version 3 or any later version).
d69b2b
@@ -253,7 +253,7 @@ can use the CLI tool "dsconf" to resolve the conflict.  Here is an example:
d69b2b
 
d69b2b
     Remove conflict entry and keep only the original/counterpart entry:
d69b2b
 
d69b2b
-        # dsconf slapd-YOUR_INSTANCE  repl-conflict remove <DN of conflict entry>
d69b2b
+        # dsconf slapd-YOUR_INSTANCE  repl-conflict delete <DN of conflict entry>
d69b2b
 
d69b2b
     Replace the original/counterpart entry with the conflict entry:
d69b2b
 
d69b2b
@@ -418,3 +418,25 @@ until the time issues have been resolved:
d69b2b
 Also look at https://access.redhat.com/documentation/en-us/red_hat_directory_server/11/html/administration_guide/managing_replication-troubleshooting_replication_related_problems
d69b2b
 and find the paragraph "Too much time skew"."""
d69b2b
 }
d69b2b
+
d69b2b
+DSLOGNOTES0001 = {
d69b2b
+    'dsle': 'DSLOGNOTES0001',
d69b2b
+    'severity': 'Medium',
d69b2b
+    'description': 'Unindexed Search',
d69b2b
+    'items': ['Performance'],
d69b2b
+    'detail': """Found NUMBER fully unindexed searches in the current access log.
d69b2b
+Unindexed searches can cause high CPU and slow down the entire server's performance.\n""",
d69b2b
+    'fix': """Examine the searches that are unindexed, and either properly index the attributes
d69b2b
+in the filter, increase the nsslapd-idlistscanlimit, or stop using that filter."""
d69b2b
+}
d69b2b
+
d69b2b
+DSLOGNOTES0002 = {
d69b2b
+    'dsle': 'DSLOGNOTES0002',
d69b2b
+    'severity': 'Medium',
d69b2b
+    'description': 'Unknown Attribute In Filter',
d69b2b
+    'items': ['Possible Performance Impact'],
d69b2b
+    'detail': """Found NUMBER searches in the current access log that are using an
d69b2b
+unknown attribute in the search filter.\n""",
d69b2b
+    'fix': """Stop using this these unknown attributes in the filter, or add the schema
d69b2b
+to the server and make sure it's properly indexed."""
d69b2b
+}
d69b2b
-- 
d69b2b
2.26.2
d69b2b