Blob Blame History Raw
From b72e8a48be07a1cebce8b2237d7344220678c2ec Mon Sep 17 00:00:00 2001
From: Noriko Hosoi <nhosoi@redhat.com>
Date: Fri, 16 Oct 2020 08:15:11 -0700
Subject: [PATCH 5/7] Logging - support property-based filters in the files and
 forwards outputs

Adding property-based filter options to files, forwards and remote_files output.
A test case is added to tests_basics_files2.yml.

In addition, fixing a bug caused by a left over file from the previous tests.

Issue - https://github.com/linux-system-roles/logging/issues/179

(cherry picked from commit 6ac8f9ff680a4b0230446062f5927f5921829f80)
---
 README.md                                     | 68 ++++++++++++-------
 roles/rsyslog/templates/output_files.j2       |  4 +-
 roles/rsyslog/templates/output_forwards.j2    |  4 +-
 .../rsyslog/templates/output_remote_files.j2  |  4 +-
 tests/tests_basics_files2.yml                 | 40 +++++++++--
 tests/tests_basics_forwards_cert.yml          |  8 +++
 tests/tests_basics_forwards_cert_missing.yml  |  4 ++
 tests/tests_server_conflict.yml               |  8 +++
 8 files changed, 108 insertions(+), 32 deletions(-)

diff --git a/README.md b/README.md
index db29dc5..4352ee7 100644
--- a/README.md
+++ b/README.md
@@ -180,11 +180,16 @@ This is a schematic logging configuration to show log messages from input_nameA
 
 - `files` type - `files` output supports storing logs in the local files usually in /var/log.<br>
   **available options**
-  - `facility`: Facility; default to `*`.
-  - `severity`: Severity; default to `*`.
-  - `exclude`: Exclude list; default to none.
+  - `facility`: Facility in selector; default to `*`.
+  - `severity`: Severity in selector; default to `*`.
+  - `exclude`: Exclude list used in selector; default to none.
+  - `property`: Property in property-based filter; no default
+  - `prop_op`: Operation in property-based filter; In case of not `!`, put the `prop_op` value in quotes; default to `contains`
+  - `prop_value`: Value in property-based filter; default to `error`
   - `path`: Path to the output file.
 
