mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-14 07:42:18 -06:00
Compare commits
1 Commits
cf16a29ad3
...
20068-impo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebf8f7fa1b |
@@ -13,7 +13,6 @@ class DataSourceStatusChoices(ChoiceSet):
|
||||
SYNCING = 'syncing'
|
||||
COMPLETED = 'completed'
|
||||
FAILED = 'failed'
|
||||
READY = 'ready'
|
||||
|
||||
CHOICES = (
|
||||
(NEW, _('New'), 'blue'),
|
||||
@@ -21,7 +20,6 @@ class DataSourceStatusChoices(ChoiceSet):
|
||||
(SYNCING, _('Syncing'), 'cyan'),
|
||||
(COMPLETED, _('Completed'), 'green'),
|
||||
(FAILED, _('Failed'), 'red'),
|
||||
(READY, _('Ready'), 'green'),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ from utilities.forms import get_field_value
|
||||
from utilities.forms.fields import CommentField, JSONField
|
||||
from utilities.forms.rendering import FieldSet
|
||||
from utilities.forms.widgets import HTMXSelect
|
||||
from core.choices import DataSourceStatusChoices
|
||||
|
||||
__all__ = (
|
||||
'ConfigRevisionForm',
|
||||
@@ -80,28 +79,14 @@ class DataSourceForm(NetBoxModelForm):
|
||||
if self.instance and self.instance.parameters:
|
||||
self.fields[field_name].initial = self.instance.parameters.get(name)
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
parameters = {}
|
||||
for name in self.fields:
|
||||
if name.startswith('backend_'):
|
||||
parameters[name[8:]] = self.cleaned_data[name]
|
||||
self.instance.parameters = parameters
|
||||
|
||||
# Determine initial status based on new/existing instance
|
||||
if not self.instance.pk:
|
||||
# New instance
|
||||
object_status = DataSourceStatusChoices.NEW
|
||||
else:
|
||||
# Existing instance
|
||||
if not self.cleaned_data.get("sync_interval"):
|
||||
object_status = DataSourceStatusChoices.READY
|
||||
else:
|
||||
object_status = self.instance.status
|
||||
|
||||
# # Final override only if the user explicitly provided a status
|
||||
self.instance.status = object_status
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
||||
|
||||
@@ -111,7 +111,10 @@ class DataSource(JobsMixin, PrimaryModel):
|
||||
|
||||
@property
|
||||
def ready_for_sync(self):
|
||||
return self.enabled and self.status != DataSourceStatusChoices.SYNCING
|
||||
return self.enabled and self.status not in (
|
||||
DataSourceStatusChoices.QUEUED,
|
||||
DataSourceStatusChoices.SYNCING
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
@@ -472,14 +472,30 @@ class ModuleTypeImportForm(NetBoxModelImportForm):
|
||||
required=False,
|
||||
help_text=_('Unit for module weight')
|
||||
)
|
||||
attribute_data = forms.JSONField(
|
||||
label=_('Attributes'),
|
||||
required=False,
|
||||
help_text=_('Attribute values for the assigned profile, passed as a dictionary')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ModuleType
|
||||
fields = [
|
||||
'manufacturer', 'model', 'part_number', 'description', 'airflow', 'weight', 'weight_unit', 'profile',
|
||||
'comments', 'tags'
|
||||
'attribute_data', 'comments', 'tags',
|
||||
]
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
# Attribute data may be included only if a profile is specified
|
||||
if self.cleaned_data.get('attribute_data') and not self.cleaned_data.get('profile'):
|
||||
raise forms.ValidationError(_("Profile must be specified if attribute data is provided."))
|
||||
|
||||
# Default attribute_data to an empty dictionary if a profile is specified (to enforce schema validation)
|
||||
if self.cleaned_data.get('profile') and not self.cleaned_data.get('attribute_data'):
|
||||
self.cleaned_data['attribute_data'] = {}
|
||||
|
||||
|
||||
class DeviceRoleImportForm(NetBoxModelImportForm):
|
||||
parent = CSVModelChoiceField(
|
||||
|
||||
@@ -2,14 +2,11 @@ import logging
|
||||
import traceback
|
||||
from contextlib import ExitStack
|
||||
|
||||
from django.db import router, transaction
|
||||
from django.db import DEFAULT_DB_ALIAS
|
||||
from django.db import transaction
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from core.signals import clear_events
|
||||
from dcim.models import Device
|
||||
from extras.models import Script as ScriptModel
|
||||
from netbox.context_managers import event_tracking
|
||||
from netbox.jobs import JobRunner
|
||||
from netbox.registry import registry
|
||||
from utilities.exceptions import AbortScript, AbortTransaction
|
||||
@@ -45,21 +42,10 @@ class ScriptJob(JobRunner):
|
||||
# A script can modify multiple models so need to do an atomic lock on
|
||||
# both the default database (for non ChangeLogged models) and potentially
|
||||
# any other database (for ChangeLogged models)
|
||||
changeloged_db = router.db_for_write(Device)
|
||||
with transaction.atomic(using=DEFAULT_DB_ALIAS):
|
||||
# If branch database is different from default, wrap in a second atomic transaction
|
||||
# Note: Don't add any extra code between the two atomic transactions,
|
||||
# otherwise the changes might get committed to the default database
|
||||
# if there are any raised exceptions.
|
||||
if changeloged_db != DEFAULT_DB_ALIAS:
|
||||
with transaction.atomic(using=changeloged_db):
|
||||
script.output = script.run(data, commit)
|
||||
if not commit:
|
||||
raise AbortTransaction()
|
||||
else:
|
||||
script.output = script.run(data, commit)
|
||||
if not commit:
|
||||
raise AbortTransaction()
|
||||
with transaction.atomic():
|
||||
script.output = script.run(data, commit)
|
||||
if not commit:
|
||||
raise AbortTransaction()
|
||||
except AbortTransaction:
|
||||
script.log_info(message=_("Database changes have been reverted automatically."))
|
||||
if script.failed:
|
||||
@@ -122,14 +108,14 @@ class ScriptJob(JobRunner):
|
||||
script.request = request
|
||||
self.logger.debug(f"Request ID: {request.id if request else None}")
|
||||
|
||||
# Execute the script. If commit is True, wrap it with the event_tracking context manager to ensure we process
|
||||
# change logging, event rules, etc.
|
||||
if commit:
|
||||
self.logger.info("Executing script (commit enabled)")
|
||||
with ExitStack() as stack:
|
||||
for request_processor in registry['request_processors']:
|
||||
stack.enter_context(request_processor(request))
|
||||
self.run_script(script, request, data, commit)
|
||||
else:
|
||||
self.logger.warning("Executing script (commit disabled)")
|
||||
|
||||
with ExitStack() as stack:
|
||||
for request_processor in registry['request_processors']:
|
||||
if not commit and request_processor is event_tracking:
|
||||
continue
|
||||
stack.enter_context(request_processor(request))
|
||||
self.run_script(script, request, data, commit)
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-12-04 05:01+0000\n"
|
||||
"POT-Creation-Date: 2025-12-02 05:02+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -8985,19 +8985,19 @@ msgstr ""
|
||||
msgid "Interval at which this script is re-run (in minutes)"
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/jobs.py:64
|
||||
#: netbox/extras/jobs.py:50
|
||||
msgid "Database changes have been reverted automatically."
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/jobs.py:70
|
||||
#: netbox/extras/jobs.py:56
|
||||
msgid "Script aborted with error: "
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/jobs.py:81
|
||||
#: netbox/extras/jobs.py:67
|
||||
msgid "An exception occurred: "
|
||||
msgstr ""
|
||||
|
||||
#: netbox/extras/jobs.py:87
|
||||
#: netbox/extras/jobs.py:73
|
||||
msgid "Database changes have been reverted due to error."
|
||||
msgstr ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user