Merge branch 'develop' into feature

This commit is contained in:
Jeremy Stretch 2024-09-03 11:59:59 -04:00
commit a777850702
47 changed files with 4929 additions and 4783 deletions

View File

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

View File

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

View File

@ -1,5 +1,15 @@
# NetBox v4.0 # NetBox v4.0
## v4.0.11 (2024-09-03)
### Bug Fixes
* [#17310](https://github.com/netbox-community/netbox/issues/17310) - Enforce restricted queryset for related objects in GraphQL API requests
* [#17321](https://github.com/netbox-community/netbox/issues/17321) - Ensure the job is attributed to the specified user when using the `runscript` management command
* [#17323](https://github.com/netbox-community/netbox/issues/17323) - Associate job with script object when executed using the `runscript` management command
* [#17337](https://github.com/netbox-community/netbox/issues/17337) - Fix ordering of virtual device contexts by device name
* [#17341](https://github.com/netbox-community/netbox/issues/17341) - Avoid `NoReverseMatch` exceptions with specific dashboard widget configurations
## v4.0.10 (2024-08-29) ## v4.0.10 (2024-08-29)
### Enhancements ### Enhancements

View File

@ -96,6 +96,7 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'status': 'planned', 'status': 'planned',
} }
user_permissions = ('circuits.view_provider', 'circuits.view_circuittype')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -150,6 +151,7 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
class CircuitTerminationTest(APIViewTestCases.APIViewTestCase): class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
model = CircuitTermination model = CircuitTermination
brief_fields = ['_occupied', 'cable', 'circuit', 'description', 'display', 'id', 'term_side', 'url'] brief_fields = ['_occupied', 'cable', 'circuit', 'description', 'display', 'id', 'term_side', 'url']
user_permissions = ('circuits.view_circuit', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -241,6 +243,7 @@ class CircuitGroupTest(APIViewTestCases.APIViewTestCase):
class ProviderAccountTest(APIViewTestCases.APIViewTestCase): class ProviderAccountTest(APIViewTestCases.APIViewTestCase):
model = ProviderAccount model = ProviderAccount
brief_fields = ['account', 'description', 'display', 'id', 'name', 'url'] brief_fields = ['account', 'description', 'display', 'id', 'name', 'url']
user_permissions = ('circuits.view_provider',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -287,6 +290,7 @@ class CircuitGroupAssignmentTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'priority': CircuitPriorityChoices.PRIORITY_INACTIVE, 'priority': CircuitPriorityChoices.PRIORITY_INACTIVE,
} }
user_permissions = ('circuits.view_circuit', 'circuits.view_circuitgroup')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -355,6 +359,7 @@ class CircuitGroupAssignmentTest(APIViewTestCases.APIViewTestCase):
class ProviderNetworkTest(APIViewTestCases.APIViewTestCase): class ProviderNetworkTest(APIViewTestCases.APIViewTestCase):
model = ProviderNetwork model = ProviderNetwork
brief_fields = ['description', 'display', 'id', 'name', 'url'] brief_fields = ['description', 'display', 'id', 'name', 'url']
user_permissions = ('circuits.view_provider', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -57,6 +57,7 @@ class DataFileTest(
): ):
model = DataFile model = DataFile
brief_fields = ['display', 'id', 'path', 'url'] brief_fields = ['display', 'id', 'path', 'url']
user_permissions = ('core.view_datasource', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -1051,7 +1051,7 @@ class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable):
) )
device = tables.TemplateColumn( device = tables.TemplateColumn(
verbose_name=_('Device'), verbose_name=_('Device'),
order_by=('_name',), order_by=('device___name',),
template_code=DEVICE_LINK, template_code=DEVICE_LINK,
linkify=True linkify=True
) )

View File

@ -192,6 +192,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_site',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -277,6 +278,7 @@ class RackTypeTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'new description', 'description': 'new description',
} }
user_permissions = ('dcim.view_manufacturer',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -321,6 +323,7 @@ class RackTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'status': 'planned', 'status': 'planned',
} }
user_permissions = ('dcim.view_site', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -409,6 +412,7 @@ class RackReservationTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_rack', 'users.view_user')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -488,6 +492,7 @@ class DeviceTypeTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'part_number': 'ABC123', 'part_number': 'ABC123',
} }
user_permissions = ('dcim.view_manufacturer', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -533,6 +538,7 @@ class ModuleTypeTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'part_number': 'ABC123', 'part_number': 'ABC123',
} }
user_permissions = ('dcim.view_manufacturer', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -704,6 +710,7 @@ class PowerOutletTemplateTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_devicetype', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -809,6 +816,7 @@ class FrontPortTemplateTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_rearporttemplate', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -946,6 +954,7 @@ class ModuleBayTemplateTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_devicetype', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -986,6 +995,7 @@ class DeviceBayTemplateTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_devicetype', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1026,6 +1036,7 @@ class InventoryItemTemplateTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_devicetype', 'dcim.view_manufacturer',)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1144,6 +1155,10 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'status': 'failed', 'status': 'failed',
} }
user_permissions = (
'dcim.view_site', 'dcim.view_rack', 'dcim.view_location', 'dcim.view_devicerole', 'dcim.view_devicetype',
'extras.view_configtemplate',
)
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1334,6 +1349,7 @@ class ModuleTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'serial': '1234ABCD', 'serial': '1234ABCD',
} }
user_permissions = ('dcim.view_modulebay', 'dcim.view_moduletype', 'dcim.view_device')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1400,6 +1416,7 @@ class ConsolePortTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCa
'description': 'New description', 'description': 'New description',
} }
peer_termination_type = ConsoleServerPort peer_termination_type = ConsoleServerPort
user_permissions = ('dcim.view_device', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1442,6 +1459,7 @@ class ConsoleServerPortTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIView
'description': 'New description', 'description': 'New description',
} }
peer_termination_type = ConsolePort peer_termination_type = ConsolePort
user_permissions = ('dcim.view_device', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1484,6 +1502,7 @@ class PowerPortTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
'description': 'New description', 'description': 'New description',
} }
peer_termination_type = PowerOutlet peer_termination_type = PowerOutlet
user_permissions = ('dcim.view_device', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1523,6 +1542,7 @@ class PowerOutletTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCa
'description': 'New description', 'description': 'New description',
} }
peer_termination_type = PowerPort peer_termination_type = PowerPort
user_permissions = ('dcim.view_device', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1571,6 +1591,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
'description': 'New description', 'description': 'New description',
} }
peer_termination_type = Interface peer_termination_type = Interface
user_permissions = ('dcim.view_device', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1705,6 +1726,7 @@ class FrontPortTest(APIViewTestCases.APIViewTestCase):
'description': 'New description', 'description': 'New description',
} }
peer_termination_type = Interface peer_termination_type = Interface
user_permissions = ('dcim.view_device', 'dcim.view_rearport')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1763,6 +1785,7 @@ class RearPortTest(APIViewTestCases.APIViewTestCase):
'description': 'New description', 'description': 'New description',
} }
peer_termination_type = Interface peer_termination_type = Interface
user_permissions = ('dcim.view_device', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1804,6 +1827,7 @@ class ModuleBayTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_device', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1844,6 +1868,7 @@ class DeviceBayTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_device', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -1907,6 +1932,7 @@ class InventoryItemTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'description': 'New description', 'description': 'New description',
} }
user_permissions = ('dcim.view_device', 'dcim.view_manufacturer')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -2203,6 +2229,7 @@ class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
class PowerPanelTest(APIViewTestCases.APIViewTestCase): class PowerPanelTest(APIViewTestCases.APIViewTestCase):
model = PowerPanel model = PowerPanel
brief_fields = ['description', 'display', 'id', 'name', 'powerfeed_count', 'url'] brief_fields = ['description', 'display', 'id', 'name', 'powerfeed_count', 'url']
user_permissions = ('dcim.view_site', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -2255,6 +2282,7 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'status': 'planned', 'status': 'planned',
} }
user_permissions = ('dcim.view_powerpanel', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -183,10 +183,13 @@ class ObjectCountsWidget(DashboardWidget):
for model in get_models_from_content_types(self.config['models']): for model in get_models_from_content_types(self.config['models']):
permission = get_permission_for_model(model, 'view') permission = get_permission_for_model(model, 'view')
if request.user.has_perm(permission): if request.user.has_perm(permission):
url = reverse(get_viewname(model, 'list')) try:
url = reverse(get_viewname(model, 'list'))
except NoReverseMatch:
url = None
qs = model.objects.restrict(request.user, 'view') qs = model.objects.restrict(request.user, 'view')
# Apply any specified filters # Apply any specified filters
if filters := self.config.get('filters'): if url and (filters := self.config.get('filters')):
params = dict_to_querydict(filters) params = dict_to_querydict(filters)
filterset = getattr(resolve(url).func.view_class, 'filterset', None) filterset = getattr(resolve(url).func.view_class, 'filterset', None)
qs = filterset(params, qs).qs qs = filterset(params, qs).qs

View File

@ -768,6 +768,7 @@ class FHRPGroupAssignmentTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'priority': 100, 'priority': 100,
} }
user_permissions = ('ipam.view_fhrpgroup', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -38,7 +38,7 @@ schema = strawberry.Schema(
query=Query, query=Query,
config=StrawberryConfig(auto_camel_case=False), config=StrawberryConfig(auto_camel_case=False),
extensions=[ extensions=[
DjangoOptimizerExtension, DjangoOptimizerExtension(prefetch_custom_queryset=True),
MaxAliasesLimiter(max_alias_count=settings.GRAPHQL_MAX_ALIASES), MaxAliasesLimiter(max_alias_count=settings.GRAPHQL_MAX_ALIASES),
] ]
) )

