Add feature: support multiple file-list arguments
This update allows users to provide the download list parameter multiple times. This would helps avoid situations where only one file is modified, and users can now provide a list of files to be downloaded later by logscraper and parsed for tags by logsender. Change-Id: If9adeb5f9be1e7d79ac05076589374a045246668
This commit is contained in:
@@ -28,7 +28,7 @@ for example:
|
|||||||
zuul_api_url:
|
zuul_api_url:
|
||||||
- https://zuul.opendev.org/api/tenant/openstack
|
- https://zuul.opendev.org/api/tenant/openstack
|
||||||
insecure: false
|
insecure: false
|
||||||
file_list: /etc/logscraper/download-list-TENANT.yaml
|
file_list: ['/etc/logscraper/download-list-TENANT.yaml']
|
||||||
|
|
||||||
will deploy service with name: `logscraper@openstack.service`.
|
will deploy service with name: `logscraper@openstack.service`.
|
||||||
It is because on one service we are able to deploy multiple instances
|
It is because on one service we are able to deploy multiple instances
|
||||||
@@ -68,7 +68,8 @@ and second one for getting logs from `sometenant` tenant.
|
|||||||
insecure: True
|
insecure: True
|
||||||
download: true
|
download: true
|
||||||
download_dir: /mnt/logscraper
|
download_dir: /mnt/logscraper
|
||||||
file_list: /etc/logscraper/my-downloadlist.yaml
|
file_list:
|
||||||
|
- /etc/logscraper/my-downloadlist.yaml
|
||||||
roles:
|
roles:
|
||||||
- logscraper
|
- logscraper
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ container_images:
|
|||||||
# insecure: true
|
# insecure: true
|
||||||
# download: true
|
# download: true
|
||||||
# download_dir: /mnt/logscraper/sometenant
|
# download_dir: /mnt/logscraper/sometenant
|
||||||
# file_list: ""
|
# file_list: []
|
||||||
# job_name:
|
# job_name:
|
||||||
# - test
|
# - test
|
||||||
# - test_new
|
# - test_new
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ workers: {{ item['logscraper_workers'] | default(1) }}
|
|||||||
max_skipped: {{ item['max_skipped'] | default(1000) }}
|
max_skipped: {{ item['max_skipped'] | default(1000) }}
|
||||||
debug: {{ item['debug'] | default(false) }}
|
debug: {{ item['debug'] | default(false) }}
|
||||||
download: {{ item['download'] | default(true) }}
|
download: {{ item['download'] | default(true) }}
|
||||||
file_list: {{ item['file_list'] | default(logscraper_dir + '/download-list-' + item['tenant'] + '.yaml') }}
|
file_list: {{ item['file_list'] | default([logscraper_dir + '/download-list-' + item['tenant'] + '.yaml']) | list }}
|
||||||
directory: {{ item['download_dir'] | default('/tmp/logscraper') }}
|
directory: {{ item['download_dir'] | default('/tmp/logscraper') }}
|
||||||
wait_time: {{ item['logscraper_wait_time'] | default(120) }}
|
wait_time: {{ item['logscraper_wait_time'] | default(120) }}
|
||||||
insecure: {{ item['insecure'] | default(false) }}
|
insecure: {{ item['insecure'] | default(false) }}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
--uidmap 1000:{{ logscraper_uid }}:1 \
|
--uidmap 1000:{{ logscraper_uid }}:1 \
|
||||||
--name logscraper-{{ item.tenant }} \
|
--name logscraper-{{ item.tenant }} \
|
||||||
--volume {{ item.logscraper_dir | default(logscraper_dir) }}:{{ logscraper_dir }}:z \
|
--volume {{ item.logscraper_dir | default(logscraper_dir) }}:{{ logscraper_dir }}:z \
|
||||||
--volume {{ item['file_list'] | default(logscraper_dir + '/download-list-' + item['tenant'] + '.yaml') }}:{{ item['file_list'] | default(logscraper_dir + '/download-list-' + item['tenant'] + '.yaml') }}:z \
|
{% for file_list in item['file_list'] | default([logscraper_dir + '/download-list-' + item['tenant'] + '.yaml']) -%}
|
||||||
|
--volume {{ file_list }}:{{ file_list }}:z \
|
||||||
|
{% endfor -%}
|
||||||
{% if 'logscraper_custom_ca_crt' in item %}
|
{% if 'logscraper_custom_ca_crt' in item %}
|
||||||
--volume {{ item['logscraper_custom_ca_crt'] }}:{{ item['logscraper_custom_ca_crt'] }}:z \
|
--volume {{ item['logscraper_custom_ca_crt'] }}:{{ item['logscraper_custom_ca_crt'] }}:z \
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Example Ansible variables that are configuring service:
|
|||||||
es_insecure: true
|
es_insecure: true
|
||||||
es_index: logstash-logscraper
|
es_index: logstash-logscraper
|
||||||
download_dir: /mnt/logscraper/sometenant
|
download_dir: /mnt/logscraper/sometenant
|
||||||
file_list: /etc/logsender/download-list-TENANT.yaml
|
file_list: ['/etc/logsender/download-list-TENANT.yaml']
|
||||||
|
|
||||||
|
|
||||||
That configuration will will deploy service with name: `logsender-openstack.service`.
|
That configuration will will deploy service with name: `logsender-openstack.service`.
|
||||||
@@ -76,7 +76,8 @@ and second one for getting logs from `sometenant` tenant.
|
|||||||
es_index: ""
|
es_index: ""
|
||||||
es_index_prefix: ""
|
es_index_prefix: ""
|
||||||
download_dir: /mnt/logscraper/sometenant
|
download_dir: /mnt/logscraper/sometenant
|
||||||
file_list: /etc/logscraper/my-downloadlist.yaml
|
file_list:
|
||||||
|
- /etc/logscraper/my-downloadlist.yaml
|
||||||
roles:
|
roles:
|
||||||
- logsender
|
- logsender
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ container_images:
|
|||||||
# es_index: ""
|
# es_index: ""
|
||||||
# es_insecure: false
|
# es_insecure: false
|
||||||
# download_dir: /mnt/logscraper/sometenant
|
# download_dir: /mnt/logscraper/sometenant
|
||||||
|
# file_list: []
|
||||||
# doc_type: "_doc"
|
# doc_type: "_doc"
|
||||||
# logsender_workers: 1
|
# logsender_workers: 1
|
||||||
# chunk_size: 1500
|
# chunk_size: 1500
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ follow: {{ item['follow'] | default(true) }}
|
|||||||
workers: {{ item['logsender_workers'] | default(1) }}
|
workers: {{ item['logsender_workers'] | default(1) }}
|
||||||
ignore_es_status: {{ item['ignore_es_status'] | default(false) }}
|
ignore_es_status: {{ item['ignore_es_status'] | default(false) }}
|
||||||
directory: {{ item['download_dir'] | default('/tmp/logscraper') }}
|
directory: {{ item['download_dir'] | default('/tmp/logscraper') }}
|
||||||
file_list: {{ item['file_list'] | default(logscraper_dir + '/download-list-' + item['tenant'] + '.yaml') }}
|
file_list: {{ item['file_list'] | default([logscraper_dir + '/download-list-' + item['tenant'] + '.yaml']) | list }}
|
||||||
performance_index_prefix: {{ item['performance_index_prefix'] | default('') }}
|
performance_index_prefix: {{ item['performance_index_prefix'] | default('') }}
|
||||||
subunit_index_prefix: {{ item['subunit_index_prefix'] | default('') }}
|
subunit_index_prefix: {{ item['subunit_index_prefix'] | default('') }}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
--name logsender-{{ item.tenant }} \
|
--name logsender-{{ item.tenant }} \
|
||||||
--volume {{ item.download_dir }}:{{ item.download_dir }}:z \
|
--volume {{ item.download_dir }}:{{ item.download_dir }}:z \
|
||||||
--volume {{ item.logscraper_dir | default(logscraper_dir) }}:{{ logscraper_dir }}:z \
|
--volume {{ item.logscraper_dir | default(logscraper_dir) }}:{{ logscraper_dir }}:z \
|
||||||
--volume {{ item['file_list'] | default(logscraper_dir + '/download-list-' + item['tenant'] + '.yaml') }}:{{ item['file_list'] | default(logscraper_dir + '/download-list-' + item['tenant'] + '.yaml') }}:z \
|
{% for file_list in item['file_list'] | default([logscraper_dir + '/download-list-' + item['tenant'] + '.yaml']) -%}
|
||||||
|
--volume {{ file_list }}:{{ file_list }}:z \
|
||||||
|
{% endfor -%}
|
||||||
{% if 'logsender_custom_ca_crt' in item %}
|
{% if 'logsender_custom_ca_crt' in item %}
|
||||||
--volume {{ item['logsender_custom_ca_crt'] }}:{{ item['logsender_custom_ca_crt'] }}:z \
|
--volume {{ item['logsender_custom_ca_crt'] }}:{{ item['logsender_custom_ca_crt'] }}:z \
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ workers: 1
|
|||||||
max_skipped: 1000
|
max_skipped: 1000
|
||||||
debug: False
|
debug: False
|
||||||
download: True
|
download: True
|
||||||
file_list: logscraper/download-list.yaml.sample
|
file_list: ['logscraper/download-list.yaml.sample']
|
||||||
directory: /tmp/logscraper
|
directory: /tmp/logscraper
|
||||||
wait_time: 120
|
wait_time: 120
|
||||||
insecure: False
|
insecure: False
|
||||||
|
|||||||
@@ -133,7 +133,9 @@ def get_arguments():
|
|||||||
"CI job logs into gearman.")
|
"CI job logs into gearman.")
|
||||||
parser.add_argument("--config", help="Logscraper config file",
|
parser.add_argument("--config", help="Logscraper config file",
|
||||||
required=True)
|
required=True)
|
||||||
parser.add_argument("--file-list", help="File list to download")
|
parser.add_argument("--file-list", help="File list to download. Parameter "
|
||||||
|
"can be set multiple times.", default=[],
|
||||||
|
action='append')
|
||||||
parser.add_argument("--zuul-api-url", help="URL(s) for Zuul API. Parameter"
|
parser.add_argument("--zuul-api-url", help="URL(s) for Zuul API. Parameter"
|
||||||
" can be set multiple times.", nargs='+', default=[])
|
" can be set multiple times.", nargs='+', default=[])
|
||||||
parser.add_argument("--job-name", help="CI job name(s). Parameter can be "
|
parser.add_argument("--job-name", help="CI job name(s). Parameter can be "
|
||||||
@@ -496,10 +498,31 @@ def save_build_info(directory, build):
|
|||||||
yaml.dump(build, text_file)
|
yaml.dump(build, text_file)
|
||||||
|
|
||||||
|
|
||||||
|
def merge_dicts(main, additional):
|
||||||
|
merged = main.copy()
|
||||||
|
|
||||||
|
for k, v in additional.items():
|
||||||
|
if k in merged:
|
||||||
|
if isinstance(merged[k], list) and isinstance(v, list):
|
||||||
|
merged[k].extend(v)
|
||||||
|
elif isinstance(merged[k], dict) and isinstance(v, dict):
|
||||||
|
merged[k] = merge_dicts(merged[k], v)
|
||||||
|
else:
|
||||||
|
logging.critical("Trying to merge incompatible "
|
||||||
|
"types", type(merged[k]), type(v))
|
||||||
|
else:
|
||||||
|
merged[k] = v
|
||||||
|
|
||||||
|
return merged
|
||||||
|
|
||||||
|
|
||||||
def load_config(config_path):
|
def load_config(config_path):
|
||||||
try:
|
try:
|
||||||
with open(config_path) as f:
|
merged_config = {}
|
||||||
return yaml.safe_load(f)
|
for yaml_file in config_path:
|
||||||
|
with open(yaml_file) as f:
|
||||||
|
merged_config = merge_dicts(merged_config, yaml.safe_load(f))
|
||||||
|
return merged_config
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
logging.critical("Can not open config file %s" % config_path)
|
logging.critical("Can not open config file %s" % config_path)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ insecure: False
|
|||||||
ca_file:
|
ca_file:
|
||||||
follow: True
|
follow: True
|
||||||
workers: 1
|
workers: 1
|
||||||
file_list: logscraper/download-list.yaml.sample
|
file_list: ['logscraper/download-list.yaml.sample']
|
||||||
performance_index_prefix: performance-
|
performance_index_prefix: performance-
|
||||||
subunit_index_prefix: subunit-
|
subunit_index_prefix: subunit-
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ from pathlib import Path
|
|||||||
from ruamel.yaml import YAML
|
from ruamel.yaml import YAML
|
||||||
from subunit2sql.read_subunit import ReadSubunit
|
from subunit2sql.read_subunit import ReadSubunit
|
||||||
|
|
||||||
|
try:
|
||||||
|
from logscraper import load_config as l_config
|
||||||
|
except ImportError:
|
||||||
|
from logscraper.logscraper import load_config as l_config
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# CLI #
|
# CLI #
|
||||||
@@ -50,7 +55,9 @@ def get_arguments():
|
|||||||
"and push to the Opensearch service")
|
"and push to the Opensearch service")
|
||||||
parser.add_argument("--config", help="Logscraper config file",
|
parser.add_argument("--config", help="Logscraper config file",
|
||||||
required=True)
|
required=True)
|
||||||
parser.add_argument("--file-list", help="File list to download")
|
parser.add_argument("--file-list", help="File list to download. Parameter "
|
||||||
|
"can be set multiple times.", default=[],
|
||||||
|
action='append')
|
||||||
parser.add_argument("--directory",
|
parser.add_argument("--directory",
|
||||||
help="Directory, where the logs will "
|
help="Directory, where the logs will "
|
||||||
"be stored.")
|
"be stored.")
|
||||||
@@ -330,13 +337,11 @@ def open_file(path):
|
|||||||
|
|
||||||
|
|
||||||
def get_file_info(config, build_file):
|
def get_file_info(config, build_file):
|
||||||
yaml = YAML()
|
config_files = l_config(config)
|
||||||
with open_file(config) as f:
|
for f in config_files["files"]:
|
||||||
config_files = yaml.load(f)
|
file_name = os.path.basename(f["name"])
|
||||||
for f in config_files["files"]:
|
if build_file.endswith(file_name):
|
||||||
file_name = os.path.basename(f["name"])
|
return f["name"], f.get('tags', []) + [file_name]
|
||||||
if build_file.endswith(file_name):
|
|
||||||
return f["name"], f.get('tags', []) + [file_name]
|
|
||||||
return os.path.basename(build_file), [os.path.basename(build_file)]
|
return os.path.basename(build_file), [os.path.basename(build_file)]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -552,6 +552,61 @@ class TestScraper(base.TestCase):
|
|||||||
10)
|
10)
|
||||||
self.assertEqual(2, len(files))
|
self.assertEqual(2, len(files))
|
||||||
|
|
||||||
|
@mock.patch('yaml.safe_load')
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open())
|
||||||
|
def test_load_config(self, mock_open, mock_yaml):
|
||||||
|
config_path = ['/tmp/config_1', '/tmp/config_2']
|
||||||
|
config_1 = {'files': [{
|
||||||
|
'name': 'job-output.txt',
|
||||||
|
'tags': ['console', 'console.html']
|
||||||
|
}, {'name': 'logs/undercloud/var/log/extra/logstash.txt',
|
||||||
|
'tags': ['console', 'postpci']}]}
|
||||||
|
config_2 = {'files': [{
|
||||||
|
'name': 'new-job.txt',
|
||||||
|
'tags': ['new-console']
|
||||||
|
}, {'name': 'logs/some-file.log',
|
||||||
|
'tags': ['postpci']}]}
|
||||||
|
final_config = {'files': [{
|
||||||
|
'name': 'job-output.txt',
|
||||||
|
'tags': ['console', 'console.html']
|
||||||
|
}, {
|
||||||
|
'name': 'logs/undercloud/var/log/extra/logstash.txt',
|
||||||
|
'tags': ['console', 'postpci']
|
||||||
|
}, {
|
||||||
|
'name': 'new-job.txt', 'tags': ['new-console']
|
||||||
|
}, {
|
||||||
|
'name': 'logs/some-file.log', 'tags': ['postpci']}]}
|
||||||
|
mock_yaml.side_effect = [config_1, config_2]
|
||||||
|
parsed_config = logscraper.load_config(config_path)
|
||||||
|
self.assertEqual(final_config, parsed_config)
|
||||||
|
|
||||||
|
@mock.patch('yaml.safe_load')
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open())
|
||||||
|
def test_load_config_different_keys(self, mock_open, mock_yaml):
|
||||||
|
config_path = ['/tmp/config_1', '/tmp/config_2']
|
||||||
|
config_1 = {'files': [{
|
||||||
|
'name': 'job-output.txt',
|
||||||
|
'tags': ['console', 'console.html']
|
||||||
|
}, {'name': 'logs/undercloud/var/log/extra/logstash.txt',
|
||||||
|
'tags': ['console', 'postpci']}]}
|
||||||
|
config_2 = {'files2': [{
|
||||||
|
'name': 'new-job.txt',
|
||||||
|
'tags': ['new-console']
|
||||||
|
}, {'name': 'logs/some-file.log',
|
||||||
|
'tags': ['postpci']}]}
|
||||||
|
final_config = {'files': [{
|
||||||
|
'name': 'job-output.txt',
|
||||||
|
'tags': ['console', 'console.html']
|
||||||
|
}, {'name': 'logs/undercloud/var/log/extra/logstash.txt',
|
||||||
|
'tags': ['console', 'postpci']}
|
||||||
|
], 'files2': [{
|
||||||
|
'name': 'new-job.txt',
|
||||||
|
'tags': ['new-console']
|
||||||
|
}, {'name': 'logs/some-file.log', 'tags': ['postpci']}]}
|
||||||
|
mock_yaml.side_effect = [config_1, config_2]
|
||||||
|
parsed_config = logscraper.load_config(config_path)
|
||||||
|
self.assertEqual(final_config, parsed_config)
|
||||||
|
|
||||||
|
|
||||||
class TestConfig(base.TestCase):
|
class TestConfig(base.TestCase):
|
||||||
@mock.patch('logscraper.logscraper.load_config')
|
@mock.patch('logscraper.logscraper.load_config')
|
||||||
|
|||||||
@@ -1157,9 +1157,12 @@ class TestSender(base.TestCase):
|
|||||||
got = logsender.get_timestamp(line)
|
got = logsender.get_timestamp(line)
|
||||||
self.assertEqual(expected, got)
|
self.assertEqual(expected, got)
|
||||||
|
|
||||||
@mock.patch('ruamel.yaml.YAML.load')
|
@mock.patch('yaml.safe_load')
|
||||||
|
@mock.patch('builtins.open', new_callable=mock.mock_open())
|
||||||
|
@mock.patch('logscraper.logscraper.load_config')
|
||||||
@mock.patch('logscraper.logsender.open_file')
|
@mock.patch('logscraper.logsender.open_file')
|
||||||
def test_get_file_info(self, mock_open_file, mock_yaml):
|
def test_get_file_info(self, mock_open_file, mock_load_config, mock_open,
|
||||||
|
mock_yaml):
|
||||||
config = {'files': [{
|
config = {'files': [{
|
||||||
'name': 'job-output.txt',
|
'name': 'job-output.txt',
|
||||||
'tags': ['console', 'console.html']
|
'tags': ['console', 'console.html']
|
||||||
|
|||||||
Reference in New Issue
Block a user