소스 검색

Merge branch 'develop' into feature

Jeremy Stretch 1 년 전
부모
커밋
5d1070796d

+ 1 - 1
.github/ISSUE_TEMPLATE/01-feature_request.yaml

@@ -14,7 +14,7 @@ body:
     attributes:
       label: NetBox version
       description: What version of NetBox are you currently running?
-      placeholder: v4.1.10
+      placeholder: v4.1.11
     validations:
       required: true
   - type: dropdown

+ 1 - 1
.github/ISSUE_TEMPLATE/02-bug_report.yaml

@@ -39,7 +39,7 @@ body:
     attributes:
       label: NetBox Version
       description: What version of NetBox are you currently running?
-      placeholder: v4.1.10
+      placeholder: v4.1.11
     validations:
       required: true
   - type: dropdown

+ 1 - 1
base_requirements.txt

@@ -101,7 +101,7 @@ netaddr
 nh3
 
 # Fork of PIL (Python Imaging Library) for image processing
-# https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst
+# https://github.com/python-pillow/Pillow/releases
 Pillow
 
 # PostgreSQL database adapter for Python

+ 13 - 1
docs/models/extras/eventrule.md

@@ -10,7 +10,7 @@ See the [event rules documentation](../../features/event-rules.md)  for more inf
 
 A unique human-friendly name.
 
-### Content Types
+### Object Types
 
 The type(s) of object in NetBox that will trigger the rule.
 
@@ -38,3 +38,15 @@ The event types which will trigger the rule. At least one event type must be sel
 ### Conditions
 
 A set of [prescribed conditions](../../reference/conditions.md) against which the triggering object will be evaluated. If the conditions are defined but not met by the object, no action will be taken. An event rule that does not define any conditions will _always_ trigger.
+
+### Action Type
+
+The type of action to take when the rule triggers. This must be one of the following choices:
+
+* Webhook
+* Custom script
+* Notification
+
+### Action Data
+
+An optional dictionary of JSON data to pass when executing the rule. This can be useful to include additional context data, e.g. when transmitting a webhook.

+ 12 - 0
docs/release-notes/version-4.1.md

@@ -1,5 +1,17 @@
 # NetBox v4.1
 