+  Selector options and property-based filter options are exclusive. If Property-based filter options are defined, selector options will be ignored.
+
   Unless the above options are given, these local file outputs are configured.
   ```
   kern.*                                      /dev/console
@@ -199,8 +204,12 @@ This is a schematic logging configuration to show log messages from input_nameA
 
 - `forwards` type - `forwards` output sends logs to the remote logging system over the network. This is for the client rsyslog.<br>
   **available options**
-  - `facility`: Facility; default to `*`.
-  - `severity`: Severity; default to `*`.
+  - `facility`: Facility in selector; default to `*`.
+  - `severity`: Severity in selector; default to `*`.
+  - `exclude`: Exclude list used in selector; default to none.
+  - `property`: Property in property-based filter; no default
+  - `prop_op`: Operation in property-based filter; In case of not `!`, put the `prop_op` value in quotes; default to `contains`
+  - `prop_value`: Value in property-based filter; default to `error`
   - `target`: Target host (fqdn). **Required**.
   - `udp_port`: UDP port number. Default to `514`.
   - `tcp_port`: TCP port number. Default to `514`.
@@ -208,11 +217,16 @@ This is a schematic logging configuration to show log messages from input_nameA
   - `pki_authmode`: Specifying the default network driver authentication mode. `x509/name`, `x509/fingerprint`, `anon` is accepted. Default to `x509/name`.
   - `permitted_server`: Hostname, IP address, fingerprint(sha1) or wildcard DNS domain of the server which this client will be allowed to connect and send logs over TLS. Default to `*.{{ logging_domain }}`
 
+  Selector options and property-based filter options are exclusive. If Property-based filter options are defined, selector options will be ignored.
+
 - `remote_files` type - `remote_files` output stores logs to the local files per remote host and program name originated the logs.<br>
   **available options**
-  - `facility`: Facility; default to `*`.
-  - `severity`: Severity; default to `*`.
-  - `exclude`: Exclude list; default to none.
+  - `facility`: Facility in selector; default to `*`.
+  - `severity`: Severity in selector; default to `*`.
+  - `exclude`: Exclude list used in selector; default to none.
+  - `property`: Property in property-based filter; no default
+  - `prop_op`: Operation in property-based filter; In case of not `!`, put the `prop_op` value in quotes; default to `contains`
+  - `prop_value`: Value in property-based filter; default to `error`
   - `async_writing`: If set to `true`, the files are written asynchronously. Allowed value is `true` or `false`. Default to `false`.
   - `client_count`: Count of client logging system supported this rsyslog server. Default to `10`.
   - `io_buffer_size`: Buffer size used to write output data. Default to `65536` bytes.
@@ -221,6 +235,8 @@ This is a schematic logging configuration to show log messages from input_nameA
                        `/path/to/output/dir/%HOSTNAME%/%PROGRAMNAME:::secpath-replace%.log`
   - `remote_sub_path`: Relative path to logging_system_log_dir to store the filtered logs.
 
+  Selector options and property-based filter options are exclusive. If Property-based filter options are defined, selector options will be ignored.
+
   if both `remote_log_path` and `remote_sub_path` are _not_ specified, the remote_file output configured with the following settings.
   ```
   template(
@@ -446,32 +462,38 @@ The following playbook generates the same logging configuration files.
         outputs: [files_output0, files_output1]
 ```
 
-5. Deploying `files input` reading logs from a local file and `elasticsearch output` to store the logs. Assuming the ca_cert, cert and key to connect to Elasticsearch are prepared.
+5. Deploying `files input` reading logs from local files and `files output` to write to the local files based on the property-based filters.
 ```yaml
 ---
-- name: Deploying basic input and elasticsearch output
+- name: Deploying files input and configured files output
   hosts: all
   roles:
     - linux-system-roles.logging
   vars:
     logging_inputs:
-      - name: files_input
+      - name: files_input0
         type: files
-        input_log_path: /var/log/containers/*.log
+        input_log_path: /var/log/containerA/*.log
+      - name: files_input1
+        type: files
+        input_log_path: /var/log/containerB/*.log
     logging_outputs:
-      - name: elasticsearch_output
-        type: elasticsearch
-        server_host: your_target_host
-        server_port: 9200
-        index_prefix: project.
-        input_type: ovirt
-        ca_cert_src: /local/path/to/ca_cert
-        cert_src: /local/path/to/cert
-        private_key_src: /local/path/to/key
+      - name: files_output0
+        type: files
+        property: msg
+        prop_op: contains
+        prop_value: error
+        path: /var/log/errors.log
+      - name: files_output1
+        type: files
+        property: msg
+        prop_op: "!contains"
+        prop_value: error
+        path: /var/log/others.log
     logging_flows:
       - name: flow0
-        inputs: [files_input]
-        outputs: [elasticsearch_output]
+        inputs: [files_input0, files_input1]
+        outputs: [files_output0, files_output1]
 ```
 
 ### Client configuration
diff --git a/roles/rsyslog/templates/output_files.j2 b/roles/rsyslog/templates/output_files.j2
index d994414..e15e4cd 100644
--- a/roles/rsyslog/templates/output_files.j2
+++ b/roles/rsyslog/templates/output_files.j2
@@ -1,6 +1,8 @@
 {% if item.path is defined %}
 ruleset(name="{{ item.name }}") {
-{%   if item.exclude | d([]) %}
+{%   if item.property | d() %}
+    :{{ item.property }}, {{ item.prop_op | d('contains') }}, "{{ item.prop_value | d('error') }}" {{ item.path }}
+{%   elif item.exclude | d([]) %}
     {{ item.facility | d('*') }}.{{ item.severity | d('*') }};{{ item.exclude | join(';') }} {{ item.path }}
 {%   else %}
     {{ item.facility | d('*') }}.{{ item.severity | d('*') }} {{ item.path }}
diff --git a/roles/rsyslog/templates/output_forwards.j2 b/roles/rsyslog/templates/output_forwards.j2
index 61254ee..35030b4 100644
--- a/roles/rsyslog/templates/output_forwards.j2
+++ b/roles/rsyslog/templates/output_forwards.j2
@@ -9,7 +9,9 @@
 {%   set __forwards_protocol = '' %}
 {% endif %}
 ruleset(name="{{ item.name }}") {
-{% if item.exclude | d([]) %}
+{% if item.property | d() %}
+    :{{ item.property }}, {{ item.prop_op | d('contains') }}, "{{ item.prop_value | d('error') }}" action(name="{{ item.name }}"
+{% elif item.exclude | d([]) %}
     {{ item.facility | d('*') }}.{{ item.severity | d('*') }};{{ item.exclude | join(';') }} action(name="{{ item.name }}"
 {% else %}
     {{ item.facility | d('*') }}.{{ item.severity | d('*') }} action(name="{{ item.name }}"
diff --git a/roles/rsyslog/templates/output_remote_files.j2 b/roles/rsyslog/templates/output_remote_files.j2
index 3c9339f..aaf547e 100644
--- a/roles/rsyslog/templates/output_remote_files.j2
+++ b/roles/rsyslog/templates/output_remote_files.j2
@@ -17,7 +17,9 @@ ruleset(name="{{ item.name }}"
         queue.size="{{ logging_server_queue_size }}"
         queue.workerThreads="{{ logging_server_threads }}") {
     # Store remote logs in separate logfiles
-{%   if item.exclude | d([]) %}
+{%   if item.property | d() %}
+    :{{ item.property }}, {{ item.prop_op | d('contains') }}, "{{ item.prop_value | d('error') }}" action(name="{{ item.name }}" type="omfile" DynaFile="{{ item.name }}_template" DynaFileCacheSize="{{ item.client_count | d(10) }}" ioBufferSize="{{ item.io_buffer_size | d('65536') }}" asyncWriting="{{ 'on' if item.async_writing | d(false) | bool else 'off' }}")
+{%   elif item.exclude | d([]) %}
     {{ item.facility | d('*') }}.{{ item.severity | d('*') }};{{ item.exclude | join(';') }} action(name="{{ item.name }}" type="omfile" DynaFile="{{ item.name }}_template" DynaFileCacheSize="{{ item.client_count | d(10) }}" ioBufferSize="{{ item.io_buffer_size | d('65536') }}" asyncWriting="{{ 'on' if item.async_writing | d(false) | bool else 'off' }}")
 {%   else %}
     {{ item.facility | d('*') }}.{{ item.severity | d('*') }} action(name="{{ item.name }}" type="omfile" DynaFile="{{ item.name }}_template" DynaFileCacheSize="{{ item.client_count | d(10) }}" ioBufferSize="{{ item.io_buffer_size | d('65536') }}" asyncWriting="{{ 'on' if item.async_writing | d(false) | bool else 'off' }}")
diff --git a/tests/tests_basics_files2.yml b/tests/tests_basics_files2.yml
index 094b125..b1a0f62 100644
--- a/tests/tests_basics_files2.yml
+++ b/tests/tests_basics_files2.yml
@@ -10,9 +10,9 @@
 #    If logging role is executed, the file size is about 100 bytes.
 #    Thus, assert the size is less than 1000.
 # 2. Check file count in /etc/rsyslog.d.
-#    If logging role is executed, 8 config files are generated.
+#    If logging role is executed, 9 config files are generated.
 #    By setting logging_purge_confs, pre-existing config files are deleted.
-#    Thus, assert the the count is equal to 8.
+#    Thus, assert the the count is equal to 9.
 # 3. Check systemctl status of rsyslog as well as error or specific message in the output.
 # 4. To verify the generated filename is correct, check the config file of files output exists.
 # 4.1 Check the config file contains the expected filter and the output file as configured.
@@ -24,6 +24,8 @@
   vars:
     __test_files_conf: /etc/rsyslog.d/30-output-files-files_output1.conf
     __default_system_log: /var/log/messages
+    __prop_based_log0: /var/log/property_based_filter_in.log
+    __prop_based_log1: /var/log/property_based_filter_out.log
 
   tasks:
     - name: deploy config to output into local files
@@ -49,15 +51,23 @@
             path: :omusrmsg:*
           - name: files_output3
             type: files
-            facility: local7
-            path: /var/log/boot.log
+            property: msg
+            prop_op: contains
+            prop_value: property_based_filter_test
+            path: "{{ __prop_based_log0 }}"
+          - name: files_output4
+            type: files
+            property: msg
+            prop_op: "!contains"
+            prop_value: property_based_filter_test
+            path: "{{ __prop_based_log1 }}"
         logging_inputs:
           - name: basic_input
             type: basics
         logging_flows:
           - name: flow_0
             inputs: [basic_input]
-            outputs: [files_output0, files_output1, files_output2, files_output3]
+            outputs: [files_output0, files_output1, files_output2, files_output3, files_output4]
       include_role:
         name: linux-system-roles.logging
 
@@ -74,7 +84,7 @@
 
     - name: Check file counts in rsyslog.d
       assert:
-        that: rsyslog_d_file_count.matched == 8
+        that: rsyslog_d_file_count.matched == 9
 
     # Checking 'error' in stdout from systemctl status is for detecting the case in which rsyslog is running,
     # but some functionality is disabled due to some error, e.g., error: 'tls.cacert' file couldn't be accessed.
@@ -104,3 +114,21 @@
       retries: 5
       delay: 1
       changed_when: false
+
+    - name: Run logger to generate a test log message containing property_based_filter_test
+      command: /bin/logger -i -p local6.info -t testTag1 property_based_filter_test
+      changed_when: false
+
+    - name: Check the test log message in {{ __prop_based_log0 }}
+      command: /bin/grep property_based_filter_test "{{ __prop_based_log0 }}"
+      register: __result
+      until: __result is success
+      retries: 5
+      delay: 1
+      changed_when: false
+
+    - name: Check the test log message not in {{ __prop_based_log1 }}
+      command: /bin/grep property_based_filter_test "{{ __prop_based_log1 }}"
+      register: __result
+      changed_when: false
+      failed_when: "__result is not failed"
diff --git a/tests/tests_basics_forwards_cert.yml b/tests/tests_basics_forwards_cert.yml
index e27e016..48263ae 100644
--- a/tests/tests_basics_forwards_cert.yml
+++ b/tests/tests_basics_forwards_cert.yml
@@ -139,3 +139,11 @@
         - /etc/pki/tls/certs/{{ __test_ca_cert_name }}
         - /etc/pki/tls/certs/{{ __test_cert_name }}
         - /etc/pki/tls/private/{{ __test_key_name }}
+
+    - name: clean up test files
+      file: path="{{ item }}" state=absent
+      loop:
+        - "{{ __test_ca_cert }}"
+        - "{{ __test_cert }}"
+        - "{{ __test_key }}"
+      delegate_to: localhost
diff --git a/tests/tests_basics_forwards_cert_missing.yml b/tests/tests_basics_forwards_cert_missing.yml
index 3e82856..0ad0569 100644
--- a/tests/tests_basics_forwards_cert_missing.yml
+++ b/tests/tests_basics_forwards_cert_missing.yml
@@ -63,6 +63,10 @@
           assert:
             that: "'{{ ansible_failed_result.results.0.msg }}' is match('{{ __expected_error }}')"
 
+    - name: clean up test files
+      file: path="{{ __test_key }}" state=absent
+      delegate_to: localhost
+
     - name: default run for cleanup
       vars:
         logging_inputs:
diff --git a/tests/tests_server_conflict.yml b/tests/tests_server_conflict.yml
index 36eeeb7..8c182f6 100644
--- a/tests/tests_server_conflict.yml
+++ b/tests/tests_server_conflict.yml
@@ -76,3 +76,11 @@
         - assert:
             that: item.msg is not defined or item.msg is defined and item.msg == __expected_error
           loop: "{{ ansible_failed_result.results }}"
+
+    - name: clean up test files
+      file: path="{{ item }}" state=absent
+      loop:
+        - "{{ __test_ca_cert }}"
+        - "{{ __test_cert }}"
+        - "{{ __test_key }}"
+      delegate_to: localhost
-- 
2.26.2