mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
review updates
This commit is contained in:
@ -120,6 +120,43 @@ class TemplateLanguageChoices(ChoiceSet):
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Log Levels for Reports and Scripts
|
||||
#
|
||||
|
||||
class LogLevelChoices(ChoiceSet):
|
||||
|
||||
LOG_DEFAULT = 'default'
|
||||
LOG_SUCCESS = 'sucess'
|
||||
LOG_INFO = 'info'
|
||||
LOG_WARNING = 'warning'
|
||||
LOG_FAILURE = 'failure'
|
||||
|
||||
CHOICES = (
|
||||
(LOG_DEFAULT, 'Default'),
|
||||
(LOG_SUCCESS, 'Success'),
|
||||
(LOG_INFO, 'Info'),
|
||||
(LOG_WARNING, 'Warning'),
|
||||
(LOG_FAILURE, 'Failure'),
|
||||
)
|
||||
|
||||
CLASS_MAP = (
|
||||
(LOG_DEFAULT, 'default'),
|
||||
(LOG_SUCCESS, 'success'),
|
||||
(LOG_INFO, 'info'),
|
||||
(LOG_WARNING, 'warning'),
|
||||
(LOG_FAILURE, 'danger'),
|
||||
)
|
||||
|
||||
LEGACY_MAP = (
|
||||
(LOG_DEFAULT, 0),
|
||||
(LOG_SUCCESS, 10),
|
||||
(LOG_INFO, 20),
|
||||
(LOG_WARNING, 30),
|
||||
(LOG_FAILURE, 40),
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Job results
|
||||
#
|
||||
|
@ -1,17 +1,3 @@
|
||||
# Report logging levels
|
||||
LOG_DEFAULT = 0
|
||||
LOG_SUCCESS = 10
|
||||
LOG_INFO = 20
|
||||
LOG_WARNING = 30
|
||||
LOG_FAILURE = 40
|
||||
LOG_LEVEL_CODES = {
|
||||
LOG_DEFAULT: 'default',
|
||||
LOG_SUCCESS: 'success',
|
||||
LOG_INFO: 'info',
|
||||
LOG_WARNING: 'warning',
|
||||
LOG_FAILURE: 'failure',
|
||||
}
|
||||
|
||||
# Webhook content types
|
||||
HTTP_CONTENT_TYPE_JSON = 'application/json'
|
||||
|
||||
|
@ -9,8 +9,7 @@ from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from django_rq import job
|
||||
|
||||
from .choices import JobResultStatusChoices
|
||||
from .constants import *
|
||||
from .choices import JobResultStatusChoices, LogLevelChoices
|
||||
from .models import JobResult
|
||||
|
||||
|
||||
@ -77,7 +76,8 @@ def run_report(job_result, *args, **kwargs):
|
||||
|
||||
try:
|
||||
report.run(job_result)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
print(e)
|
||||
job_result.set_status(JobResultStatusChoices.STATUS_ERRORED)
|
||||
logging.error(f"Error during execution of report {job_result.name}")
|
||||
|
||||
@ -153,15 +153,15 @@ class Report(object):
|
||||
def full_name(self):
|
||||
return '.'.join([self.__module__, self.__class__.__name__])
|
||||
|
||||
def _log(self, obj, message, level=LOG_DEFAULT):
|
||||
def _log(self, obj, message, level=LogLevelChoices.LOG_DEFAULT):
|
||||
"""
|
||||
Log a message from a test method. Do not call this method directly; use one of the log_* wrappers below.
|
||||
"""
|
||||
if level not in LOG_LEVEL_CODES:
|
||||
if level not in LogLevelChoices.as_dict():
|
||||
raise Exception("Unknown logging level: {}".format(level))
|
||||
self._results[self.active_test]['log'].append((
|
||||
timezone.now().isoformat(),
|
||||
LOG_LEVEL_CODES.get(level),
|
||||
level,
|
||||
str(obj) if obj else None,
|
||||
obj.get_absolute_url() if getattr(obj, 'get_absolute_url', None) else None,
|
||||
message,
|
||||
@ -171,7 +171,7 @@ class Report(object):
|
||||
"""
|
||||
Log a message which is not associated with a particular object.
|
||||
"""
|
||||
self._log(None, message, level=LOG_DEFAULT)
|
||||
self._log(None, message, level=LogLevelChoices.LOG_DEFAULT)
|
||||
self.logger.info(message)
|
||||
|
||||
def log_success(self, obj, message=None):
|
||||
@ -179,7 +179,7 @@ class Report(object):
|
||||
Record a successful test against an object. Logging a message is optional.
|
||||
"""
|
||||
if message:
|
||||
self._log(obj, message, level=LOG_SUCCESS)
|
||||
self._log(obj, message, level=LogLevelChoices.LOG_SUCCESS)
|
||||
self._results[self.active_test]['success'] += 1
|
||||
self.logger.info(f"Success | {obj}: {message}")
|
||||
|
||||
@ -187,7 +187,7 @@ class Report(object):
|
||||
"""
|
||||
Log an informational message.
|
||||
"""
|
||||
self._log(obj, message, level=LOG_INFO)
|
||||
self._log(obj, message, level=LogLevelChoices.LOG_INFO)
|
||||
self._results[self.active_test]['info'] += 1
|
||||
self.logger.info(f"Info | {obj}: {message}")
|
||||
|
||||
@ -195,7 +195,7 @@ class Report(object):
|
||||
"""
|
||||
Log a warning.
|
||||
"""
|
||||
self._log(obj, message, level=LOG_WARNING)
|
||||
self._log(obj, message, level=LogLevelChoices.LOG_WARNING)
|
||||
self._results[self.active_test]['warning'] += 1
|
||||
self.logger.info(f"Warning | {obj}: {message}")
|
||||
|
||||
@ -203,7 +203,7 @@ class Report(object):
|
||||
"""
|
||||
Log a failure. Calling this method will automatically mark the report as failed.
|
||||
"""
|
||||
self._log(obj, message, level=LOG_FAILURE)
|
||||
self._log(obj, message, level=LogLevelChoices.LOG_FAILURE)
|
||||
self._results[self.active_test]['failure'] += 1
|
||||
self.logger.info(f"Failure | {obj}: {message}")
|
||||
self.failed = True
|
||||
|
@ -19,11 +19,10 @@ from mptt.forms import TreeNodeChoiceField, TreeNodeMultipleChoiceField
|
||||
from mptt.models import MPTTModel
|
||||
|
||||
from extras.api.serializers import ScriptOutputSerializer
|
||||
from extras.choices import JobResultStatusChoices
|
||||
from extras.choices import JobResultStatusChoices, LogLevelChoices
|
||||
from extras.models import JobResult
|
||||
from ipam.formfields import IPAddressFormField, IPNetworkFormField
|
||||
from ipam.validators import MaxPrefixLengthValidator, MinPrefixLengthValidator, prefix_validator
|
||||
from .constants import LOG_DEFAULT, LOG_FAILURE, LOG_INFO, LOG_SUCCESS, LOG_WARNING
|
||||
from utilities.exceptions import AbortTransaction
|
||||
from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField
|
||||
from .forms import ScriptForm
|
||||
@ -324,23 +323,23 @@ class BaseScript:
|
||||
|
||||
def log_debug(self, message):
|
||||
self.logger.log(logging.DEBUG, message)
|
||||
self.log.append((LOG_DEFAULT, message))
|
||||
self.log.append((LogLevelChoices.LOG_DEFAULT, message))
|
||||
|
||||
def log_success(self, message):
|
||||
self.logger.log(logging.INFO, message) # No syslog equivalent for SUCCESS
|
||||
self.log.append((LOG_SUCCESS, message))
|
||||
self.log.append((LogLevelChoices.LOG_SUCCESS, message))
|
||||
|
||||
def log_info(self, message):
|
||||
self.logger.log(logging.INFO, message)
|
||||
self.log.append((LOG_INFO, message))
|
||||
self.log.append((LogLevelChoices.LOG_INFO, message))
|
||||
|
||||
def log_warning(self, message):
|
||||
self.logger.log(logging.WARNING, message)
|
||||
self.log.append((LOG_WARNING, message))
|
||||
self.log.append((LogLevelChoices.LOG_WARNING, message))
|
||||
|
||||
def log_failure(self, message):
|
||||
self.logger.log(logging.ERROR, message)
|
||||
self.log.append((LOG_FAILURE, message))
|
||||
self.log.append((LogLevelChoices.LOG_FAILURE, message))
|
||||
|
||||
# Convenience functions
|
||||
|
||||
@ -428,11 +427,15 @@ def run_script(data, request, commit=True, *args, **kwargs):
|
||||
try:
|
||||
with transaction.atomic():
|
||||
script.output = script.run(**kwargs)
|
||||
job_result.status = JobResultStatusChoices.STATUS_COMPLETED
|
||||
job_result.data = ScriptOutputSerializer(script).data
|
||||
job_result.set_status(JobResultStatusChoices.STATUS_COMPLETED)
|
||||
|
||||
if not commit:
|
||||
raise AbortTransaction()
|
||||
|
||||
except AbortTransaction:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
stacktrace = traceback.format_exc()
|
||||
script.log_failure(
|
||||
@ -440,7 +443,8 @@ def run_script(data, request, commit=True, *args, **kwargs):
|
||||
)
|
||||
logger.error(f"Exception raised during script execution: {e}")
|
||||
commit = False
|
||||
job_result.status = JobResultStatusChoices.STATUS_FAILED
|
||||
job_result.set_status(JobResultStatusChoices.STATUS_ERRORED)
|
||||
|
||||
finally:
|
||||
if not commit:
|
||||
# Delete all pending changelog entries
|
||||
@ -449,10 +453,6 @@ def run_script(data, request, commit=True, *args, **kwargs):
|
||||
"Database changes have been reverted automatically."
|
||||
)
|
||||
|
||||
job_result.data = ScriptOutputSerializer(script).data
|
||||
job_result.completed = timezone.now()
|
||||
job_result.save()
|
||||
|
||||
logger.info(f"Script completed in {job_result.duration}")
|
||||
|
||||
# Delete any previous terminal state results
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django import template
|
||||
|
||||
from extras.constants import LOG_DEFAULT, LOG_FAILURE, LOG_INFO, LOG_SUCCESS, LOG_WARNING
|
||||
from extras.choices import LogLevelChoices
|
||||
|
||||
|
||||
register = template.Library()
|
||||
@ -11,28 +11,7 @@ def log_level(level):
|
||||
"""
|
||||
Display a label indicating a syslog severity (e.g. info, warning, etc.).
|
||||
"""
|
||||
# TODO: we should convert this to a choices class
|
||||
levels = {
|
||||
'default': {
|
||||
'name': 'Default',
|
||||
'class': 'default'
|
||||
},
|
||||
'success': {
|
||||
'name': 'Success',
|
||||
'class': 'success',
|
||||
},
|
||||
'info': {
|
||||
'name': 'Info',
|
||||
'class': 'info'
|
||||
},
|
||||
'warning': {
|
||||
'name': 'Warning',
|
||||
'class': 'warning'
|
||||
},
|
||||
'failure': {
|
||||
'name': 'Failure',
|
||||
'class': 'danger'
|
||||
}
|
||||
return {
|
||||
'name': LogLevelChoices.as_dict()[level],
|
||||
'class': dict(LogLevelChoices.CLASS_MAP)[level]
|
||||
}
|
||||
|
||||
return levels[level]
|
||||
|
@ -453,8 +453,22 @@ class ScriptListView(ContentTypePermissionRequiredMixin, View):
|
||||
|
||||
def get(self, request):
|
||||
|
||||
scripts = get_scripts(use_names=True)
|
||||
script_content_type = ContentType.objects.get(app_label='extras', model='script')
|
||||
results = {
|
||||
r.name: r
|
||||
for r in JobResult.objects.filter(
|
||||
obj_type=script_content_type,
|
||||
status__in=JobResultStatusChoices.TERMINAL_STATE_CHOICES
|
||||
).defer('data')
|
||||
}
|
||||
|
||||
for _scripts in scripts.values():
|
||||
for script in _scripts.values():
|
||||
script.result = results.get(script.full_name)
|
||||
|
||||
return render(request, 'extras/script_list.html', {
|
||||
'scripts': get_scripts(use_names=True),
|
||||
'scripts': scripts,
|
||||
})
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user