+## v4.1.11 (2025-01-06)
+
+### Bug Fixes
+
+* [#17771](https://github.com/netbox-community/netbox/issues/17771) - Fix duplicate entries appearing on VLAN list when filtering by interface assignment
+* [#18222](https://github.com/netbox-community/netbox/issues/18222) - Pass event rule action data to webhooks as context data
+* [#18263](https://github.com/netbox-community/netbox/issues/18263) - Fix recalculation of cable paths when modifying cable terminations via the REST API
+* [#18271](https://github.com/netbox-community/netbox/issues/18271) - Require only encryption _or_ authentication algorithm when creating an IPSec proposal via the REST API
+* [#18289](https://github.com/netbox-community/netbox/issues/18289) - Enable ordering modules and module types by created & last updated times
+
+---
+
 ## v4.1.10 (2024-12-23)
 
 ### Bug Fixes

+ 4 - 0
netbox/dcim/models/cables.py

@@ -605,6 +605,10 @@ class CablePath(models.Model):
                     cable_end = 'A' if lct.cable_end == 'B' else 'B'
                     q_filter |= Q(cable=lct.cable, cable_end=cable_end)
 
+                # Make sure this filter has been populated; if not, we have probably been given invalid data
+                if not q_filter:
+                    break
+
                 remote_cable_terminations = CableTermination.objects.filter(q_filter)
                 remote_terminations = [ct.termination for ct in remote_cable_terminations]
             else:

+ 2 - 1
netbox/dcim/signals.py

@@ -85,7 +85,8 @@ def update_connected_endpoints(instance, created, raw=False, **kwargs):
     if instance._terminations_modified:
         a_terminations = []
         b_terminations = []
-        for t in instance.terminations.all():
+        # Note: instance.terminations.all() is not safe to use here as it might be stale
+        for t in CableTermination.objects.filter(cable=instance):
             if t.cable_end == CableEndChoices.SIDE_A:
                 a_terminations.append(t.termination)
             else:

+ 2 - 1
netbox/dcim/tables/modules.py

@@ -41,6 +41,7 @@ class ModuleTypeTable(NetBoxTable):
         model = ModuleType
         fields = (
             'pk', 'id', 'model', 'manufacturer', 'part_number', 'airflow', 'weight', 'description', 'comments', 'tags',
+            'created', 'last_updated',
         )
         default_columns = (
             'pk', 'model', 'manufacturer', 'part_number',
@@ -79,7 +80,7 @@ class ModuleTable(NetBoxTable):
         model = Module
         fields = (
             'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'status', 'serial', 'asset_tag',
-            'description', 'comments', 'tags',
+            'description', 'comments', 'tags', 'created', 'last_updated',
         )
         default_columns = (
             'pk', 'id', 'device', 'module_bay', 'manufacturer', 'module_type', 'status', 'serial', 'asset_tag',

+ 8 - 4
netbox/extras/events.py

@@ -90,6 +90,10 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non
         if not event_rule.eval_conditions(data):
             continue
 
+        # Compile event data
+        event_data = event_rule.action_data or {}
+        event_data.update(data)
+
         # Webhooks
         if event_rule.action_type == EventRuleActionChoices.WEBHOOK:
 
@@ -102,7 +106,7 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non
                 "event_rule": event_rule,
                 "model_name": object_type.model,
                 "event_type": event_type,
-                "data": data,
+                "data": event_data,
                 "snapshots": snapshots,
                 "timestamp": timezone.now().isoformat(),
                 "username": username,
@@ -130,7 +134,7 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non
                 instance=event_rule.action_object,
                 name=script.name,
                 user=user,
-                data=data
+                data=event_data
             )
 
         # Notification groups
@@ -138,8 +142,8 @@ def process_event_rules(event_rules, object_type, event_type, data, username=Non
             # Bulk-create notifications for all members of the notification group
             event_rule.action_object.notify(
                 object_type=object_type,
-                object_id=data['id'],
-                object_repr=data.get('display'),
+                object_id=event_data['id'],
+                object_repr=event_data.get('display'),
                 event_type=event_type
             )
 

+ 13 - 3
netbox/extras/tests/test_event_rules.py

@@ -50,21 +50,24 @@ class EventRuleTest(APITestCase):
                 event_types=[OBJECT_CREATED],
                 action_type=EventRuleActionChoices.WEBHOOK,
                 action_object_type=webhook_type,
-                action_object_id=webhooks[0].id
+                action_object_id=webhooks[0].id,
+                action_data={"foo": 1},
             ),
             EventRule(
                 name='Event Rule 2',
                 event_types=[OBJECT_UPDATED],
                 action_type=EventRuleActionChoices.WEBHOOK,
                 action_object_type=webhook_type,
-                action_object_id=webhooks[0].id
+                action_object_id=webhooks[0].id,
+                action_data={"foo": 2},
             ),
             EventRule(
                 name='Event Rule 3',
                 event_types=[OBJECT_DELETED],
                 action_type=EventRuleActionChoices.WEBHOOK,
                 action_object_type=webhook_type,
-                action_object_id=webhooks[0].id
+                action_object_id=webhooks[0].id,
+                action_data={"foo": 3},
             ),
         ))
         for event_rule in event_rules:
@@ -134,6 +137,7 @@ class EventRuleTest(APITestCase):
         self.assertEqual(job.kwargs['event_type'], OBJECT_CREATED)
         self.assertEqual(job.kwargs['model_name'], 'site')
         self.assertEqual(job.kwargs['data']['id'], response.data['id'])
+        self.assertEqual(job.kwargs['data']['foo'], 1)
         self.assertEqual(len(job.kwargs['data']['tags']), len(response.data['tags']))
         self.assertEqual(job.kwargs['snapshots']['postchange']['name'], 'Site 1')
         self.assertEqual(job.kwargs['snapshots']['postchange']['tags'], ['Bar', 'Foo'])
@@ -184,6 +188,7 @@ class EventRuleTest(APITestCase):
             self.assertEqual(job.kwargs['event_type'], OBJECT_CREATED)
             self.assertEqual(job.kwargs['model_name'], 'site')
             self.assertEqual(job.kwargs['data']['id'], response.data[i]['id'])
+            self.assertEqual(job.kwargs['data']['foo'], 1)
             self.assertEqual(len(job.kwargs['data']['tags']), len(response.data[i]['tags']))
             self.assertEqual(job.kwargs['snapshots']['postchange']['name'], response.data[i]['name'])
             self.assertEqual(job.kwargs['snapshots']['postchange']['tags'], ['Bar', 'Foo'])
@@ -215,6 +220,7 @@ class EventRuleTest(APITestCase):
         self.assertEqual(job.kwargs['event_type'], OBJECT_UPDATED)
         self.assertEqual(job.kwargs['model_name'], 'site')
         self.assertEqual(job.kwargs['data']['id'], site.pk)
+        self.assertEqual(job.kwargs['data']['foo'], 2)
         self.assertEqual(len(job.kwargs['data']['tags']), len(response.data['tags']))
         self.assertEqual(job.kwargs['snapshots']['prechange']['name'], 'Site 1')
         self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo'])
@@ -271,6 +277,7 @@ class EventRuleTest(APITestCase):
             self.assertEqual(job.kwargs['event_type'], OBJECT_UPDATED)
             self.assertEqual(job.kwargs['model_name'], 'site')
             self.assertEqual(job.kwargs['data']['id'], data[i]['id'])
+            self.assertEqual(job.kwargs['data']['foo'], 2)
             self.assertEqual(len(job.kwargs['data']['tags']), len(response.data[i]['tags']))
             self.assertEqual(job.kwargs['snapshots']['prechange']['name'], sites[i].name)
             self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo'])
@@ -297,6 +304,7 @@ class EventRuleTest(APITestCase):
         self.assertEqual(job.kwargs['event_type'], OBJECT_DELETED)
         self.assertEqual(job.kwargs['model_name'], 'site')
         self.assertEqual(job.kwargs['data']['id'], site.pk)
+        self.assertEqual(job.kwargs['data']['foo'], 3)
         self.assertEqual(job.kwargs['snapshots']['prechange']['name'], 'Site 1')
         self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo'])
 
@@ -330,6 +338,7 @@ class EventRuleTest(APITestCase):
             self.assertEqual(job.kwargs['event_type'], OBJECT_DELETED)
             self.assertEqual(job.kwargs['model_name'], 'site')
             self.assertEqual(job.kwargs['data']['id'], sites[i].pk)
+            self.assertEqual(job.kwargs['data']['foo'], 3)
             self.assertEqual(job.kwargs['snapshots']['prechange']['name'], sites[i].name)
             self.assertEqual(job.kwargs['snapshots']['prechange']['tags'], ['Bar', 'Foo'])
 
@@ -358,6 +367,7 @@ class EventRuleTest(APITestCase):
             self.assertEqual(body['username'], 'testuser')
             self.assertEqual(body['request_id'], str(request_id))
             self.assertEqual(body['data']['name'], 'Site 1')
+            self.assertEqual(body['data']['foo'], 1)
 
             return HttpResponse()
 

+ 2 - 3
netbox/release.yaml

@@ -1,4 +1,3 @@
-version: "4.2"
+version: "4.2.0"
 edition: "Community"
-published: "2024-12-02"
-designation: "beta1"
+published: "2025-01-06"

+ 9 - 9
netbox/translations/en/LC_MESSAGES/django.po

@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-13 05:02+0000\n"
+"POT-Creation-Date: 2025-01-04 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"
@@ -650,7 +650,7 @@ msgstr ""
 #: netbox/dcim/forms/filtersets.py:924 netbox/dcim/forms/filtersets.py:958
 #: netbox/dcim/forms/filtersets.py:1059 netbox/dcim/forms/filtersets.py:1170
 #: netbox/dcim/tables/devices.py:140 netbox/dcim/tables/devices.py:817
-#: netbox/dcim/tables/devices.py:1063 netbox/dcim/tables/modules.py:69
+#: netbox/dcim/tables/devices.py:1063 netbox/dcim/tables/modules.py:70
 #: netbox/dcim/tables/power.py:74 netbox/dcim/tables/racks.py:126
 #: netbox/dcim/tables/sites.py:82 netbox/dcim/tables/sites.py:138
 #: netbox/ipam/forms/bulk_edit.py:256 netbox/ipam/forms/bulk_edit.py:306
@@ -1508,7 +1508,7 @@ msgstr ""
 #: netbox/circuits/tables/providers.py:82
 #: netbox/circuits/tables/providers.py:107 netbox/dcim/tables/devices.py:1036
 #: netbox/dcim/tables/devicetypes.py:92 netbox/dcim/tables/modules.py:29
-#: netbox/dcim/tables/modules.py:72 netbox/dcim/tables/power.py:39
+#: netbox/dcim/tables/modules.py:73 netbox/dcim/tables/power.py:39
 #: netbox/dcim/tables/power.py:96 netbox/dcim/tables/racks.py:84
 #: netbox/dcim/tables/racks.py:145 netbox/dcim/tables/racks.py:225
 #: netbox/dcim/tables/sites.py:108 netbox/extras/tables/tables.py:582
@@ -3432,7 +3432,7 @@ msgstr ""
 #: netbox/dcim/tables/devices.py:96 netbox/dcim/tables/devices.py:172
 #: netbox/dcim/tables/devices.py:940 netbox/dcim/tables/devicetypes.py:80
 #: netbox/dcim/tables/devicetypes.py:308 netbox/dcim/tables/modules.py:20
-#: netbox/dcim/tables/modules.py:60 netbox/dcim/tables/racks.py:58
+#: netbox/dcim/tables/modules.py:61 netbox/dcim/tables/racks.py:58
 #: netbox/dcim/tables/racks.py:132 netbox/templates/dcim/devicetype.html:14
 #: netbox/templates/dcim/inventoryitem.html:44
 #: netbox/templates/dcim/manufacturer.html:33
@@ -3679,7 +3679,7 @@ msgid "Device Type"
 msgstr ""
 
 #: netbox/dcim/forms/bulk_edit.py:598 netbox/dcim/forms/model_forms.py:401
-#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:65
+#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66
 #: netbox/templates/dcim/module.html:65 netbox/templates/dcim/modulebay.html:66
 #: netbox/templates/dcim/moduletype.html:22
 msgid "Module Type"
@@ -3785,7 +3785,7 @@ msgstr ""
 #: netbox/dcim/tables/devices.py:597 netbox/dcim/tables/devices.py:697
 #: netbox/dcim/tables/devices.py:754 netbox/dcim/tables/devices.py:801
 #: netbox/dcim/tables/devices.py:861 netbox/dcim/tables/devices.py:930
-#: netbox/dcim/tables/devices.py:1057 netbox/dcim/tables/modules.py:52
+#: netbox/dcim/tables/devices.py:1057 netbox/dcim/tables/modules.py:53
 #: netbox/extras/forms/filtersets.py:321 netbox/ipam/forms/bulk_import.py:304
 #: netbox/ipam/forms/bulk_import.py:505 netbox/ipam/forms/filtersets.py:551
 #: netbox/ipam/forms/model_forms.py:323 netbox/ipam/forms/model_forms.py:712
@@ -6617,7 +6617,7 @@ msgstr ""
 msgid "Inventory items"
 msgstr ""
 
-#: netbox/dcim/tables/devices.py:305 netbox/dcim/tables/modules.py:56
+#: netbox/dcim/tables/devices.py:305 netbox/dcim/tables/modules.py:57
 #: netbox/templates/dcim/modulebay.html:17
 msgid "Module Bay"
 msgstr ""
@@ -7336,12 +7336,12 @@ msgstr ""
 msgid "Show your personal bookmarks"
 msgstr ""
 
-#: netbox/extras/events.py:147
+#: netbox/extras/events.py:151
 #, python-brace-format
 msgid "Unknown action type for an event rule: {action_type}"
 msgstr ""
 
-#: netbox/extras/events.py:192
+#: netbox/extras/events.py:196
 #, python-brace-format
 msgid "Cannot import events pipeline {name} error: {error}"
 msgstr ""

BIN
netbox/translations/ru/LC_MESSAGES/django.mo


+ 130 - 114
netbox/translations/ru/LC_MESSAGES/django.po

@@ -11,17 +11,18 @@
 # stavr666, 2024
 # Alexander Ryazanov (alryaz) <alryaz@xavux.com>, 2024
 # Vladyslav V. Prodan, 2024
-# Artem Kotik, 2024
 # Jeremy Stretch, 2024
+# Michail Tatarinov, 2024
+# Artem Kotik, 2025
 # 
 #, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-12-12 05:02+0000\n"
+"POT-Creation-Date: 2025-01-04 05:02+0000\n"
 "PO-Revision-Date: 2023-10-30 17:48+0000\n"
-"Last-Translator: Jeremy Stretch, 2024\n"
+"Last-Translator: Artem Kotik, 2025\n"
 "Language-Team: Russian (https://app.transifex.com/netbox-community/teams/178115/ru/)\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -157,7 +158,7 @@ msgstr "Неактивный"
 #: netbox/dcim/filtersets.py:464 netbox/dcim/filtersets.py:1021
 #: netbox/dcim/filtersets.py:1368 netbox/dcim/filtersets.py:1903
 #: netbox/dcim/filtersets.py:2146 netbox/dcim/filtersets.py:2204
-#: netbox/ipam/filtersets.py:339 netbox/ipam/filtersets.py:959
+#: netbox/ipam/filtersets.py:341 netbox/ipam/filtersets.py:961
 #: netbox/virtualization/filtersets.py:45
 #: netbox/virtualization/filtersets.py:173 netbox/vpn/filtersets.py:358
 msgid "Region (ID)"
@@ -169,8 +170,8 @@ msgstr "Регион (ID)"
 #: netbox/dcim/filtersets.py:471 netbox/dcim/filtersets.py:1028
 #: netbox/dcim/filtersets.py:1375 netbox/dcim/filtersets.py:1910
 #: netbox/dcim/filtersets.py:2153 netbox/dcim/filtersets.py:2211
-#: netbox/extras/filtersets.py:509 netbox/ipam/filtersets.py:346
-#: netbox/ipam/filtersets.py:966 netbox/virtualization/filtersets.py:52
+#: netbox/extras/filtersets.py:509 netbox/ipam/filtersets.py:348
+#: netbox/ipam/filtersets.py:968 netbox/virtualization/filtersets.py:52
 #: netbox/virtualization/filtersets.py:180 netbox/vpn/filtersets.py:353
 msgid "Region (slug)"
 msgstr "Регион (подстрока)"
@@ -180,8 +181,8 @@ msgstr "Регион (подстрока)"
 #: netbox/dcim/filtersets.py:346 netbox/dcim/filtersets.py:477
 #: netbox/dcim/filtersets.py:1034 netbox/dcim/filtersets.py:1381
 #: netbox/dcim/filtersets.py:1916 netbox/dcim/filtersets.py:2159
-#: netbox/dcim/filtersets.py:2217 netbox/ipam/filtersets.py:352
-#: netbox/ipam/filtersets.py:972 netbox/virtualization/filtersets.py:58
+#: netbox/dcim/filtersets.py:2217 netbox/ipam/filtersets.py:354
+#: netbox/ipam/filtersets.py:974 netbox/virtualization/filtersets.py:58
 #: netbox/virtualization/filtersets.py:186
 msgid "Site group (ID)"
 msgstr "Группа сайтов (ID)"
@@ -192,7 +193,7 @@ msgstr "Группа сайтов (ID)"
 #: netbox/dcim/filtersets.py:1041 netbox/dcim/filtersets.py:1388
 #: netbox/dcim/filtersets.py:1923 netbox/dcim/filtersets.py:2166
 #: netbox/dcim/filtersets.py:2224 netbox/extras/filtersets.py:515
-#: netbox/ipam/filtersets.py:359 netbox/ipam/filtersets.py:979
+#: netbox/ipam/filtersets.py:361 netbox/ipam/filtersets.py:981
 #: netbox/virtualization/filtersets.py:65
 #: netbox/virtualization/filtersets.py:193
 msgid "Site group (slug)"
@@ -262,8 +263,8 @@ msgstr "Сайт"
 #: netbox/circuits/filtersets.py:62 netbox/circuits/filtersets.py:229
 #: netbox/circuits/filtersets.py:274 netbox/dcim/filtersets.py:242
 #: netbox/dcim/filtersets.py:363 netbox/dcim/filtersets.py:458
-#: netbox/extras/filtersets.py:531 netbox/ipam/filtersets.py:238
-#: netbox/ipam/filtersets.py:369 netbox/ipam/filtersets.py:989
+#: netbox/extras/filtersets.py:531 netbox/ipam/filtersets.py:240
+#: netbox/ipam/filtersets.py:371 netbox/ipam/filtersets.py:991
 #: netbox/virtualization/filtersets.py:75
 #: netbox/virtualization/filtersets.py:203 netbox/vpn/filtersets.py:363
 msgid "Site (slug)"
@@ -282,13 +283,13 @@ msgstr "ASN"
 
 #: netbox/circuits/filtersets.py:95 netbox/circuits/filtersets.py:122
 #: netbox/circuits/filtersets.py:156 netbox/circuits/filtersets.py:283
-#: netbox/circuits/filtersets.py:325 netbox/ipam/filtersets.py:243
+#: netbox/circuits/filtersets.py:325 netbox/ipam/filtersets.py:245
 msgid "Provider (ID)"
 msgstr "Провайдер (ID)"
 
 #: netbox/circuits/filtersets.py:101 netbox/circuits/filtersets.py:128
 #: netbox/circuits/filtersets.py:162 netbox/circuits/filtersets.py:289
-#: netbox/circuits/filtersets.py:331 netbox/ipam/filtersets.py:249
+#: netbox/circuits/filtersets.py:331 netbox/ipam/filtersets.py:251
 msgid "Provider (slug)"
 msgstr "Провайдер (подстрока)"
 
@@ -317,8 +318,8 @@ msgstr "Тип канала связи (подстрока)"
 #: netbox/dcim/filtersets.py:452 netbox/dcim/filtersets.py:1045
 #: netbox/dcim/filtersets.py:1393 netbox/dcim/filtersets.py:1928
 #: netbox/dcim/filtersets.py:2170 netbox/dcim/filtersets.py:2229
-#: netbox/ipam/filtersets.py:232 netbox/ipam/filtersets.py:363
-#: netbox/ipam/filtersets.py:983 netbox/virtualization/filtersets.py:69
+#: netbox/ipam/filtersets.py:234 netbox/ipam/filtersets.py:365
+#: netbox/ipam/filtersets.py:985 netbox/virtualization/filtersets.py:69
 #: netbox/virtualization/filtersets.py:197 netbox/vpn/filtersets.py:368
 msgid "Site (ID)"
 msgstr "Сайт (ID)"
@@ -672,7 +673,7 @@ msgstr "Аккаунт провайдера"
 #: netbox/dcim/forms/filtersets.py:924 netbox/dcim/forms/filtersets.py:958
 #: netbox/dcim/forms/filtersets.py:1059 netbox/dcim/forms/filtersets.py:1170
 #: netbox/dcim/tables/devices.py:140 netbox/dcim/tables/devices.py:817
-#: netbox/dcim/tables/devices.py:1063 netbox/dcim/tables/modules.py:69
+#: netbox/dcim/tables/devices.py:1063 netbox/dcim/tables/modules.py:70
 #: netbox/dcim/tables/power.py:74 netbox/dcim/tables/racks.py:126
 #: netbox/dcim/tables/sites.py:82 netbox/dcim/tables/sites.py:138
 #: netbox/ipam/forms/bulk_edit.py:256 netbox/ipam/forms/bulk_edit.py:306
@@ -1107,7 +1108,7 @@ msgstr "Задание"
 #: netbox/circuits/tables/circuits.py:155 netbox/dcim/forms/bulk_edit.py:118
 #: netbox/dcim/forms/bulk_import.py:100 netbox/dcim/forms/model_forms.py:117
 #: netbox/dcim/tables/sites.py:89 netbox/extras/forms/filtersets.py:480
-#: netbox/ipam/filtersets.py:999 netbox/ipam/forms/bulk_edit.py:493
+#: netbox/ipam/filtersets.py:1001 netbox/ipam/forms/bulk_edit.py:493
 #: netbox/ipam/forms/bulk_import.py:460 netbox/ipam/forms/model_forms.py:561
 #: netbox/ipam/tables/fhrp.py:67 netbox/ipam/tables/vlans.py:122
 #: netbox/ipam/tables/vlans.py:226
@@ -1547,7 +1548,7 @@ msgstr "Гарантированная скорость"
 #: netbox/circuits/tables/providers.py:82
 #: netbox/circuits/tables/providers.py:107 netbox/dcim/tables/devices.py:1036
 #: netbox/dcim/tables/devicetypes.py:92 netbox/dcim/tables/modules.py:29
-#: netbox/dcim/tables/modules.py:72 netbox/dcim/tables/power.py:39
+#: netbox/dcim/tables/modules.py:73 netbox/dcim/tables/power.py:39
 #: netbox/dcim/tables/power.py:96 netbox/dcim/tables/racks.py:84
 #: netbox/dcim/tables/racks.py:145 netbox/dcim/tables/racks.py:225
 #: netbox/dcim/tables/sites.py:108 netbox/extras/tables/tables.py:582
@@ -1955,7 +1956,7 @@ msgstr "Фасады стоек"
 #: netbox/dcim/forms/bulk_edit.py:1390 netbox/dcim/tables/racks.py:158
 #: netbox/netbox/navigation/menu.py:291 netbox/netbox/navigation/menu.py:295
 msgid "Power"
-msgstr "Мощность"
+msgstr "Электропитание"
 
 #: netbox/core/forms/model_forms.py:159 netbox/netbox/navigation/menu.py:154
 #: netbox/templates/core/inc/config_data.html:37
@@ -2945,7 +2946,7 @@ msgid "Parent site group (slug)"
 msgstr "Группа сайтов родителя (подстрока)"
 
 #: netbox/dcim/filtersets.py:164 netbox/extras/filtersets.py:364
-#: netbox/ipam/filtersets.py:841 netbox/ipam/filtersets.py:993
+#: netbox/ipam/filtersets.py:843 netbox/ipam/filtersets.py:995
 msgid "Group (ID)"
 msgstr "Группа (ID)"
 
@@ -3003,15 +3004,15 @@ msgstr "Тип стойки (ID)"
 
 #: netbox/dcim/filtersets.py:411 netbox/dcim/filtersets.py:892
 #: netbox/dcim/filtersets.py:994 netbox/dcim/filtersets.py:1850
-#: netbox/ipam/filtersets.py:381 netbox/ipam/filtersets.py:493
-#: netbox/ipam/filtersets.py:1003 netbox/virtualization/filtersets.py:210
+#: netbox/ipam/filtersets.py:383 netbox/ipam/filtersets.py:495
+#: netbox/ipam/filtersets.py:1005 netbox/virtualization/filtersets.py:210
 msgid "Role (ID)"
 msgstr "Роль (ID)"
 
 #: netbox/dcim/filtersets.py:417 netbox/dcim/filtersets.py:898
 #: netbox/dcim/filtersets.py:1000 netbox/dcim/filtersets.py:1856
-#: netbox/extras/filtersets.py:558 netbox/ipam/filtersets.py:387
-#: netbox/ipam/filtersets.py:499 netbox/ipam/filtersets.py:1009
+#: netbox/extras/filtersets.py:558 netbox/ipam/filtersets.py:389
+#: netbox/ipam/filtersets.py:501 netbox/ipam/filtersets.py:1011
 #: netbox/virtualization/filtersets.py:216
 msgid "Role (slug)"
 msgstr "Роль (подстрока)"
@@ -3209,7 +3210,7 @@ msgstr "VDC (ID)"
 msgid "Device model"
 msgstr "модель устройства"
 
-#: netbox/dcim/filtersets.py:1267 netbox/ipam/filtersets.py:632
+#: netbox/dcim/filtersets.py:1267 netbox/ipam/filtersets.py:634
 #: netbox/vpn/filtersets.py:102 netbox/vpn/filtersets.py:401
 msgid "Interface (ID)"
 msgstr "Интерфейс (ID)"
@@ -3223,8 +3224,8 @@ msgid "Module bay (ID)"
 msgstr "Отсек для модулей (ID)"
 
 #: netbox/dcim/filtersets.py:1333 netbox/dcim/filtersets.py:1425
-#: netbox/ipam/filtersets.py:611 netbox/ipam/filtersets.py:851
-#: netbox/ipam/filtersets.py:1115 netbox/virtualization/filtersets.py:161
+#: netbox/ipam/filtersets.py:613 netbox/ipam/filtersets.py:853
+#: netbox/ipam/filtersets.py:1117 netbox/virtualization/filtersets.py:161
 #: netbox/vpn/filtersets.py:379
 msgid "Device (ID)"
 msgstr "Устройство (ID)"
@@ -3233,8 +3234,8 @@ msgstr "Устройство (ID)"
 msgid "Rack (name)"
 msgstr "Стойка (имя)"
 
-#: netbox/dcim/filtersets.py:1431 netbox/ipam/filtersets.py:606
-#: netbox/ipam/filtersets.py:846 netbox/ipam/filtersets.py:1121
+#: netbox/dcim/filtersets.py:1431 netbox/ipam/filtersets.py:608
+#: netbox/ipam/filtersets.py:848 netbox/ipam/filtersets.py:1123
 #: netbox/vpn/filtersets.py:374
 msgid "Device (name)"
 msgstr "Устройство (имя)"
@@ -3286,9 +3287,9 @@ msgstr "Назначенный VID"
 #: netbox/dcim/forms/bulk_import.py:913 netbox/dcim/forms/filtersets.py:1428
 #: netbox/dcim/forms/model_forms.py:1385
 #: netbox/dcim/models/device_components.py:711
-#: netbox/dcim/tables/devices.py:626 netbox/ipam/filtersets.py:316
-#: netbox/ipam/filtersets.py:327 netbox/ipam/filtersets.py:483
-#: netbox/ipam/filtersets.py:584 netbox/ipam/filtersets.py:595
+#: netbox/dcim/tables/devices.py:626 netbox/ipam/filtersets.py:318
+#: netbox/ipam/filtersets.py:329 netbox/ipam/filtersets.py:485
+#: netbox/ipam/filtersets.py:586 netbox/ipam/filtersets.py:597
 #: netbox/ipam/forms/bulk_edit.py:242 netbox/ipam/forms/bulk_edit.py:298
 #: netbox/ipam/forms/bulk_edit.py:340 netbox/ipam/forms/bulk_import.py:157
 #: netbox/ipam/forms/bulk_import.py:243 netbox/ipam/forms/bulk_import.py:279
@@ -3315,19 +3316,19 @@ msgstr "Назначенный VID"
 msgid "VRF"
 msgstr "VRF"
 
-#: netbox/dcim/filtersets.py:1619 netbox/ipam/filtersets.py:322
-#: netbox/ipam/filtersets.py:333 netbox/ipam/filtersets.py:489
-#: netbox/ipam/filtersets.py:590 netbox/ipam/filtersets.py:601
+#: netbox/dcim/filtersets.py:1619 netbox/ipam/filtersets.py:324
+#: netbox/ipam/filtersets.py:335 netbox/ipam/filtersets.py:491
+#: netbox/ipam/filtersets.py:592 netbox/ipam/filtersets.py:603
 msgid "VRF (RD)"
 msgstr "VRF (RD)"
 
-#: netbox/dcim/filtersets.py:1624 netbox/ipam/filtersets.py:1030
+#: netbox/dcim/filtersets.py:1624 netbox/ipam/filtersets.py:1032
 #: netbox/vpn/filtersets.py:342
 msgid "L2VPN (ID)"
 msgstr "L2VPN (ID)"
 
 #: netbox/dcim/filtersets.py:1630 netbox/dcim/forms/filtersets.py:1433
-#: netbox/dcim/tables/devices.py:570 netbox/ipam/filtersets.py:1036
+#: netbox/dcim/tables/devices.py:570 netbox/ipam/filtersets.py:1038
 #: netbox/ipam/forms/filtersets.py:518 netbox/ipam/tables/vlans.py:137
 #: netbox/templates/dcim/interface.html:93 netbox/templates/ipam/vlan.html:66
 #: netbox/templates/vpn/l2vpntermination.html:12
@@ -3421,7 +3422,7 @@ msgstr "Нерасторгнутый"
 
 #: netbox/dcim/filtersets.py:2239
 msgid "Power panel (ID)"
-msgstr "Панель питания (ID)"
+msgstr "Распределительный щит (ID)"
 
 #: netbox/dcim/forms/bulk_create.py:40 netbox/extras/forms/filtersets.py:401
 #: netbox/extras/forms/model_forms.py:567
@@ -3489,7 +3490,7 @@ msgstr "Часовой пояс"
 #: netbox/dcim/forms/object_import.py:187 netbox/dcim/tables/devices.py:96
 #: netbox/dcim/tables/devices.py:172 netbox/dcim/tables/devices.py:940
 #: netbox/dcim/tables/devicetypes.py:80 netbox/dcim/tables/devicetypes.py:308
-#: netbox/dcim/tables/modules.py:20 netbox/dcim/tables/modules.py:60
+#: netbox/dcim/tables/modules.py:20 netbox/dcim/tables/modules.py:61
 #: netbox/dcim/tables/racks.py:58 netbox/dcim/tables/racks.py:132
 #: netbox/templates/dcim/devicetype.html:14
 #: netbox/templates/dcim/inventoryitem.html:44
@@ -3740,7 +3741,7 @@ msgid "Device Type"
 msgstr "Тип устройства"
 
 #: netbox/dcim/forms/bulk_edit.py:598 netbox/dcim/forms/model_forms.py:401
-#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:65
+#: netbox/dcim/tables/modules.py:17 netbox/dcim/tables/modules.py:66
 #: netbox/templates/dcim/module.html:65
 #: netbox/templates/dcim/modulebay.html:66
 #: netbox/templates/dcim/moduletype.html:22
@@ -3848,7 +3849,7 @@ msgstr "Кластер"
 #: netbox/dcim/tables/devices.py:697 netbox/dcim/tables/devices.py:754
 #: netbox/dcim/tables/devices.py:801 netbox/dcim/tables/devices.py:861
 #: netbox/dcim/tables/devices.py:930 netbox/dcim/tables/devices.py:1057
-#: netbox/dcim/tables/modules.py:52 netbox/extras/forms/filtersets.py:321
+#: netbox/dcim/tables/modules.py:53 netbox/extras/forms/filtersets.py:321
 #: netbox/ipam/forms/bulk_import.py:304 netbox/ipam/forms/bulk_import.py:505
 #: netbox/ipam/forms/filtersets.py:551 netbox/ipam/forms/model_forms.py:323
 #: netbox/ipam/forms/model_forms.py:712 netbox/ipam/forms/model_forms.py:745
@@ -3946,7 +3947,7 @@ msgstr "Домен"
 #: netbox/dcim/forms/bulk_edit.py:920 netbox/dcim/forms/bulk_import.py:1367
 #: netbox/dcim/forms/filtersets.py:1158 netbox/dcim/forms/model_forms.py:750
 msgid "Power panel"
-msgstr "Панель питания"
+msgstr "Распределительный щит"
 
 #: netbox/dcim/forms/bulk_edit.py:942 netbox/dcim/forms/bulk_import.py:1403
 #: netbox/dcim/forms/filtersets.py:1180
@@ -4096,15 +4097,15 @@ msgstr "VLAN без тегов"
 #: netbox/virtualization/forms/bulk_edit.py:256
 #: netbox/virtualization/forms/model_forms.py:335
 msgid "Tagged VLANs"
-msgstr "VLAN с тегами"
+msgstr "Тегированные VLAN-ы"
 
 #: netbox/dcim/forms/bulk_edit.py:1511
 msgid "Add tagged VLANs"
-msgstr ""
+msgstr "Добавить тегированные VLAN-ы"
 
 #: netbox/dcim/forms/bulk_edit.py:1520
 msgid "Remove tagged VLANs"
-msgstr ""
+msgstr "Удалить тегированные VLAN-ы"
 
 #: netbox/dcim/forms/bulk_edit.py:1536 netbox/dcim/forms/model_forms.py:1348
 msgid "Wireless LAN group"
@@ -4152,7 +4153,7 @@ msgstr "Коммутация 802.1Q"
 
 #: netbox/dcim/forms/bulk_edit.py:1558
 msgid "Add/Remove"
-msgstr ""
+msgstr "Добавить/удалить"
 
 #: netbox/dcim/forms/bulk_edit.py:1617 netbox/dcim/forms/bulk_edit.py:1619
 msgid "Interface mode must be specified to assign VLANs"
@@ -4230,7 +4231,7 @@ msgstr "Название назначенной роли"
 
 #: netbox/dcim/forms/bulk_import.py:264
 msgid "Rack type model"
-msgstr ""
+msgstr "Модель типа стойки"
 
 #: netbox/dcim/forms/bulk_import.py:292 netbox/dcim/forms/bulk_import.py:435
 #: netbox/dcim/forms/bulk_import.py:605
@@ -4239,11 +4240,11 @@ msgstr "Направление воздушного потока"
 
 #: netbox/dcim/forms/bulk_import.py:324
 msgid "Width must be set if not specifying a rack type."
-msgstr ""
+msgstr "Если не указан тип стойки, необходимо задать ширину."
 
 #: netbox/dcim/forms/bulk_import.py:326
 msgid "U height must be set if not specifying a rack type."
-msgstr ""
+msgstr "Если не указан тип стойки, необходимо задать высоту в юнитах."
 
 #: netbox/dcim/forms/bulk_import.py:334
 msgid "Parent site"
@@ -4596,7 +4597,7 @@ msgstr "Имя родительского сайта"
 
 #: netbox/dcim/forms/bulk_import.py:1370
 msgid "Upstream power panel"
-msgstr "Панель питания в восходящем направлении"
+msgstr "Распределительный щит"
 
 #: netbox/dcim/forms/bulk_import.py:1400
 msgid "Primary or redundant"
@@ -4684,13 +4685,13 @@ msgstr "A {model} названный {name} уже существует"
 #: netbox/templates/dcim/powerpanel.html:19
 #: netbox/templates/dcim/trace/powerpanel.html:4
 msgid "Power Panel"
-msgstr "Панель питания"
+msgstr "Распределительный щит"
 
 #: netbox/dcim/forms/connections.py:58 netbox/dcim/forms/model_forms.py:765
 #: netbox/templates/dcim/powerfeed.html:21
 #: netbox/templates/dcim/powerport.html:80
 msgid "Power Feed"
-msgstr "Подача питания"
+msgstr "Кабель питания"
 
 #: netbox/dcim/forms/connections.py:81
 msgid "Side"
@@ -4905,6 +4906,10 @@ msgid ""
 "present, will be automatically replaced with the position value when "
 "creating a new module."
 msgstr ""
+"Для массового создания поддерживаются алфавитно-цифровые диапазоны. "
+"Смешанные имена и номера в одном диапазоне не поддерживаются (например: "
+"<code>[ge, xe]-0/0/[0-9]</code>). Переменная {module}</code> будет "
+"автоматически заменена значением позиции при создании нового модуля."
 
 #: netbox/dcim/forms/model_forms.py:1094
 msgid "Console port template"
@@ -5629,7 +5634,7 @@ msgstr "VLAN без тегов"
 #: netbox/dcim/models/device_components.py:703
 #: netbox/virtualization/models/virtualmachines.py:341
 msgid "tagged VLANs"
-msgstr "VLAN без тегов"
+msgstr "тегированные VLAN"
 
 #: netbox/dcim/models/device_components.py:745
 #: netbox/virtualization/models/virtualmachines.py:377
@@ -6325,11 +6330,11 @@ msgstr "При установке веса необходимо указать 
 
 #: netbox/dcim/models/power.py:55
 msgid "power panel"
-msgstr "панель питания"
+msgstr "распределительный щит"
 
 #: netbox/dcim/models/power.py:56
 msgid "power panels"
-msgstr "панели питания"
+msgstr "распределительный щиты"
 
 #: netbox/dcim/models/power.py:70
 #, python-brace-format
@@ -6369,11 +6374,11 @@ msgstr "доступная мощность"
 
 #: netbox/dcim/models/power.py:164
 msgid "power feed"
-msgstr "подача питания"
+msgstr "Кабель питания"
 
 #: netbox/dcim/models/power.py:165
 msgid "power feeds"
-msgstr "источники питания"
+msgstr "кабели питания"
 
 #: netbox/dcim/models/power.py:179
 #, python-brace-format
@@ -6381,7 +6386,7 @@ msgid ""
 "Rack {rack} ({rack_site}) and power panel {powerpanel} ({powerpanel_site}) "
 "are in different sites."
 msgstr ""
-"Стойка {rack} ({rack_site}) и панель питания {powerpanel} "
+"Стойка {rack} ({rack_site}) и распределительный щит {powerpanel} "
 "({powerpanel_site}) расположены на разных сайтах."
 
 #: netbox/dcim/models/power.py:190
@@ -6821,7 +6826,7 @@ msgstr "Отсеки для модулей"
 msgid "Inventory items"
 msgstr "Комплектующие"
 
-#: netbox/dcim/tables/devices.py:305 netbox/dcim/tables/modules.py:56
+#: netbox/dcim/tables/devices.py:305 netbox/dcim/tables/modules.py:57
 #: netbox/templates/dcim/modulebay.html:17
 msgid "Module Bay"
 msgstr "Модульный отсек"
@@ -7040,7 +7045,7 @@ msgstr "Отсеки для модулей"
 #: netbox/dcim/tables/power.py:36 netbox/netbox/navigation/menu.py:297
 #: netbox/templates/dcim/powerpanel.html:51
 msgid "Power Feeds"
-msgstr "Источники питания"
+msgstr "Кабели питания"
 
 #: netbox/dcim/tables/power.py:80 netbox/templates/dcim/powerfeed.html:99
 msgid "Max Utilization"
@@ -7550,12 +7555,12 @@ msgstr "Закладки"
 msgid "Show your personal bookmarks"
 msgstr "Покажите свои личные закладки"
 
-#: netbox/extras/events.py:147
+#: netbox/extras/events.py:151
 #, python-brace-format
 msgid "Unknown action type for an event rule: {action_type}"
 msgstr "Неизвестный тип действия для правила события: {action_type}"
 
-#: netbox/extras/events.py:192
+#: netbox/extras/events.py:196
 #, python-brace-format
 msgid "Cannot import events pipeline {name} error: {error}"
 msgstr "Невозможно импортировать конвейер событий {name} ошибка: {error}"
@@ -9326,129 +9331,129 @@ msgstr "Экспорт L2VPN"
 msgid "Exporting L2VPN (identifier)"
 msgstr "Экспорт L2VPN (идентификатор)"
 
-#: netbox/ipam/filtersets.py:155 netbox/ipam/filtersets.py:281
+#: netbox/ipam/filtersets.py:155 netbox/ipam/filtersets.py:283
 #: netbox/ipam/forms/model_forms.py:229 netbox/ipam/tables/ip.py:212
 #: netbox/templates/ipam/prefix.html:12
 msgid "Prefix"
 msgstr "Префикс"
 
 #: netbox/ipam/filtersets.py:159 netbox/ipam/filtersets.py:198
-#: netbox/ipam/filtersets.py:221
+#: netbox/ipam/filtersets.py:223
 msgid "RIR (ID)"
 msgstr "RIR (ID)"
 
 #: netbox/ipam/filtersets.py:165 netbox/ipam/filtersets.py:204
-#: netbox/ipam/filtersets.py:227
+#: netbox/ipam/filtersets.py:229
 msgid "RIR (slug)"
 msgstr "RIR (подстрока)"
 
-#: netbox/ipam/filtersets.py:285
+#: netbox/ipam/filtersets.py:287
 msgid "Within prefix"
 msgstr "В префиксе"
 
-#: netbox/ipam/filtersets.py:289
+#: netbox/ipam/filtersets.py:291
 msgid "Within and including prefix"
 msgstr "В префиксе и включительно"
 
-#: netbox/ipam/filtersets.py:293
+#: netbox/ipam/filtersets.py:295
 msgid "Prefixes which contain this prefix or IP"
 msgstr "Префиксы, содержащие этот префикс или IP-адрес"
 
-#: netbox/ipam/filtersets.py:304 netbox/ipam/filtersets.py:572
+#: netbox/ipam/filtersets.py:306 netbox/ipam/filtersets.py:574
 #: netbox/ipam/forms/bulk_edit.py:343 netbox/ipam/forms/filtersets.py:196
 #: netbox/ipam/forms/filtersets.py:331
 msgid "Mask length"
 msgstr "Длина маски"
 
-#: netbox/ipam/filtersets.py:373 netbox/vpn/filtersets.py:427
+#: netbox/ipam/filtersets.py:375 netbox/vpn/filtersets.py:427
 msgid "VLAN (ID)"
 msgstr "VLAN (ID)"
 
-#: netbox/ipam/filtersets.py:377 netbox/vpn/filtersets.py:422
+#: netbox/ipam/filtersets.py:379 netbox/vpn/filtersets.py:422
 msgid "VLAN number (1-4094)"
 msgstr "Номер VLAN (1-4094)"
 
-#: netbox/ipam/filtersets.py:471 netbox/ipam/filtersets.py:475
-#: netbox/ipam/filtersets.py:567 netbox/ipam/forms/model_forms.py:496
+#: netbox/ipam/filtersets.py:473 netbox/ipam/filtersets.py:477
+#: netbox/ipam/filtersets.py:569 netbox/ipam/forms/model_forms.py:496
 #: netbox/templates/tenancy/contact.html:53
 #: netbox/tenancy/forms/bulk_edit.py:113
 msgid "Address"
 msgstr "Адрес"
 
-#: netbox/ipam/filtersets.py:479
+#: netbox/ipam/filtersets.py:481
 msgid "Ranges which contain this prefix or IP"
 msgstr "Диапазоны, содержащие этот префикс или IP-адрес"
 
-#: netbox/ipam/filtersets.py:507 netbox/ipam/filtersets.py:563
+#: netbox/ipam/filtersets.py:509 netbox/ipam/filtersets.py:565
 msgid "Parent prefix"
 msgstr "Родительский префикс"
 
-#: netbox/ipam/filtersets.py:616 netbox/ipam/filtersets.py:856
-#: netbox/ipam/filtersets.py:1131 netbox/vpn/filtersets.py:385
+#: netbox/ipam/filtersets.py:618 netbox/ipam/filtersets.py:858
+#: netbox/ipam/filtersets.py:1133 netbox/vpn/filtersets.py:385
 msgid "Virtual machine (name)"
 msgstr "Виртуальная машина (имя)"
 
-#: netbox/ipam/filtersets.py:621 netbox/ipam/filtersets.py:861
-#: netbox/ipam/filtersets.py:1125 netbox/virtualization/filtersets.py:282
+#: netbox/ipam/filtersets.py:623 netbox/ipam/filtersets.py:863
+#: netbox/ipam/filtersets.py:1127 netbox/virtualization/filtersets.py:282
 #: netbox/virtualization/filtersets.py:321 netbox/vpn/filtersets.py:390
 msgid "Virtual machine (ID)"
 msgstr "Виртуальная машина (ID)"
 
-#: netbox/ipam/filtersets.py:627 netbox/vpn/filtersets.py:97
+#: netbox/ipam/filtersets.py:629 netbox/vpn/filtersets.py:97
 #: netbox/vpn/filtersets.py:396
 msgid "Interface (name)"
 msgstr "Интерфейс (имя)"
 
-#: netbox/ipam/filtersets.py:638 netbox/vpn/filtersets.py:108
+#: netbox/ipam/filtersets.py:640 netbox/vpn/filtersets.py:108
 #: netbox/vpn/filtersets.py:407
 msgid "VM interface (name)"
 msgstr "Интерфейс виртуальной машины (имя)"
 
-#: netbox/ipam/filtersets.py:643 netbox/vpn/filtersets.py:113
+#: netbox/ipam/filtersets.py:645 netbox/vpn/filtersets.py:113
 msgid "VM interface (ID)"
 msgstr "Интерфейс виртуальной машины (ID)"
 
-#: netbox/ipam/filtersets.py:648
+#: netbox/ipam/filtersets.py:650
 msgid "FHRP group (ID)"
 msgstr "FHRP группа (ID)"
 
-#: netbox/ipam/filtersets.py:652
+#: netbox/ipam/filtersets.py:654
 msgid "Is assigned to an interface"
 msgstr "Присвоен интерфейсу"
 
-#: netbox/ipam/filtersets.py:656
+#: netbox/ipam/filtersets.py:658
 msgid "Is assigned"
 msgstr "Назначено"
 
-#: netbox/ipam/filtersets.py:668
+#: netbox/ipam/filtersets.py:670
 msgid "Service (ID)"
 msgstr "Сервис (ID)"
 
-#: netbox/ipam/filtersets.py:673
+#: netbox/ipam/filtersets.py:675
 msgid "NAT inside IP address (ID)"
 msgstr "Внутренний NAT IP-адрес (ID)"
 
-#: netbox/ipam/filtersets.py:1041 netbox/ipam/forms/bulk_import.py:322
+#: netbox/ipam/filtersets.py:1043 netbox/ipam/forms/bulk_import.py:322
 msgid "Assigned interface"
 msgstr "Назначенный интерфейс"
 
-#: netbox/ipam/filtersets.py:1046
+#: netbox/ipam/filtersets.py:1048
 msgid "Assigned VM interface"
 msgstr "Назначенный интерфейс виртуальной машины"
 
-#: netbox/ipam/filtersets.py:1136
+#: netbox/ipam/filtersets.py:1138
 msgid "IP address (ID)"
 msgstr "IP-адрес (ID)"
 
-#: netbox/ipam/filtersets.py:1142 netbox/ipam/models/ip.py:788
+#: netbox/ipam/filtersets.py:1144 netbox/ipam/models/ip.py:788
 msgid "IP address"
 msgstr "IP-адрес"
 
-#: netbox/ipam/filtersets.py:1167
+#: netbox/ipam/filtersets.py:1169
 msgid "Primary IPv4 (ID)"
 msgstr "Основной IPv4 (ID)"
 
-#: netbox/ipam/filtersets.py:1172
+#: netbox/ipam/filtersets.py:1174
 msgid "Primary IPv6 (ID)"
 msgstr "Основной IPv6 (ID)"
 
@@ -9672,11 +9677,11 @@ msgstr "Сделайте этот IP-адрес основным для назн
 
 #: netbox/ipam/forms/bulk_import.py:330
 msgid "Is out-of-band"
-msgstr ""
+msgstr "Внеполосный IP-адрес"
 
 #: netbox/ipam/forms/bulk_import.py:331
 msgid "Designate this as the out-of-band IP address for the assigned device"
-msgstr ""
+msgstr "Назначьте это как внеполосный IP-адрес для указанного устройства"
 
 #: netbox/ipam/forms/bulk_import.py:371
 msgid "No device or virtual machine specified; cannot set as primary IP"
@@ -9686,11 +9691,11 @@ msgstr ""
 
 #: netbox/ipam/forms/bulk_import.py:375
 msgid "No device specified; cannot set as out-of-band IP"
-msgstr ""
+msgstr "Устройство не указано; невозможно установить как внеполосный IP-адрес"
 
 #: netbox/ipam/forms/bulk_import.py:379
 msgid "Cannot set out-of-band IP for virtual machines"
-msgstr ""
+msgstr "Невозможно установить внеполосный IP-адрес для виртуальных машин"
 
 #: netbox/ipam/forms/bulk_import.py:383
 msgid "No interface specified; cannot set as primary IP"
@@ -9699,7 +9704,7 @@ msgstr ""
 
 #: netbox/ipam/forms/bulk_import.py:387
 msgid "No interface specified; cannot set as out-of-band IP"
-msgstr ""
+msgstr "Интерфейс не указан; невозможно установить как внеполосный IP-адрес"
 
 #: netbox/ipam/forms/bulk_import.py:422
 msgid "Auth type"
@@ -9812,7 +9817,7 @@ msgstr "DNS-имя"
 #: netbox/ipam/views.py:971 netbox/netbox/navigation/menu.py:193
 #: netbox/netbox/navigation/menu.py:195
 msgid "VLANs"
-msgstr "VLAN"
+msgstr "VLAN"
 
 #: netbox/ipam/forms/filtersets.py:457
 msgid "Contains VLAN ID"
@@ -9876,7 +9881,7 @@ msgstr "Сделайте этот IP-адрес основным для устр
 
 #: netbox/ipam/forms/model_forms.py:314
 msgid "Make this the out-of-band IP for the device"
-msgstr ""
+msgstr "Назначить внеполосным IP-адресом устройства"
 
 #: netbox/ipam/forms/model_forms.py:329
 msgid "NAT IP (Inside)"
@@ -9889,10 +9894,13 @@ msgstr "IP-адрес можно присвоить только одному о
 #: netbox/ipam/forms/model_forms.py:398
 msgid "Cannot reassign primary IP address for the parent device/VM"
 msgstr ""
+"Невозможно переназначить основной IP-адрес родительского "
+"устройства/виртуальной машины"
 
 #: netbox/ipam/forms/model_forms.py:402
 msgid "Cannot reassign out-of-Band IP address for the parent device"
 msgstr ""
+"Невозможно переназначить внеполосный IP-адрес родительскому устройству"
 
 #: netbox/ipam/forms/model_forms.py:412
 msgid ""
@@ -9906,6 +9914,8 @@ msgid ""
 "Only IP addresses assigned to a device interface can be designated as the "
 "out-of-band IP for a device."
 msgstr ""
+"В качестве внеполосного IP-адреса устройства можно указать только IP-адреса,"
+" назначенные интерфейсу устройства."
 
 #: netbox/ipam/forms/model_forms.py:508
 msgid "Virtual IP Address"
@@ -10304,11 +10314,15 @@ msgstr "Невозможно установить scope_id без scope_type."
 #, python-brace-format
 msgid "Starting VLAN ID in range ({value}) cannot be less than {minimum}"
 msgstr ""
+"Начальный идентификатор VLAN в диапазоне ({value}) не может быть меньше "
+"{minimum}"
 
 #: netbox/ipam/models/vlans.py:111
 #, python-brace-format
 msgid "Ending VLAN ID in range ({value}) cannot exceed {maximum}"
 msgstr ""
+"Последний идентификатор VLAN в диапазоне ({value}) не может превышать "
+"{maximum}"
 
 #: netbox/ipam/models/vlans.py:118
 #, python-brace-format
@@ -10316,6 +10330,8 @@ msgid ""
 "Ending VLAN ID in range must be greater than or equal to the starting VLAN "
 "ID ({range})"
 msgstr ""
+"Последний идентификатор VLAN в диапазоне должен быть больше или равен "
+"начальному идентификатору VLAN ({range})"
 
 #: netbox/ipam/models/vlans.py:124
 msgid "Ranges cannot overlap."
@@ -11169,7 +11185,7 @@ msgstr "Сети провайдеров"
 
 #: netbox/netbox/navigation/menu.py:298
 msgid "Power Panels"
-msgstr "Панели питания"
+msgstr "Распределительный щиты"
 
 #: netbox/netbox/navigation/menu.py:309
 msgid "Configurations"
@@ -12109,7 +12125,7 @@ msgstr "Ширина юнита по умолчанию"
 
 #: netbox/templates/core/inc/config_data.html:20
 msgid "Power feeds"
-msgstr "Источники питания"
+msgstr "Кабели питания"
 
 #: netbox/templates/core/inc/config_data.html:23
 msgid "Default voltage"
@@ -12684,11 +12700,11 @@ msgstr "Скачать"
 #: netbox/templates/dcim/device/render_config.html:64
 #: netbox/templates/virtualization/virtualmachine/render_config.html:64
 msgid "Error rendering template"
-msgstr ""
+msgstr "Ошибка при отображении шаблона"
 
 #: netbox/templates/dcim/device/render_config.html:70
 msgid "No configuration template has been assigned for this device."
-msgstr ""
+msgstr "Для этого устройства не назначен шаблон конфигурации."
 
 #: netbox/templates/dcim/device_edit.html:44
 msgid "Parent Bay"
@@ -12842,7 +12858,7 @@ msgstr "Без тегов"
 
 #: netbox/templates/dcim/inc/interface_vlans_table.html:37
 msgid "No VLANs Assigned"
-msgstr "VLAN не назначены"
+msgstr "VLAN не назначены"
 
 #: netbox/templates/dcim/inc/interface_vlans_table.html:44
 #: netbox/templates/ipam/prefix_list.html:16
@@ -13013,7 +13029,7 @@ msgstr "Фаза электропитания"
 
 #: netbox/templates/dcim/powerpanel.html:72
 msgid "Add Power Feeds"
-msgstr "Добавить каналы питания"
+msgstr "Добавить кабели питания"
 
 #: netbox/templates/dcim/powerport.html:44
 msgid "Maximum Draw"
@@ -13555,7 +13571,7 @@ msgstr "Повторить"
 #: netbox/templates/extras/script_list.html:133
 #, python-format
 msgid "Could not load scripts from module %(module)s"
-msgstr ""
+msgstr "Не удалось загрузить скрипты из модуля %(module)s"
 
 #: netbox/templates/extras/script_list.html:141
 msgid "No Scripts Found"
@@ -14369,7 +14385,7 @@ msgstr "Добавить виртуальный диск"
 
 #: netbox/templates/virtualization/virtualmachine/render_config.html:70
 msgid "No configuration template has been assigned for this virtual machine."
-msgstr ""
+msgstr "Для этой виртуальной машины не назначен шаблон конфигурации."
 
 #: netbox/templates/vpn/ikepolicy.html:10
 #: netbox/templates/vpn/ipsecprofile.html:33 netbox/vpn/tables/crypto.py:166
@@ -15448,12 +15464,12 @@ msgstr "Память (МБ)"
 
 #: netbox/virtualization/forms/bulk_edit.py:174
 msgid "Disk (MB)"
-msgstr ""
+msgstr "Диск (МБ)"
 
 #: netbox/virtualization/forms/bulk_edit.py:334
 #: netbox/virtualization/forms/filtersets.py:251
 msgid "Size (MB)"
-msgstr ""
+msgstr "Размер (МБ)"
 
 #: netbox/virtualization/forms/bulk_import.py:44
 msgid "Type of cluster"
@@ -15664,19 +15680,19 @@ msgstr "GRE"
 
 #: netbox/vpn/choices.py:39
 msgid "WireGuard"
-msgstr ""
+msgstr "WireGuard"
 
 #: netbox/vpn/choices.py:40
 msgid "OpenVPN"
-msgstr ""
+msgstr "OpenVPN"
 
 #: netbox/vpn/choices.py:41
 msgid "L2TP"
-msgstr ""
+msgstr "L2TP"
 
 #: netbox/vpn/choices.py:42
 msgid "PPTP"
-msgstr ""
+msgstr "PPTP"
 
 #: netbox/vpn/choices.py:64
 msgid "Hub"

+ 4 - 2
netbox/vpn/api/serializers_/crypto.py

@@ -64,10 +64,12 @@ class IKEPolicySerializer(NetBoxModelSerializer):
 
 class IPSecProposalSerializer(NetBoxModelSerializer):
     encryption_algorithm = ChoiceField(
-        choices=EncryptionAlgorithmChoices
+        choices=EncryptionAlgorithmChoices,
+        required=False
     )
     authentication_algorithm = ChoiceField(
-        choices=AuthenticationAlgorithmChoices
+        choices=AuthenticationAlgorithmChoices,
+        required=False
     )
 
     class Meta:

+ 6 - 6
requirements.txt

@@ -11,28 +11,28 @@ django-redis==5.4.0
 django-rich==1.13.0
 django-rq==3.0
 django-taggit==6.1.0
-django-tables2==2.7.0
+django-tables2==2.7.5
 django-timezone-field==7.0
 djangorestframework==3.15.2
 drf-spectacular==0.28.0
 drf-spectacular-sidecar==2024.12.1
 feedparser==6.0.11
 gunicorn==23.0.0
-Jinja2==3.1.4
+Jinja2==3.1.5
 Markdown==3.7
 mkdocs-material==9.5.49
 mkdocstrings[python-legacy]==0.27.0
 netaddr==1.3.0
 nh3==0.2.20
-Pillow==11.0.0
+Pillow==11.1.0
 psycopg[c,pool]==3.2.3
 PyYAML==6.0.2
 requests==2.32.3
-rq==2.0
+rq==2.1.0
 social-auth-app-django==5.4.2
 social-auth-core==4.5.4
-strawberry-graphql==0.254.0
-strawberry-graphql-django==0.52.0
+strawberry-graphql==0.256.1
+strawberry-graphql-django==0.53.1
 svgwrite==1.4.3
 tablib==3.7.0
 tzdata==2024.2