mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-18 21:48:16 -06:00
14438 script job mapping
This commit is contained in:
parent
fc890e36af
commit
6930e441ae
@ -8,14 +8,19 @@ def update_scripts(apps, schema_editor):
|
|||||||
from extras.models import ScriptModule
|
from extras.models import ScriptModule
|
||||||
ScriptModuleNew = apps.get_model('extras', 'ScriptModule')
|
ScriptModuleNew = apps.get_model('extras', 'ScriptModule')
|
||||||
Script = apps.get_model('extras', 'Script')
|
Script = apps.get_model('extras', 'Script')
|
||||||
|
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||||
|
ct = ContentType.objects.get(app_label='extras', model='script')
|
||||||
|
|
||||||
for module in ScriptModule.objects.all():
|
for module in ScriptModule.objects.all():
|
||||||
for script in module.get_module_scripts.keys():
|
for script in module.get_module_scripts.keys():
|
||||||
Script.objects.create(
|
obj = Script.objects.create(
|
||||||
name=script,
|
name=script,
|
||||||
module=ScriptModuleNew.objects.get(file_root=module.file_root, file_path=module.file_path),
|
module=ScriptModuleNew.objects.get(file_root=module.file_root, file_path=module.file_path),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# update all jobs associated with this module/name to point to the new script obj
|
||||||
|
module.jobs.filter(name=name).update(object_type=ct, object_id=obj.id)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
@ -35,6 +40,10 @@ class Migration(migrations.Migration):
|
|||||||
'ordering': ('name', 'pk'),
|
'ordering': ('name', 'pk'),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='script',
|
||||||
|
constraint=models.UniqueConstraint(fields=('name', 'module'), name='extras_script_unique_name_module'),
|
||||||
|
),
|
||||||
migrations.RunPython(
|
migrations.RunPython(
|
||||||
code=update_scripts,
|
code=update_scripts,
|
||||||
reverse_code=migrations.RunPython.noop
|
reverse_code=migrations.RunPython.noop
|
||||||
|
@ -21,7 +21,7 @@ __all__ = (
|
|||||||
logger = logging.getLogger('netbox.data_backends')
|
logger = logging.getLogger('netbox.data_backends')
|
||||||
|
|
||||||
|
|
||||||
class Script(EventRulesMixin, models.Model):
|
class Script(EventRulesMixin, JobsMixin, models.Model):
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
verbose_name=_('name'),
|
verbose_name=_('name'),
|
||||||
max_length=79,
|
max_length=79,
|
||||||
@ -37,11 +37,24 @@ class Script(EventRulesMixin, models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('name', 'pk')
|
ordering = ('name', 'pk')
|
||||||
|
constraints = (
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=('name', 'module'),
|
||||||
|
name='%(app_label)s_%(class)s_unique_name_module'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
verbose_name = _('script')
|
||||||
|
verbose_name_plural = _('scripts')
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def python_class(self):
|
def python_class(self):
|
||||||
return self.module.get_module_scripts.get(self.name)
|
return self.module.get_module_scripts.get(self.name)
|
||||||
|
|
||||||
|
def get_jobs(self):
|
||||||
|
return self.module.jobs.filter(
|
||||||
|
name=self.name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ScriptModuleManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
class ScriptModuleManager(models.Manager.from_queryset(RestrictedQuerySet)):
|
||||||
|
|
||||||
|
@ -130,9 +130,9 @@ urlpatterns = [
|
|||||||
path('scripts/add/', views.ScriptModuleCreateView.as_view(), name='scriptmodule_add'),
|
path('scripts/add/', views.ScriptModuleCreateView.as_view(), name='scriptmodule_add'),
|
||||||
path('scripts/results/<int:job_pk>/', views.ScriptResultView.as_view(), name='script_result'),
|
path('scripts/results/<int:job_pk>/', views.ScriptResultView.as_view(), name='script_result'),
|
||||||
path('scripts/<int:pk>/', include(get_model_urls('extras', 'scriptmodule'))),
|
path('scripts/<int:pk>/', include(get_model_urls('extras', 'scriptmodule'))),
|
||||||
path('scripts/<str:module>/<str:name>/', views.ScriptView.as_view(), name='script'),
|
path('scripts/class/<int:pk>/', views.ScriptView.as_view(), name='script'),
|
||||||
path('scripts/<str:module>/<str:name>/source/', views.ScriptSourceView.as_view(), name='script_source'),
|
path('scripts/class/<int:pk>/source/', views.ScriptSourceView.as_view(), name='script_source'),
|
||||||
path('scripts/<str:module>/<str:name>/jobs/', views.ScriptJobsView.as_view(), name='script_jobs'),
|
path('scripts/class/<int:pk>/jobs/', views.ScriptJobsView.as_view(), name='script_jobs'),
|
||||||
|
|
||||||
# Markdown
|
# Markdown
|
||||||
path('render/markdown/', views.RenderMarkdownView.as_view(), name="render_markdown"),
|
path('render/markdown/', views.RenderMarkdownView.as_view(), name="render_markdown"),
|
||||||
|
@ -1226,27 +1226,28 @@ class ScriptView(ContentTypePermissionRequiredMixin, View):
|
|||||||
def get_required_permission(self):
|
def get_required_permission(self):
|
||||||
return 'extras.view_script'
|
return 'extras.view_script'
|
||||||
|
|
||||||
def get(self, request, module, name):
|
def get(self, request, pk):
|
||||||
module = get_script_module(module, request)
|
script = Script.objects.get(pk=pk)
|
||||||
script = module.scripts[name]()
|
script_class = script.python_class()
|
||||||
jobs = module.get_jobs(script.class_name)
|
jobs = script.get_jobs()
|
||||||
form = script.as_form(initial=normalize_querydict(request.GET))
|
form = script_class.as_form(initial=normalize_querydict(request.GET))
|
||||||
|
|
||||||
return render(request, 'extras/script.html', {
|
return render(request, 'extras/script.html', {
|
||||||
'job_count': jobs.count(),
|
'job_count': jobs.count(),
|
||||||
'module': module,
|
'module': script.module,
|
||||||
'script': script,
|
'script': script,
|
||||||
|
'script_class': script_class,
|
||||||
'form': form,
|
'form': form,
|
||||||
})
|
})
|
||||||
|
|
||||||
def post(self, request, module, name):
|
def post(self, request, pk):
|
||||||
if not request.user.has_perm('extras.run_script'):
|
if not request.user.has_perm('extras.run_script'):
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
module = get_script_module(module, request)
|
script = Script.objects.get(pk=pk)
|
||||||
script = module.scripts[name]()
|
script_class = script.python_class()
|
||||||
jobs = module.get_jobs(script.class_name)
|
jobs = script.get_jobs()
|
||||||
form = script.as_form(request.POST, request.FILES)
|
form = script_class.as_form(request.POST, request.FILES)
|
||||||
|
|
||||||
# Allow execution only if RQ worker process is running
|
# Allow execution only if RQ worker process is running
|
||||||
if not get_workers_for_queue('default'):
|
if not get_workers_for_queue('default'):
|
||||||
@ -1255,8 +1256,8 @@ class ScriptView(ContentTypePermissionRequiredMixin, View):
|
|||||||
elif form.is_valid():
|
elif form.is_valid():
|
||||||
job = Job.enqueue(
|
job = Job.enqueue(
|
||||||
run_script,
|
run_script,
|
||||||
instance=module,
|
instance=script.module,
|
||||||
name=script.class_name,
|
name=script_class.class_name,
|
||||||
user=request.user,
|
user=request.user,
|
||||||
schedule_at=form.cleaned_data.pop('_schedule_at'),
|
schedule_at=form.cleaned_data.pop('_schedule_at'),
|
||||||
interval=form.cleaned_data.pop('_interval'),
|
interval=form.cleaned_data.pop('_interval'),
|
||||||
@ -1270,8 +1271,9 @@ class ScriptView(ContentTypePermissionRequiredMixin, View):
|
|||||||
|
|
||||||
return render(request, 'extras/script.html', {
|
return render(request, 'extras/script.html', {
|
||||||
'job_count': jobs.count(),
|
'job_count': jobs.count(),
|
||||||
'module': module,
|
'module': script.module,
|
||||||
'script': script,
|
'script': script,
|
||||||
|
'script_class': script_class,
|
||||||
'form': form,
|
'form': form,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1281,15 +1283,16 @@ class ScriptSourceView(ContentTypePermissionRequiredMixin, View):
|
|||||||
def get_required_permission(self):
|
def get_required_permission(self):
|
||||||
return 'extras.view_script'
|
return 'extras.view_script'
|
||||||
|
|
||||||
def get(self, request, module, name):
|
def get(self, request, pk):
|
||||||
module = get_script_module(module, request)
|
script = Script.objects.get(pk=pk)
|
||||||
script = module.scripts[name]()
|
script_class = script.python_class()
|
||||||
jobs = module.get_jobs(script.class_name)
|
jobs = script.get_jobs()
|
||||||
|
|
||||||
return render(request, 'extras/script/source.html', {
|
return render(request, 'extras/script/source.html', {
|
||||||
'job_count': jobs.count(),
|
'job_count': jobs.count(),
|
||||||
'module': module,
|
'module': script.module,
|
||||||
'script': script,
|
'script': script,
|
||||||
|
'script_class': script_class,
|
||||||
'tab': 'source',
|
'tab': 'source',
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1299,10 +1302,10 @@ class ScriptJobsView(ContentTypePermissionRequiredMixin, View):
|
|||||||
def get_required_permission(self):
|
def get_required_permission(self):
|
||||||
return 'extras.view_script'
|
return 'extras.view_script'
|
||||||
|
|
||||||
def get(self, request, module, name):
|
def get(self, request, pk):
|
||||||
module = get_script_module(module, request)
|
script = Script.objects.get(pk=pk)
|
||||||
script = module.scripts[name]()
|
script_class = script.python_class()
|
||||||
jobs = module.get_jobs(script.class_name)
|
jobs = script.get_jobs()
|
||||||
|
|
||||||
jobs_table = JobTable(
|
jobs_table = JobTable(
|
||||||
data=jobs,
|
data=jobs,
|
||||||
@ -1313,7 +1316,7 @@ class ScriptJobsView(ContentTypePermissionRequiredMixin, View):
|
|||||||
|
|
||||||
return render(request, 'extras/script/jobs.html', {
|
return render(request, 'extras/script/jobs.html', {
|
||||||
'job_count': jobs.count(),
|
'job_count': jobs.count(),
|
||||||
'module': module,
|
'module': script.module,
|
||||||
'script': script,
|
'script': script,
|
||||||
'table': jobs_table,
|
'table': jobs_table,
|
||||||
'tab': 'jobs',
|
'tab': 'jobs',
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="field-group my-4">
|
<div class="field-group my-4">
|
||||||
{# Render grouped fields according to declared fieldsets #}
|
{# Render grouped fields according to declared fieldsets #}
|
||||||
{% for group, fields in script.get_fieldsets %}
|
{% for group, fields in script_class.get_fieldsets %}
|
||||||
{% if fields %}
|
{% if fields %}
|
||||||
<div class="field-group mb-5">
|
<div class="field-group mb-5">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -26,13 +26,13 @@
|
|||||||
{% block tabs %}
|
{% block tabs %}
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link{% if not tab %} active{% endif %}" href="{% url 'extras:script' module=script.module name=script.class_name %}">{% trans "Script" %}</a>
|
<a class="nav-link{% if not tab %} active{% endif %}" href="{% url 'extras:script' script.id %}">{% trans "Script" %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link{% if tab == 'source' %} active{% endif %}" href="{% url 'extras:script_source' module=script.module name=script.class_name %}">{% trans "Source" %}</a>
|
<a class="nav-link{% if tab == 'source' %} active{% endif %}" href="{% url 'extras:script_source' script.id %}">{% trans "Source" %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<a class="nav-link{% if tab == 'jobs' %} active{% endif %}" href="{% url 'extras:script_jobs' module=script.module name=script.class_name %}">
|
<a class="nav-link{% if tab == 'jobs' %} active{% endif %}" href="{% url 'extras:script_jobs' script.id %}">
|
||||||
{% trans "Jobs" %} {% badge job_count %}
|
{% trans "Jobs" %} {% badge job_count %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% extends 'extras/script/base.html' %}
|
{% extends 'extras/script/base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<code class="h6 my-3 d-block">{{ script.filename }}</code>
|
<code class="h6 my-3 d-block">{{ script_class.filename }}</code>
|
||||||
<pre class="block">{{ script.source }}</pre>
|
<pre class="block">{{ script_class.source }}</pre>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
{% for script in module.scripts.all %}
|
{% for script in module.scripts.all %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url 'extras:script' module=module.python_name name=script.name %}" name="script.{{ script.name }}">{{ script.python_class.name }}</a>
|
<a href="{% url 'extras:script' script.id %}" name="script.{{ script.name }}">{{ script.python_class.name }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ script.python_class.Meta.description|markdown|placeholder }}
|
{{ script.python_class.Meta.description|markdown|placeholder }}
|
||||||
|
Loading…
Reference in New Issue
Block a user