diff --git a/nbxsync/api/views/zabbixsync.py b/nbxsync/api/views/zabbixsync.py index 4a0b4f5..2a326aa 100644 --- a/nbxsync/api/views/zabbixsync.py +++ b/nbxsync/api/views/zabbixsync.py @@ -5,16 +5,15 @@ from rest_framework.viewsets import ViewSet from django.apps import apps from django.shortcuts import get_object_or_404 +from drf_spectacular.utils import extend_schema -from nbxsync.api.serializers import ZabbixHostInterfaceSerializer -from nbxsync.models import ZabbixHostInterface from nbxsync.constants import OBJECT_TYPE_MODEL_MAP class ZabbixSyncViewSet(ViewSet): permission_classes = [IsAuthenticated] - serializer_class = ZabbixHostInterfaceSerializer + @extend_schema(exclude=True) def create(self, request, **kwargs): obj_type = (request.data.get('obj_type') or '').strip().lower() obj_id = request.data.get('obj_id') diff --git a/nbxsync/forms/zabbixtagassignment.py b/nbxsync/forms/zabbixtagassignment.py index 43191a1..d4dc78b 100644 --- a/nbxsync/forms/zabbixtagassignment.py +++ b/nbxsync/forms/zabbixtagassignment.py @@ -11,6 +11,7 @@ from nbxsync.constants import ASSIGNMENT_TYPE_TO_FIELD from nbxsync.models import ZabbixTag, ZabbixTagAssignment, ZabbixConfigurationGroup +from nbxsync.utils import get_assigned_zabbixobjects __all__ = ('ZabbixTagAssignmentForm', 'ZabbixTagAssignmentFilterForm', 'ZabbixTagAssignmentBulkEditForm') logger = logging.getLogger(__name__) @@ -71,21 +72,23 @@ def assignable_fields(self): def __init__(self, *args, **kwargs): instance = kwargs.get('instance') initial = kwargs.get('initial', {}).copy() + target = None if instance and instance.assigned_object: + target = instance.assigned_object for model_class, field in ASSIGNMENT_TYPE_TO_FIELD.items(): if isinstance(instance.assigned_object, model_class): - initial[field] = instance.assigned_object + initial[field] = target break elif 'assigned_object_type' in initial and 'assigned_object_id' in initial: try: content_type = ContentType.objects.get(pk=initial['assigned_object_type']) - obj = content_type.get_object_for_this_type(pk=initial['assigned_object_id']) + target = content_type.get_object_for_this_type(pk=initial['assigned_object_id']) for model_class, field in ASSIGNMENT_TYPE_TO_FIELD.items(): - if isinstance(obj, model_class): - initial[field] = obj.pk + if isinstance(target, model_class): + initial[field] = target.pk break except Exception as e: @@ -95,6 +98,19 @@ def __init__(self, *args, **kwargs): kwargs['initial'] = initial super().__init__(*args, **kwargs) + if target is not None: + assigned = get_assigned_zabbixobjects(target) + excluded_ids = set() + for assigned_tag in assigned['tags']: + excluded_ids.add(assigned_tag.zabbixtag_id) + + if instance is not None and instance.pk and instance.zabbixtag_id: + excluded_ids.discard(instance.zabbixtag_id) + + if excluded_ids: + self.fields['zabbixtag'].queryset = ZabbixTag.objects.exclude(pk__in=excluded_ids) + self.fields['zabbixtag'].widget.add_query_params({'id__n': list(excluded_ids)}) + def clean(self): super().clean() diff --git a/nbxsync/forms/zabbixtemplateassignment.py b/nbxsync/forms/zabbixtemplateassignment.py index c302f4f..78f5cd3 100644 --- a/nbxsync/forms/zabbixtemplateassignment.py +++ b/nbxsync/forms/zabbixtemplateassignment.py @@ -11,6 +11,7 @@ from nbxsync.constants import ASSIGNMENT_TYPE_TO_FIELD, ASSIGNMENT_TYPE_TO_FIELD_NBOBJS from nbxsync.models import ZabbixTemplate, ZabbixTemplateAssignment, ZabbixConfigurationGroup +from nbxsync.utils import get_assigned_zabbixobjects __all__ = ('ZabbixTemplateAssignmentForm', 'ZabbixTemplateAssignmentFilterForm') logger = logging.getLogger(__name__) @@ -71,8 +72,10 @@ def assignable_fields(self): def __init__(self, *args, **kwargs): instance = kwargs.get('instance') initial = kwargs.get('initial', {}).copy() + target = None if instance and instance.assigned_object: + target = instance.assigned_object for model_class, field in ASSIGNMENT_TYPE_TO_FIELD.items(): if isinstance(instance.assigned_object, model_class): initial[field] = instance.assigned_object @@ -81,11 +84,11 @@ def __init__(self, *args, **kwargs): elif 'assigned_object_type' in initial and 'assigned_object_id' in initial: try: content_type = ContentType.objects.get(pk=initial['assigned_object_type']) - obj = content_type.get_object_for_this_type(pk=initial['assigned_object_id']) + target = content_type.get_object_for_this_type(pk=initial['assigned_object_id']) for model_class, field in ASSIGNMENT_TYPE_TO_FIELD.items(): - if isinstance(obj, model_class): - initial[field] = obj.pk + if isinstance(target, model_class): + initial[field] = target.pk break except Exception as e: @@ -95,6 +98,19 @@ def __init__(self, *args, **kwargs): kwargs['initial'] = initial super().__init__(*args, **kwargs) + if target is not None: + assigned = get_assigned_zabbixobjects(target) + excluded_ids = set() + for assigned_template in assigned['templates']: + excluded_ids.add(assigned_template.zabbixtemplate_id) + + if instance is not None and instance.pk and instance.zabbixtemplate_id: + excluded_ids.discard(instance.zabbixtemplate_id) + + if excluded_ids: + self.fields['zabbixtemplate'].queryset = ZabbixTemplate.objects.exclude(pk__in=excluded_ids) + self.fields['zabbixtemplate'].widget.add_query_params({'id__n': list(excluded_ids)}) + def clean(self): super().clean() diff --git a/nbxsync/jobs/synctemplates.py b/nbxsync/jobs/synctemplates.py index e64fee1..71d6b93 100644 --- a/nbxsync/jobs/synctemplates.py +++ b/nbxsync/jobs/synctemplates.py @@ -91,7 +91,7 @@ def run(self): self.instance.last_sync_state = True self.instance.last_sync = make_aware(datetime.datetime.now()) - self.instance.last_sync_message = 'Succes' + self.instance.last_sync_message = 'Success' self.instance.save() except ConnectionError as e: diff --git a/nbxsync/models/zabbixmacroassignment.py b/nbxsync/models/zabbixmacroassignment.py index 5e96262..0e10e07 100644 --- a/nbxsync/models/zabbixmacroassignment.py +++ b/nbxsync/models/zabbixmacroassignment.py @@ -1,15 +1,21 @@ +import re + from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db import models -from netbox.models import NetBoxModel +from jinja2 import TemplateError, TemplateSyntaxError, UndefinedError +from utilities.jinja2 import render_jinja2 +from netbox.models import NetBoxModel from nbxsync.constants import ASSIGNMENT_MODELS -from nbxsync.models import SyncInfoModel +from nbxsync.models import SyncInfoModel, ZabbixConfigurationGroup __all__ = ('ZabbixMacroAssignment',) +TEMPLATE_PATTERN = re.compile(r'({{.*?}}|{%-?\s*.*?\s*-?%}|{#.*?#})') + class ZabbixMacroAssignment(SyncInfoModel, NetBoxModel): zabbixmacro = models.ForeignKey('nbxsync.ZabbixMacro', on_delete=models.CASCADE, related_name='zabbixmacroassignment') @@ -59,6 +65,42 @@ def __str__(self): return f'{self.zabbixmacro.macro[:-1]}:{self.context}}}' return self.zabbixmacro.macro + def is_template(self): + return bool(TEMPLATE_PATTERN.search(self.value)) + + def render(self, **context): + if isinstance(self.assigned_object, ZabbixConfigurationGroup): + return self.value, True + + context = self.get_context(**context) + + try: + output = render_jinja2(self.value, context) + output = output.replace('\r\n', '\n') + return output, True + + except TemplateSyntaxError as err: + return self.value, False + + except UndefinedError as err: + return self.value, False + + except TemplateError as err: + return self.value, False + + except Exception as err: + return self.value, False + + def get_context(self, **extra_context): + context = { + 'object': self.assigned_object, + 'macro': self.zabbixmacro.macro, + 'value': self.value, + 'description': self.zabbixmacro.description, + } + context.update(extra_context) + return context + @property def full_name(self): return self diff --git a/nbxsync/tables/columns.py b/nbxsync/tables/columns.py index 1e8738a..4910f69 100644 --- a/nbxsync/tables/columns.py +++ b/nbxsync/tables/columns.py @@ -39,3 +39,13 @@ def render(self, value): if model is None: return capfirst(value.model.replace('_', ' ')) return capfirst(model._meta.verbose_name) + + +class JinjaValueColumn(tables.Column): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def render(self, value, record, table): + instance = getattr(table, 'instance', None) + output, success = record.render(object=instance) + return output diff --git a/nbxsync/tables/zabbixmacroassignment.py b/nbxsync/tables/zabbixmacroassignment.py index 34bfcd1..c748e9d 100644 --- a/nbxsync/tables/zabbixmacroassignment.py +++ b/nbxsync/tables/zabbixmacroassignment.py @@ -6,7 +6,7 @@ from nbxsync.models import ZabbixMacroAssignment from nbxsync.tables import ZabbixInheritedAssignmentTable -from nbxsync.tables.columns import ContentTypeModelNameColumn, InheritanceAwareActionsColumn +from nbxsync.tables.columns import ContentTypeModelNameColumn, InheritanceAwareActionsColumn, JinjaValueColumn from nbxsync.choices import ZabbixMacroTypeChoices __all__ = ('ZabbixMacroAssignmentTable', 'ZabbixMacroAssignmentObjectViewTable') @@ -18,6 +18,11 @@ class ZabbixMacroAssignmentTable(ZabbixInheritedAssignmentTable, NetBoxTable): zabbixmacro = tables.Column(accessor='zabbixmacro.macro', verbose_name=_('Zabbix Macro'), linkify={'viewname': 'plugins:nbxsync:zabbixmacro', 'args': [A('zabbixmacro.pk')]}) macro_full_name = tables.Column(accessor='full_name', verbose_name=_('Macro'), order_by='zabbixmacro__macro') actions = InheritanceAwareActionsColumn() + rendered_output = JinjaValueColumn( + verbose_name='Value', + orderable=False, + accessor='value', + ) class Meta(NetBoxTable.Meta): model = ZabbixMacroAssignment @@ -30,10 +35,10 @@ class Meta(NetBoxTable.Meta): 'inherited_from', 'is_context', 'regex', - 'value', + 'rendered_output', 'created', 'last_updated', - 'actions,', + 'actions', ) default_columns = ( 'pk', @@ -43,7 +48,7 @@ class Meta(NetBoxTable.Meta): 'macro_full_name', 'is_context', 'regex', - 'value', + 'rendered_output', 'inherited_from', ) @@ -58,7 +63,11 @@ class ZabbixMacroAssignmentObjectViewTable(ZabbixInheritedAssignmentTable, NetBo assigned_object = tables.Column(verbose_name=_('Assigned To'), linkify=True, orderable=False) macro_full_name = tables.Column(accessor='full_name', verbose_name=_('Macro'), order_by='zabbixmacro__macro', linkify={'viewname': 'plugins:nbxsync:zabbixmacro', 'args': [A('zabbixmacro.pk')]}) actions = InheritanceAwareActionsColumn() - value = tables.Column(verbose_name='Value') + rendered_output = JinjaValueColumn( + verbose_name='Value', + orderable=False, + accessor='value', + ) class Meta(NetBoxTable.Meta): model = ZabbixMacroAssignment @@ -71,7 +80,7 @@ class Meta(NetBoxTable.Meta): 'inherited_from', 'is_regex', 'context', - 'value', + 'rendered_output', 'created', 'last_updated', 'actions,', @@ -81,7 +90,7 @@ class Meta(NetBoxTable.Meta): 'macro_full_name', 'is_regex', 'context', - 'value', + 'rendered_output', 'inherited_from', ) diff --git a/nbxsync/templates/nbxsync/forms/zabbixhostinterface.html b/nbxsync/templates/nbxsync/forms/zabbixhostinterface.html index 2a5d916..dbfa9d9 100644 --- a/nbxsync/templates/nbxsync/forms/zabbixhostinterface.html +++ b/nbxsync/templates/nbxsync/forms/zabbixhostinterface.html @@ -123,7 +123,7 @@