1
0
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:
John Anderson
2020-07-06 01:58:28 -04:00
parent f092c107b5
commit 41f92ef8e6
11 changed files with 130 additions and 75 deletions

View File

@ -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
#

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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,
})

View File

@ -3,10 +3,8 @@ var timeout = 1000;
function updatePendingStatusLabel(status){
var labelClass;
if (status.value === 'failed'){
if (status.value === 'failed' || status.value === 'errored'){
labelClass = 'danger';
} else if (status.value === 'pending'){
labelClass = 'default';
} else if (status.value === 'running'){
labelClass = 'warning';
} else if (status.value === 'completed'){
@ -33,7 +31,7 @@ $(document).ready(function(){
context: this,
success: function(data) {
updatePendingStatusLabel(data.status);
if (data.status.value === 'completed' || data.status.value === 'failed'){
if (data.status.value === 'completed' || data.status.value === 'failed' || data.status.value === 'errored'){
jobTerminatedAction()
} else {
setTimeout(checkPendingResult, timeout);

View File

@ -1,11 +1,13 @@
{% if result.status == 'failed' %}
<label class="label label-danger">Failed</label>
{% elif result.status == 'errored' %}
<label class="label label-danger">Errored</label>
{% elif result.status == 'pending' %}
<label class="label label-default">Pending</label>
{% elif result.status == 'running' %}
<label class="label label-warning">Running</label>
{% elif result.status == 'completed' %}
<label class="label label-success">Passed</label>
<label class="label label-success">Completed</label>
{% else %}
<label class="label label-default">N/A</label>
{% endif %}

View File

@ -38,7 +38,7 @@
{% endif %}
<span id="pending-result-label">{% include 'extras/inc/job_label.html' with result=result %}</span>
</p>
{% if result.completed %}
{% if result.completed and result.status != 'errored' %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Report Methods</strong>
@ -97,8 +97,10 @@
</tbody>
</table>
</div>
{% elif result.status == 'errored' %}
<div class="well">Error during report execution</div>
{% else %}
<div class="well">Pending results</div>
<div class="well">Pending results</div>
{% endif %}
</div>
</div>

View File

@ -4,15 +4,17 @@
{% block content %}
<h1>{% block title %}Scripts{% endblock %}</h1>
<div class="row">
<div class="col-md-12">
<div class="col-md-9">
{% if scripts %}
{% for module, module_scripts in scripts.items %}
<h3><a name="module.{{ module }}"></a>{{ module|bettertitle }}</h3>
<table class="table table-hover table-headings reports">
<thead>
<tr>
<th class="col-md-3">Name</th>
<th class="col-md-9">Description</th>
<th>Name</th>
<th>Status</th>
<th>Description</th>
<th class="text-right">Last Run</th>
</tr>
</thead>
<tbody>
@ -21,7 +23,15 @@
<td>
<a href="{% url 'extras:script' module=script.module name=class_name %}" name="script.{{ class_name }}"><strong>{{ script }}</strong></a>
</td>
<td>
{% include 'extras/inc/job_label.html' with result=script.result %}
</td>
<td>{{ script.Meta.description }}</td>
{% if script.result %}
<td class="text-right">{{ script.result.created }}</td>
{% else %}
<td class="text-right text-muted">Never</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
@ -34,5 +44,26 @@
</div>
{% endif %}
</div>
<div class="col-md-3">
{% if scripts %}
<div class="panel panel-default">
{% for module, module_scripts in scripts.items %}
<div class="panel-heading">
<strong>{{ module|bettertitle }}</strong>
</div>
<ul class="list-group">
{% for class_name, script in module_scripts.items %}
<a href="#script.{{ class_name }}" class="list-group-item">
<i class="fa fa-list-alt"></i> {{ script.name }}
<div class="pull-right">
{% include 'extras/inc/job_label.html' with result=script.result %}
</div>
</a>
{% endfor %}
</ul>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -41,7 +41,7 @@
<span id="pending-result-label">{% include 'extras/inc/job_label.html' with result=result %}</span>
</p>
<div role="tabpanel" class="tab-pane active" id="log">
{% if result.completed %}
{% if result.completed and result.status != 'errored' %}
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
@ -76,6 +76,12 @@
</div>
</div>
</div>
{% elif result.stats == 'errored' %}
<div class="row">
<div class="col-md-12">
<div class="well">Error during script execution</div>
</div>
</div>
{% else %}
<div class="row">
<div class="col-md-12">