View File

@ -6,6 +6,7 @@ from rest_framework import status
from core.models import ObjectType from core.models import ObjectType
from dcim.models import Site, Location from dcim.models import Site, Location
from ipam.models import ASN, RIR
from users.models import ObjectPermission from users.models import ObjectPermission
from utilities.testing import disable_warnings, APITestCase, TestCase from utilities.testing import disable_warnings, APITestCase, TestCase
@ -45,7 +46,6 @@ class GraphQLTestCase(TestCase):
class GraphQLAPITestCase(APITestCase): class GraphQLAPITestCase(APITestCase):
@override_settings(LOGIN_REQUIRED=True) @override_settings(LOGIN_REQUIRED=True)
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*', 'auth.user'])
def test_graphql_filter_objects(self): def test_graphql_filter_objects(self):
""" """
Test the operation of filters for GraphQL API requests. Test the operation of filters for GraphQL API requests.
@ -66,6 +66,7 @@ class GraphQLAPITestCase(APITestCase):
obj_perm.save() obj_perm.save()
obj_perm.users.add(self.user) obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(Location)) obj_perm.object_types.add(ObjectType.objects.get_for_model(Location))
obj_perm.object_types.add(ObjectType.objects.get_for_model(Site))
# A valid request should return the filtered list # A valid request should return the filtered list
url = reverse('graphql') url = reverse('graphql')
@ -75,6 +76,7 @@ class GraphQLAPITestCase(APITestCase):
data = json.loads(response.content) data = json.loads(response.content)
self.assertNotIn('errors', data) self.assertNotIn('errors', data)
self.assertEqual(len(data['data']['location_list']), 1) self.assertEqual(len(data['data']['location_list']), 1)
self.assertIsNotNone(data['data']['location_list'][0]['site'])
# An invalid request should return an empty list # An invalid request should return an empty list
query = '{location_list(filters: {site_id: "99999"}) {id site {id}}}' # Invalid site ID query = '{location_list(filters: {site_id: "99999"}) {id site {id}}}' # Invalid site ID
@ -82,3 +84,12 @@ class GraphQLAPITestCase(APITestCase):
self.assertHttpStatus(response, status.HTTP_200_OK) self.assertHttpStatus(response, status.HTTP_200_OK)
data = json.loads(response.content) data = json.loads(response.content)
self.assertEqual(len(data['data']['location_list']), 0) self.assertEqual(len(data['data']['location_list']), 0)
# Removing the permissions from location should result in an empty locations list
obj_perm.object_types.remove(ObjectType.objects.get_for_model(Location))
query = '{site(id: ' + str(sites[0].pk) + ') {id locations {id}}}'
response = self.client.post(url, data={'query': query}, format="json", **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
data = json.loads(response.content)
self.assertNotIn('errors', data)
self.assertEqual(len(data['data']['site']['locations']), 0)

View File

@ -992,14 +992,7 @@ brace-expansion@^2.0.1:
dependencies: dependencies:
balanced-match "^1.0.0" balanced-match "^1.0.0"
braces@^3.0.2: braces@^3.0.3, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
braces@~3.0.2:
version "3.0.3" version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
@ -1575,7 +1568,7 @@ file-entry-cache@^6.0.1:
dependencies: dependencies:
flat-cache "^3.0.4" flat-cache "^3.0.4"
fill-range@^7.0.1, fill-range@^7.1.1: fill-range@^7.1.1:
version "7.1.1" version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
@ -2190,11 +2183,11 @@ meros@^1.1.4:
integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==
micromatch@^4.0.4: micromatch@^4.0.4:
version "4.0.5" version "4.0.8"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies: dependencies:
braces "^3.0.2" braces "^3.0.3"
picomatch "^2.3.1" picomatch "^2.3.1"
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:

View File

@ -3,7 +3,7 @@
{% if counts %} {% if counts %}
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
{% for model, count, url in counts %} {% for model, count, url in counts %}
<a href="{{ url }}" class="list-group-item list-group-item-action px-1 py-2"> <a {% if url %}href="{{ url }}" {% endif %}class="list-group-item list-group-item-action px-1 py-2">
<div class="d-flex w-100 justify-content-between align-items-center"> <div class="d-flex w-100 justify-content-between align-items-center">
{{ model|meta:"verbose_name_plural"|bettertitle }} {{ model|meta:"verbose_name_plural"|bettertitle }}
{% if count is None %} {% if count is None %}

View File

@ -210,6 +210,7 @@ class ContactAssignmentTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'priority': ContactPriorityChoices.PRIORITY_INACTIVE, 'priority': ContactPriorityChoices.PRIORITY_INACTIVE,
} }
user_permissions = ('tenancy.view_contact', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -253,6 +253,7 @@ class VMInterfaceTest(APIViewTestCases.APIViewTestCase):
'description': 'New description', 'description': 'New description',
} }
graphql_base_name = 'vm_interface' graphql_base_name = 'vm_interface'
user_permissions = ('virtualization.view_virtualmachine', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -342,6 +343,7 @@ class VirtualDiskTest(APIViewTestCases.APIViewTestCase):
'size': 888, 'size': 888,
} }
graphql_base_name = 'virtual_disk' graphql_base_name = 'virtual_disk'
user_permissions = ('virtualization.view_virtualmachine', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -116,6 +116,7 @@ class TunnelTerminationTest(APIViewTestCases.APIViewTestCase):
bulk_update_data = { bulk_update_data = {
'role': TunnelTerminationRoleChoices.ROLE_PEER, 'role': TunnelTerminationRoleChoices.ROLE_PEER,
} }
user_permissions = ('vpn.view_tunnel', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -430,6 +431,7 @@ class IPSecPolicyTest(APIViewTestCases.APIViewTestCase):
class IPSecProfileTest(APIViewTestCases.APIViewTestCase): class IPSecProfileTest(APIViewTestCases.APIViewTestCase):
model = IPSecProfile model = IPSecProfile
brief_fields = ['description', 'display', 'id', 'name', 'url'] brief_fields = ['description', 'display', 'id', 'name', 'url']
user_permissions = ('vpn.view_ikepolicy', 'vpn.view_ipsecpolicy')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
@ -558,6 +560,7 @@ class L2VPNTest(APIViewTestCases.APIViewTestCase):
class L2VPNTerminationTest(APIViewTestCases.APIViewTestCase): class L2VPNTerminationTest(APIViewTestCases.APIViewTestCase):
model = L2VPNTermination model = L2VPNTermination
brief_fields = ['display', 'id', 'l2vpn', 'url'] brief_fields = ['display', 'id', 'l2vpn', 'url']
user_permissions = ('dcim.view_location', 'vpn.view_l2vpn')
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -116,6 +116,7 @@ class WirelessLinkTest(APIViewTestCases.APIViewTestCase):
'distance': 100, 'distance': 100,
'distance_unit': 'm', 'distance_unit': 'm',
} }
user_permissions = ('dcim.view_interface', )
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):

