mirror of
https://github.com/ansible/awx.git
synced 2026-05-15 05:17:36 -02:30
Tweaks to Test Button logic and cleans up flake8 and test failures
This commit is contained in:
@@ -325,17 +325,3 @@ def test_setting_singleton_delete_no_read_only_fields(api_request, dummy_setting
|
|||||||
)
|
)
|
||||||
assert response.data['FOO_BAR'] == 23
|
assert response.data['FOO_BAR'] == 23
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
|
||||||
def test_setting_logging_test(api_request):
|
|
||||||
with mock.patch('awx.conf.views.AWXProxyHandler.perform_test') as mock_func:
|
|
||||||
api_request(
|
|
||||||
'post',
|
|
||||||
reverse('api:setting_logging_test'),
|
|
||||||
data={'LOG_AGGREGATOR_HOST': 'http://foobar', 'LOG_AGGREGATOR_TYPE': 'logstash'}
|
|
||||||
)
|
|
||||||
call = mock_func.call_args_list[0]
|
|
||||||
args, kwargs = call
|
|
||||||
given_settings = kwargs['custom_settings']
|
|
||||||
assert given_settings.LOG_AGGREGATOR_HOST == 'http://foobar'
|
|
||||||
assert given_settings.LOG_AGGREGATOR_TYPE == 'logstash'
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import collections
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import socket
|
import socket
|
||||||
import os
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
from socket import SHUT_RDWR
|
from socket import SHUT_RDWR
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
@@ -165,18 +163,17 @@ class SettingLoggingTest(GenericAPIView):
|
|||||||
filter_backends = []
|
filter_backends = []
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
# Error if logging is not enabled
|
||||||
|
enabled = getattr(settings, 'LOG_AGGREGATOR_ENABLED', False)
|
||||||
|
if not enabled:
|
||||||
|
return Response({'error': 'Logging not enabled'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# Send test message to configured logger based on db settings
|
# Send test message to configured logger based on db settings
|
||||||
logging.getLogger('awx').error('AWX Connection Test Message')
|
logging.getLogger('awx').error('AWX Connection Test Message')
|
||||||
|
|
||||||
hostname = getattr(settings, 'LOG_AGGREGATOR_HOST', None)
|
hostname = getattr(settings, 'LOG_AGGREGATOR_HOST', None)
|
||||||
protocol = getattr(settings, 'LOG_AGGREGATOR_PROTOCOL', None)
|
protocol = getattr(settings, 'LOG_AGGREGATOR_PROTOCOL', None)
|
||||||
|
|
||||||
# Check if host is reacheable
|
|
||||||
host = urlparse(hostname).netloc
|
|
||||||
response = os.system("ping -c 1 " + host)
|
|
||||||
if response != 0:
|
|
||||||
return Response({'error': 'The host is not available'}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# Check to ensure port is open at host
|
# Check to ensure port is open at host
|
||||||
if protocol in ['udp', 'tcp']:
|
if protocol in ['udp', 'tcp']:
|
||||||
port = getattr(settings, 'LOG_AGGREGATOR_PORT', None)
|
port = getattr(settings, 'LOG_AGGREGATOR_PORT', None)
|
||||||
@@ -184,14 +181,9 @@ class SettingLoggingTest(GenericAPIView):
|
|||||||
if not port:
|
if not port:
|
||||||
return Response({'error': 'Port required for ' + protocol}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': 'Port required for ' + protocol}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
else:
|
||||||
|
# if http/https by this point, domain is reacheable
|
||||||
return Response(status=status.HTTP_202_ACCEPTED)
|
return Response(status=status.HTTP_202_ACCEPTED)
|
||||||
|
|
||||||
# Error if logging is not enabled
|
|
||||||
enabled = getattr(settings, 'LOG_AGGREGATOR_ENABLED', False)
|
|
||||||
if not enabled:
|
|
||||||
return Response({'error': 'Logging not enabled'}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
s.settimeout(.5)
|
s.settimeout(.5)
|
||||||
s.connect((hostname, int(port)))
|
s.connect((hostname, int(port)))
|
||||||
|
|||||||
@@ -5,17 +5,13 @@
|
|||||||
# Python
|
# Python
|
||||||
import pytest
|
import pytest
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
# Mock
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.api.versioning import reverse
|
from awx.api.versioning import reverse
|
||||||
from awx.conf.models import Setting
|
from awx.conf.models import Setting
|
||||||
from awx.main.utils.handlers import AWXProxyHandler, LoggingConnectivityException
|
from awx.conf.registry import settings_registry
|
||||||
|
|
||||||
|
|
||||||
TEST_GIF_LOGO = 'data:image/gif;base64,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' # NOQA
|
TEST_GIF_LOGO = 'data:image/gif;base64,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' # NOQA
|
||||||
@@ -237,78 +233,94 @@ def test_ui_settings(get, put, patch, delete, admin):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_logging_aggregrator_connection_test_requires_superuser(get, post, alice):
|
def test_logging_aggregator_connection_test_requires_superuser(post, alice):
|
||||||
url = reverse('api:setting_logging_test')
|
url = reverse('api:setting_logging_test')
|
||||||
post(url, {}, user=alice, expect=403)
|
post(url, {}, user=alice, expect=403)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('key', [
|
|
||||||
'LOG_AGGREGATOR_TYPE',
|
|
||||||
'LOG_AGGREGATOR_HOST',
|
|
||||||
])
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_logging_aggregrator_connection_test_bad_request(get, post, admin, key):
|
def test_logging_aggregator_connection_test_not_enabled(post, admin):
|
||||||
url = reverse('api:setting_logging_test')
|
url = reverse('api:setting_logging_test')
|
||||||
resp = post(url, {}, user=admin, expect=400)
|
resp = post(url, {}, user=admin, expect=400)
|
||||||
assert 'This field is required.' in resp.data.get(key, [])
|
assert 'Logging not enabled' in resp.data.get('error')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
def _mock_logging_defaults():
|
||||||
def test_logging_aggregrator_connection_test_valid(mocker, get, post, admin):
|
# Pre-populate settings obj with defaults
|
||||||
with mock.patch.object(AWXProxyHandler, 'perform_test') as perform_test:
|
class MockSettings:
|
||||||
url = reverse('api:setting_logging_test')
|
|
||||||
user_data = {
|
|
||||||
'LOG_AGGREGATOR_TYPE': 'logstash',
|
|
||||||
'LOG_AGGREGATOR_HOST': 'localhost',
|
|
||||||
'LOG_AGGREGATOR_PORT': 8080,
|
|
||||||
'LOG_AGGREGATOR_USERNAME': 'logger',
|
|
||||||
'LOG_AGGREGATOR_PASSWORD': 'mcstash'
|
|
||||||
}
|
|
||||||
post(url, user_data, user=admin, expect=200)
|
|
||||||
args, kwargs = perform_test.call_args_list[0]
|
|
||||||
create_settings = kwargs['custom_settings']
|
|
||||||
for k, v in user_data.items():
|
|
||||||
assert hasattr(create_settings, k)
|
|
||||||
assert getattr(create_settings, k) == v
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
|
||||||
def test_logging_aggregrator_connection_test_with_masked_password(mocker, patch, post, admin):
|
|
||||||
url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'logging'})
|
|
||||||
patch(url, user=admin, data={'LOG_AGGREGATOR_PASSWORD': 'password123'}, expect=200)
|
|
||||||
time.sleep(1) # log settings are cached slightly
|
|
||||||
|
|
||||||
with mock.patch.object(AWXProxyHandler, 'perform_test') as perform_test:
|
|
||||||
url = reverse('api:setting_logging_test')
|
|
||||||
user_data = {
|
|
||||||
'LOG_AGGREGATOR_TYPE': 'logstash',
|
|
||||||
'LOG_AGGREGATOR_HOST': 'localhost',
|
|
||||||
'LOG_AGGREGATOR_PORT': 8080,
|
|
||||||
'LOG_AGGREGATOR_USERNAME': 'logger',
|
|
||||||
'LOG_AGGREGATOR_PASSWORD': '$encrypted$'
|
|
||||||
}
|
|
||||||
post(url, user_data, user=admin, expect=200)
|
|
||||||
args, kwargs = perform_test.call_args_list[0]
|
|
||||||
create_settings = kwargs['custom_settings']
|
|
||||||
assert getattr(create_settings, 'LOG_AGGREGATOR_PASSWORD') == 'password123'
|
|
||||||
|
|
||||||
# Update these tests ^^
|
|
||||||
# Test the `/api/v2/settings/logging/test` functionality
|
|
||||||
def test_logging_test():
|
|
||||||
pass
|
pass
|
||||||
|
mock_settings_obj = MockSettings()
|
||||||
|
mock_settings_json = dict()
|
||||||
|
for key in settings_registry.get_registered_settings(category_slug='logging'):
|
||||||
|
value = settings_registry.get_setting_field(key).get_default()
|
||||||
|
setattr(mock_settings_obj, key, value)
|
||||||
|
mock_settings_json[key] = value
|
||||||
|
return mock_settings_obj, mock_settings_json
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('key, value, error', [
|
||||||
|
['LOG_AGGREGATOR_TYPE', 'logstash', 'Cannot enable log aggregator without providing host.'],
|
||||||
|
['LOG_AGGREGATOR_HOST', 'https://logstash', 'Cannot enable log aggregator without providing type.']
|
||||||
|
])
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_logging_aggregator_missing_settings(put, post, admin, key, value, error):
|
||||||
|
_, mock_settings = _mock_logging_defaults()
|
||||||
|
mock_settings['LOG_AGGREGATOR_ENABLED'] = True
|
||||||
|
mock_settings[key] = value
|
||||||
|
url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'logging'})
|
||||||
|
response = put(url, data=mock_settings, user=admin, expect=400)
|
||||||
|
assert error in str(response.data)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('type, host, port, username, password', [
|
||||||
|
['logstash', 'localhost', 8080, 'logger', 'mcstash'],
|
||||||
|
['loggly', 'http://logs-01.loggly.com/inputs/1fd38090-hash-h4a$h-8d80-t0k3n71/tag/http/', None, None, None],
|
||||||
|
['splunk', 'https://yoursplunk:8088/services/collector/event', None, None, None],
|
||||||
|
['other', '97.221.40.41', 9000, 'logger', 'mcstash'],
|
||||||
|
['sumologic', 'https://endpoint5.collection.us2.sumologic.com/receiver/v1/http/Zagnw_f9XGr_zZgd-_EPM0hb8_rUU7_RU8Q==',
|
||||||
|
None, None, None]
|
||||||
|
])
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_logging_aggregator_valid_settings(put, post, admin, type, host, port, username, password):
|
||||||
|
_, mock_settings = _mock_logging_defaults()
|
||||||
|
# type = 'splunk'
|
||||||
|
# host = 'https://yoursplunk:8088/services/collector/event'
|
||||||
|
mock_settings['LOG_AGGREGATOR_ENABLED'] = True
|
||||||
|
mock_settings['LOG_AGGREGATOR_TYPE'] = type
|
||||||
|
mock_settings['LOG_AGGREGATOR_HOST'] = host
|
||||||
|
if port:
|
||||||
|
mock_settings['LOG_AGGREGATOR_PORT'] = port
|
||||||
|
if username:
|
||||||
|
mock_settings['LOG_AGGREGATOR_USERNAME'] = username
|
||||||
|
if password:
|
||||||
|
mock_settings['LOG_AGGREGATOR_PASSWORD'] = password
|
||||||
|
url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'logging'})
|
||||||
|
response = put(url, data=mock_settings, user=admin, expect=200)
|
||||||
|
assert type in response.data.get('LOG_AGGREGATOR_TYPE')
|
||||||
|
assert host in response.data.get('LOG_AGGREGATOR_HOST')
|
||||||
|
if port:
|
||||||
|
assert port == response.data.get('LOG_AGGREGATOR_PORT')
|
||||||
|
if username:
|
||||||
|
assert username in response.data.get('LOG_AGGREGATOR_USERNAME')
|
||||||
|
if password: # Note: password should be encrypted
|
||||||
|
assert '$encrypted$' in response.data.get('LOG_AGGREGATOR_PASSWORD')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_logging_aggregrator_connection_test_invalid(mocker, get, post, admin):
|
def test_logging_aggregator_connection_test_valid(put, post, admin):
|
||||||
with mock.patch.object(AWXProxyHandler, 'perform_test') as perform_test:
|
_, mock_settings = _mock_logging_defaults()
|
||||||
perform_test.side_effect = LoggingConnectivityException('404: Not Found')
|
type = 'other'
|
||||||
url = reverse('api:setting_logging_test')
|
host = 'https://localhost'
|
||||||
resp = post(url, {
|
mock_settings['LOG_AGGREGATOR_ENABLED'] = True
|
||||||
'LOG_AGGREGATOR_TYPE': 'logstash',
|
mock_settings['LOG_AGGREGATOR_TYPE'] = type
|
||||||
'LOG_AGGREGATOR_HOST': 'localhost',
|
mock_settings['LOG_AGGREGATOR_HOST'] = host
|
||||||
'LOG_AGGREGATOR_PORT': 8080
|
# POST to save these mock settings
|
||||||
}, user=admin, expect=500)
|
url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'logging'})
|
||||||
assert resp.data == {'error': '404: Not Found'}
|
put(url, data=mock_settings, user=admin, expect=200)
|
||||||
|
# "Test" the logger
|
||||||
|
url = reverse('api:setting_logging_test')
|
||||||
|
post(url, {}, user=admin, expect=202)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from awx.main.utils.external_logging import construct_rsyslog_conf_template
|
from django.conf import settings
|
||||||
from awx.conf import settings_registry
|
|
||||||
|
|
||||||
|
from awx.main.utils.external_logging import construct_rsyslog_conf_template
|
||||||
|
from awx.main.tests.functional.api.test_settings import _mock_logging_defaults
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# Example User Data
|
# Example User Data
|
||||||
@@ -36,48 +37,53 @@ data_loggly = {
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
'enabled, type, host, port, protocol, expected_config', [
|
'enabled, type, host, port, protocol, expected_config', [
|
||||||
(True, 'loggly', 'http://logs-01.loggly.com/inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/', None, 'https',
|
(True, 'loggly', 'http://logs-01.loggly.com/inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/', None, 'https',
|
||||||
'$IncludeConfig /etc/rsyslog.conf\ninput(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")\ntemplate(name="awx" type="string" string="%msg%")\nmodule(load="omhttp")\naction(type="omhttp" server="logs-01.loggly.com" serverport="80" usehttps="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/")'),
|
'''$IncludeConfig /etc/rsyslog.conf\ninput(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
||||||
|
template(name="awx" type="string" string="%msg%")\nmodule(load="omhttp")
|
||||||
|
action(type="omhttp" server="logs-01.loggly.com" serverport="80" usehttps="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" ''' +
|
||||||
|
'errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/")'),
|
||||||
(True, 'other', 'localhost', 9000, 'udp',
|
(True, 'other', 'localhost', 9000, 'udp',
|
||||||
'$IncludeConfig /etc/rsyslog.conf\ninput(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")\ntemplate(name="awx" type="string" string="%msg%")\naction(type="omfwd" target="localhost" port="9000" protocol="udp" action.resumeRetryCount="-1" template="awx")'),
|
'''$IncludeConfig /etc/rsyslog.conf
|
||||||
|
input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
||||||
|
template(name="awx" type="string" string="%msg%")
|
||||||
|
action(type="omfwd" target="localhost" port="9000" protocol="udp" action.resumeRetryCount="-1" template="awx")'''),
|
||||||
(True, 'other', 'localhost', 9000, 'tcp',
|
(True, 'other', 'localhost', 9000, 'tcp',
|
||||||
'$IncludeConfig /etc/rsyslog.conf\ninput(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")\ntemplate(name="awx" type="string" string="%msg%")\naction(type="omfwd" target="localhost" port="9000" protocol="tcp" action.resumeRetryCount="-1" template="awx")'),
|
'''$IncludeConfig /etc/rsyslog.conf
|
||||||
|
input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
||||||
|
template(name="awx" type="string" string="%msg%")
|
||||||
|
action(type="omfwd" target="localhost" port="9000" protocol="tcp" action.resumeRetryCount="-1" template="awx")'''),
|
||||||
(False, 'loggly', 'http://logs-01.loggly.com/inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/', 8080, 'https',
|
(False, 'loggly', 'http://logs-01.loggly.com/inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/', 8080, 'https',
|
||||||
'$IncludeConfig /etc/rsyslog.conf'),
|
'''$IncludeConfig /etc/rsyslog.conf'''),
|
||||||
(True, 'splunk', 'https://yoursplunk:8088/services/collector/event', None, None,
|
(True, 'splunk', 'https://yoursplunk:8088/services/collector/event', None, None,
|
||||||
'''$IncludeConfig /etc/rsyslog.conf
|
'''$IncludeConfig /etc/rsyslog.conf
|
||||||
input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
||||||
template(name="awx" type="string" string="%msg%")
|
template(name="awx" type="string" string="%msg%")
|
||||||
module(load="omhttp")
|
module(load="omhttp")
|
||||||
action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'''),
|
action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" ''' +
|
||||||
|
'errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'),
|
||||||
(True, 'splunk', 'https://yoursplunk/services/collector/event', 8088, None,
|
(True, 'splunk', 'https://yoursplunk/services/collector/event', 8088, None,
|
||||||
'''$IncludeConfig /etc/rsyslog.conf
|
'''$IncludeConfig /etc/rsyslog.conf
|
||||||
input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
||||||
template(name="awx" type="string" string="%msg%")
|
template(name="awx" type="string" string="%msg%")
|
||||||
module(load="omhttp")
|
module(load="omhttp")
|
||||||
action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'''),
|
action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" ''' +
|
||||||
|
'errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'),
|
||||||
(True, 'splunk', 'https://yoursplunk/services/collector/event', 8088, 'https',
|
(True, 'splunk', 'https://yoursplunk/services/collector/event', 8088, 'https',
|
||||||
'''$IncludeConfig /etc/rsyslog.conf
|
'''$IncludeConfig /etc/rsyslog.conf
|
||||||
input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")
|
||||||
template(name="awx" type="string" string="%msg%")
|
template(name="awx" type="string" string="%msg%")
|
||||||
module(load="omhttp")
|
module(load="omhttp")
|
||||||
action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'''),
|
action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" ''' +
|
||||||
|
'errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
def test_rsyslog_conf_template(enabled, type, host, port, protocol, expected_config):
|
def test_rsyslog_conf_template(enabled, type, host, port, protocol, expected_config):
|
||||||
|
|
||||||
# Mock settings object
|
mock_settings, _ = _mock_logging_defaults()
|
||||||
class MockSettings:
|
|
||||||
pass
|
|
||||||
mock_settings = MockSettings()
|
|
||||||
|
|
||||||
# Pre-populate settings obj with defaults
|
|
||||||
for key in settings_registry.get_registered_settings(category_slug='logging'):
|
|
||||||
value = settings_registry.get_setting_field(key).get_default()
|
|
||||||
setattr(mock_settings, key, value)
|
|
||||||
|
|
||||||
|
|
||||||
# Set test settings
|
# Set test settings
|
||||||
setattr(mock_settings, 'LOGGING_SOCK', '/var/run/rsyslog/rsyslog.sock')
|
logging_defaults = getattr(settings, 'LOGGING')
|
||||||
|
setattr(mock_settings, 'LOGGING', logging_defaults)
|
||||||
|
setattr(mock_settings, 'LOGGING["handlers"]["external_logger"]["address"]', '/var/run/rsyslog/rsyslog.sock')
|
||||||
setattr(mock_settings, 'LOG_AGGREGATOR_ENABLED', enabled)
|
setattr(mock_settings, 'LOG_AGGREGATOR_ENABLED', enabled)
|
||||||
setattr(mock_settings, 'LOG_AGGREGATOR_TYPE', type)
|
setattr(mock_settings, 'LOG_AGGREGATOR_TYPE', type)
|
||||||
setattr(mock_settings, 'LOG_AGGREGATOR_HOST', host)
|
setattr(mock_settings, 'LOG_AGGREGATOR_HOST', host)
|
||||||
@@ -91,4 +97,3 @@ def test_rsyslog_conf_template(enabled, type, host, port, protocol, expected_con
|
|||||||
|
|
||||||
# check validity of created template
|
# check validity of created template
|
||||||
assert tmpl in expected_config
|
assert tmpl in expected_config
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ def test_produce_supervisor_command(mocker):
|
|||||||
mock_process.communicate = communicate_mock
|
mock_process.communicate = communicate_mock
|
||||||
Popen_mock = mocker.MagicMock(return_value=mock_process)
|
Popen_mock = mocker.MagicMock(return_value=mock_process)
|
||||||
with mocker.patch.object(reload.subprocess, 'Popen', Popen_mock):
|
with mocker.patch.object(reload.subprocess, 'Popen', Popen_mock):
|
||||||
reload._supervisor_service_command("restart")
|
reload.supervisor_service_command("restart")
|
||||||
reload.subprocess.Popen.assert_called_once_with(
|
reload.subprocess.Popen.assert_called_once_with(
|
||||||
['supervisorctl', 'restart', 'tower-processes:*',],
|
['supervisorctl', 'restart', 'tower-processes:*',],
|
||||||
stderr=-1, stdin=-1, stdout=-1)
|
stderr=-1, stdin=-1, stdout=-1)
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ def construct_rsyslog_conf_template(settings=settings):
|
|||||||
host = getattr(settings, 'LOG_AGGREGATOR_HOST', '')
|
host = getattr(settings, 'LOG_AGGREGATOR_HOST', '')
|
||||||
port = getattr(settings, 'LOG_AGGREGATOR_PORT', '')
|
port = getattr(settings, 'LOG_AGGREGATOR_PORT', '')
|
||||||
protocol = getattr(settings, 'LOG_AGGREGATOR_PROTOCOL', '')
|
protocol = getattr(settings, 'LOG_AGGREGATOR_PROTOCOL', '')
|
||||||
import pdb; pdb.set_trace()
|
|
||||||
if protocol.startswith('http'):
|
if protocol.startswith('http'):
|
||||||
scheme = 'https'
|
scheme = 'https'
|
||||||
# urlparse requires '//' to be provided if scheme is not specified
|
# urlparse requires '//' to be provided if scheme is not specified
|
||||||
@@ -29,7 +28,7 @@ def construct_rsyslog_conf_template(settings=settings):
|
|||||||
port = settings.LOG_AGGREGATOR_PORT
|
port = settings.LOG_AGGREGATOR_PORT
|
||||||
|
|
||||||
parts.extend([
|
parts.extend([
|
||||||
'input(type="imuxsock" Socket="' + settings.LOGGING['handlers']['external_logger'] + '" unlink="on")',
|
'input(type="imuxsock" Socket="' + settings.LOGGING['handlers']['external_logger']['address'] + '" unlink="on")',
|
||||||
'template(name="awx" type="string" string="%msg%")',
|
'template(name="awx" type="string" string="%msg%")',
|
||||||
])
|
])
|
||||||
if protocol.startswith('http'):
|
if protocol.startswith('http'):
|
||||||
@@ -66,7 +65,8 @@ def construct_rsyslog_conf_template(settings=settings):
|
|||||||
)
|
)
|
||||||
tmpl = '\n'.join(parts)
|
tmpl = '\n'.join(parts)
|
||||||
return tmpl
|
return tmpl
|
||||||
|
|
||||||
|
|
||||||
def reconfigure_rsyslog():
|
def reconfigure_rsyslog():
|
||||||
tmpl = construct_rsyslog_conf_template()
|
tmpl = construct_rsyslog_conf_template()
|
||||||
with open('/var/lib/awx/rsyslog/rsyslog.conf', 'w') as f:
|
with open('/var/lib/awx/rsyslog/rsyslog.conf', 'w') as f:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from django.conf import settings
|
|||||||
class RSysLogHandler(logging.handlers.SysLogHandler):
|
class RSysLogHandler(logging.handlers.SysLogHandler):
|
||||||
|
|
||||||
def emit(self, msg):
|
def emit(self, msg):
|
||||||
if not os.path.exists(settings.LOGGING['handlers']['external_logger']):
|
if not os.path.exists(settings.LOGGING['handlers']['external_logger']['address']):
|
||||||
return
|
return
|
||||||
return super(RSysLogHandler, self).emit(msg)
|
return super(RSysLogHandler, self).emit(msg)
|
||||||
|
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ export default [
|
|||||||
$scope.$parent.vm.testTooltip = i18n._('Save and enable log aggregation before testing the log aggregator.');
|
$scope.$parent.vm.testTooltip = i18n._('Save and enable log aggregation before testing the log aggregator.');
|
||||||
} else {
|
} else {
|
||||||
$scope.$parent.vm.disableTestButton = false;
|
$scope.$parent.vm.disableTestButton = false;
|
||||||
$scope.$parent.vm.testTooltip = i18n._('Send a test response to the configured log aggregator.');
|
$scope.$parent.vm.testTooltip = i18n._('Send a test log message to the configured log aggregator.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ export default [
|
|||||||
})
|
})
|
||||||
.catch(({ data, status }) => {
|
.catch(({ data, status }) => {
|
||||||
$scope.$parent.vm.disableTestButton = false;
|
$scope.$parent.vm.disableTestButton = false;
|
||||||
if (status === 400 || status == 500) {
|
if (status === 400 || status === 500) {
|
||||||
ngToast.danger({
|
ngToast.danger({
|
||||||
dismissButton: false,
|
dismissButton: false,
|
||||||
dismissOnTimeout: true,
|
dismissOnTimeout: true,
|
||||||
|
|||||||
@@ -213,7 +213,6 @@ with the traceback message.
|
|||||||
|
|
||||||
Log messages should be sent outside of the
|
Log messages should be sent outside of the
|
||||||
request-response cycle. For example, Loggly examples use
|
request-response cycle. For example, Loggly examples use
|
||||||
`requests_futures.sessions.FuturesSession`, which does some
|
rsyslog, which handles these messages without interfering with other
|
||||||
threading work to fire the message without interfering with other
|
|
||||||
operations. A timeout on the part of the log aggregation service should
|
operations. A timeout on the part of the log aggregation service should
|
||||||
not cause Tower operations to hang.
|
not cause Tower operations to hang.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ The `requirements.txt` and `requirements_ansible.txt` files are generated from `
|
|||||||
|
|
||||||
## How To Use
|
## How To Use
|
||||||
|
|
||||||
Commands should from inside `./requirements` directory of the awx repository.
|
Commands should be run from inside the `./requirements` directory of the awx repository.
|
||||||
|
|
||||||
Make sure you have `patch, awk, python3, python2, python3-venv, python2-virtualenv, pip2, pip3` installed. The development container image should have all these.
|
Make sure you have `patch, awk, python3, python2, python3-venv, python2-virtualenv, pip2, pip3` installed. The development container image should have all these.
|
||||||
|
|
||||||
|
|||||||
@@ -101,9 +101,8 @@ python3-saml==1.9.0 # via -r /awx_devel/requirements/requirements.in
|
|||||||
pytz==2019.3 # via django, irc, tempora, twilio
|
pytz==2019.3 # via django, irc, tempora, twilio
|
||||||
pyyaml==5.3.1 # via -r /awx_devel/requirements/requirements.in, ansible-runner, djangorestframework-yaml, kubernetes
|
pyyaml==5.3.1 # via -r /awx_devel/requirements/requirements.in, ansible-runner, djangorestframework-yaml, kubernetes
|
||||||
redis==3.4.1 # via -r /awx_devel/requirements/requirements.in
|
redis==3.4.1 # via -r /awx_devel/requirements/requirements.in
|
||||||
requests-futures==1.0.0 # via -r /awx_devel/requirements/requirements.in
|
|
||||||
requests-oauthlib==1.3.0 # via kubernetes, msrest, social-auth-core
|
requests-oauthlib==1.3.0 # via kubernetes, msrest, social-auth-core
|
||||||
requests==2.23.0 # via -r /awx_devel/requirements/requirements.in, adal, azure-keyvault, django-oauth-toolkit, kubernetes, msrest, requests-futures, requests-oauthlib, slackclient, social-auth-core, twilio
|
requests==2.23.0 # via -r /awx_devel/requirements/requirements.in, adal, azure-keyvault, django-oauth-toolkit, kubernetes, msrest, requests-oauthlib, slackclient, social-auth-core, twilio
|
||||||
rsa==4.0 # via google-auth
|
rsa==4.0 # via google-auth
|
||||||
ruamel.yaml.clib==0.2.0 # via ruamel.yaml
|
ruamel.yaml.clib==0.2.0 # via ruamel.yaml
|
||||||
ruamel.yaml==0.16.10 # via openshift
|
ruamel.yaml==0.16.10 # via openshift
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ stdout_logfile=/dev/fd/1
|
|||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
|
|
||||||
[group:tower-processes]
|
[group:tower-processes]
|
||||||
programs=awx-dispatcher,awx-receiver,awx-runworker,awx-uwsgi,awx-daphne,awx-nginx,awx-wsbroadcast,awx-rsyslogd
|
programs=awx-dispatcher,awx-receiver,awx-uwsgi,awx-daphne,awx-nginx,awx-wsbroadcast,awx-rsyslogd
|
||||||
priority=5
|
priority=5
|
||||||
|
|
||||||
[unix_http_server]
|
[unix_http_server]
|
||||||
|
|||||||
Reference in New Issue
Block a user