View File

@ -1,4 +1,4 @@
Django==5.0.8 Django==5.0.9
django-cors-headers==4.4.0 django-cors-headers==4.4.0
django-debug-toolbar==4.4.6 django-debug-toolbar==4.4.6
django-filter==24.3 django-filter==24.3
@ -8,7 +8,7 @@ django-mptt==0.16.0
django-pglocks==1.0.4 django-pglocks==1.0.4
django-prometheus==2.3.1 django-prometheus==2.3.1
django-redis==5.4.0 django-redis==5.4.0
django-rich==1.10.0 django-rich==1.11.0
django-rq==2.10.2 django-rq==2.10.2
django-taggit==6.0.0 django-taggit==6.0.0
django-tables2==2.7.0 django-tables2==2.7.0
@ -20,8 +20,8 @@ feedparser==6.0.11
gunicorn==23.0.0 gunicorn==23.0.0
Jinja2==3.1.4 Jinja2==3.1.4
Markdown==3.7 Markdown==3.7
mkdocs-material==9.5.33 mkdocs-material==9.5.34
mkdocstrings[python-legacy]==0.25.2 mkdocstrings[python-legacy]==0.26.0
netaddr==1.3.0 netaddr==1.3.0
nh3==0.2.18 nh3==0.2.18
Pillow==10.4.0 Pillow==10.4.0
@ -30,7 +30,7 @@ PyYAML==6.0.2
requests==2.32.3 requests==2.32.3
social-auth-app-django==5.4.2 social-auth-app-django==5.4.2
social-auth-core==4.5.4 social-auth-core==4.5.4
strawberry-graphql==0.237.3 strawberry-graphql==0.239.2
strawberry-graphql-django==0.47.1 strawberry-graphql-django==0.47.1
svgwrite==1.4.3 svgwrite==1.4.3
tablib==3.6.1 tablib==3.6.1