mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Merge pull request #17346 from netbox-community/develop
Release v4.0.11
This commit is contained in:
commit
0b120e6ad2
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -26,7 +26,7 @@ body:
|
||||
attributes:
|
||||
label: NetBox Version
|
||||
description: What version of NetBox are you currently running?
|
||||
placeholder: v4.0.10
|
||||
placeholder: v4.0.11
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
@ -14,7 +14,7 @@ body:
|
||||
attributes:
|
||||
label: NetBox version
|
||||
description: What version of NetBox are you currently running?
|
||||
placeholder: v4.0.10
|
||||
placeholder: v4.0.11
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
@ -1,5 +1,15 @@
|
||||
# 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)
|
||||
|
||||
### Enhancements
|
||||
|
@ -96,6 +96,7 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'status': 'planned',
|
||||
}
|
||||
user_permissions = ('circuits.view_provider', 'circuits.view_circuittype')
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -150,6 +151,7 @@ class CircuitTest(APIViewTestCases.APIViewTestCase):
|
||||
class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
|
||||
model = CircuitTermination
|
||||
brief_fields = ['_occupied', 'cable', 'circuit', 'description', 'display', 'id', 'term_side', 'url']
|
||||
user_permissions = ('circuits.view_circuit', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -209,6 +211,7 @@ class CircuitTerminationTest(APIViewTestCases.APIViewTestCase):
|
||||
class ProviderAccountTest(APIViewTestCases.APIViewTestCase):
|
||||
model = ProviderAccount
|
||||
brief_fields = ['account', 'description', 'display', 'id', 'name', 'url']
|
||||
user_permissions = ('circuits.view_provider', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -252,6 +255,7 @@ class ProviderAccountTest(APIViewTestCases.APIViewTestCase):
|
||||
class ProviderNetworkTest(APIViewTestCases.APIViewTestCase):
|
||||
model = ProviderNetwork
|
||||
brief_fields = ['description', 'display', 'id', 'name', 'url']
|
||||
user_permissions = ('circuits.view_provider', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@ -57,6 +57,7 @@ class DataFileTest(
|
||||
):
|
||||
model = DataFile
|
||||
brief_fields = ['display', 'id', 'path', 'url']
|
||||
user_permissions = ('core.view_datasource', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@ -1031,7 +1031,7 @@ class VirtualDeviceContextTable(TenancyColumnsMixin, NetBoxTable):
|
||||
)
|
||||
device = tables.TemplateColumn(
|
||||
verbose_name=_('Device'),
|
||||
order_by=('_name',),
|
||||
order_by=('device___name',),
|
||||
template_code=DEVICE_LINK,
|
||||
linkify=True
|
||||
)
|
||||
|
@ -195,6 +195,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_site', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -280,6 +281,7 @@ class RackTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'status': 'planned',
|
||||
}
|
||||
user_permissions = ('dcim.view_site', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -368,6 +370,7 @@ class RackReservationTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_rack', 'users.view_user')
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -447,6 +450,7 @@ class DeviceTypeTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'part_number': 'ABC123',
|
||||
}
|
||||
user_permissions = ('dcim.view_manufacturer', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -492,6 +496,7 @@ class ModuleTypeTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'part_number': 'ABC123',
|
||||
}
|
||||
user_permissions = ('dcim.view_manufacturer', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -663,6 +668,7 @@ class PowerOutletTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_devicetype', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -768,6 +774,7 @@ class FrontPortTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_rearporttemplate', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -905,6 +912,7 @@ class ModuleBayTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_devicetype', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -945,6 +953,7 @@ class DeviceBayTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_devicetype', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -985,6 +994,7 @@ class InventoryItemTemplateTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_devicetype', 'dcim.view_manufacturer',)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1103,6 +1113,10 @@ class DeviceTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'status': 'failed',
|
||||
}
|
||||
user_permissions = (
|
||||
'dcim.view_site', 'dcim.view_rack', 'dcim.view_location', 'dcim.view_devicerole', 'dcim.view_devicetype',
|
||||
'extras.view_configtemplate',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1293,6 +1307,7 @@ class ModuleTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'serial': '1234ABCD',
|
||||
}
|
||||
user_permissions = ('dcim.view_modulebay', 'dcim.view_moduletype', 'dcim.view_device')
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1358,6 +1373,7 @@ class ConsolePortTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCa
|
||||
'description': 'New description',
|
||||
}
|
||||
peer_termination_type = ConsoleServerPort
|
||||
user_permissions = ('dcim.view_device', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1400,6 +1416,7 @@ class ConsoleServerPortTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIView
|
||||
'description': 'New description',
|
||||
}
|
||||
peer_termination_type = ConsolePort
|
||||
user_permissions = ('dcim.view_device', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1442,6 +1459,7 @@ class PowerPortTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
'description': 'New description',
|
||||
}
|
||||
peer_termination_type = PowerOutlet
|
||||
user_permissions = ('dcim.view_device', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1481,6 +1499,7 @@ class PowerOutletTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCa
|
||||
'description': 'New description',
|
||||
}
|
||||
peer_termination_type = PowerPort
|
||||
user_permissions = ('dcim.view_device', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1529,6 +1548,7 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
'description': 'New description',
|
||||
}
|
||||
peer_termination_type = Interface
|
||||
user_permissions = ('dcim.view_device', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1663,6 +1683,7 @@ class FrontPortTest(APIViewTestCases.APIViewTestCase):
|
||||
'description': 'New description',
|
||||
}
|
||||
peer_termination_type = Interface
|
||||
user_permissions = ('dcim.view_device', 'dcim.view_rearport')
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1721,6 +1742,7 @@ class RearPortTest(APIViewTestCases.APIViewTestCase):
|
||||
'description': 'New description',
|
||||
}
|
||||
peer_termination_type = Interface
|
||||
user_permissions = ('dcim.view_device', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1762,6 +1784,7 @@ class ModuleBayTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_device', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1801,6 +1824,7 @@ class DeviceBayTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_device', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -1864,6 +1888,7 @@ class InventoryItemTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'description': 'New description',
|
||||
}
|
||||
user_permissions = ('dcim.view_device', 'dcim.view_manufacturer')
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -2160,6 +2185,7 @@ class VirtualChassisTest(APIViewTestCases.APIViewTestCase):
|
||||
class PowerPanelTest(APIViewTestCases.APIViewTestCase):
|
||||
model = PowerPanel
|
||||
brief_fields = ['description', 'display', 'id', 'name', 'powerfeed_count', 'url']
|
||||
user_permissions = ('dcim.view_site', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -2212,6 +2238,7 @@ class PowerFeedTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'status': 'planned',
|
||||
}
|
||||
user_permissions = ('dcim.view_powerpanel', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@ -183,10 +183,13 @@ class ObjectCountsWidget(DashboardWidget):
|
||||
for model in get_models_from_content_types(self.config['models']):
|
||||
permission = get_permission_for_model(model, 'view')
|
||||
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')
|
||||
# Apply any specified filters
|
||||
if filters := self.config.get('filters'):
|
||||
if url and (filters := self.config.get('filters')):
|
||||
params = dict_to_querydict(filters)
|
||||
filterset = getattr(resolve(url).func.view_class, 'filterset', None)
|
||||
qs = filterset(params, qs).qs
|
||||
|
@ -34,7 +34,7 @@ class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
def _run_script():
|
||||
def _run_script(script):
|
||||
"""
|
||||
Core script execution task. We capture this within a subfunction to allow for conditionally wrapping it with
|
||||
the event_tracking context manager (which is bypassed if commit == False).
|
||||
@ -85,7 +85,6 @@ class Command(BaseCommand):
|
||||
|
||||
module_name, script_name = script.split('.', 1)
|
||||
module, script = get_module_and_script(module_name, script_name)
|
||||
script = script.python_class
|
||||
|
||||
# Take user from command line if provided and exists, other
|
||||
if options['user']:
|
||||
@ -102,7 +101,7 @@ class Command(BaseCommand):
|
||||
stdouthandler.setLevel(logging.DEBUG)
|
||||
stdouthandler.setFormatter(formatter)
|
||||
|
||||
logger = logging.getLogger(f"netbox.scripts.{script.full_name}")
|
||||
logger = logging.getLogger(f"netbox.scripts.{script.python_class.full_name}")
|
||||
logger.addHandler(stdouthandler)
|
||||
|
||||
try:
|
||||
@ -118,14 +117,14 @@ class Command(BaseCommand):
|
||||
raise CommandError(f"Invalid log level: {loglevel}")
|
||||
|
||||
# Initialize the script form
|
||||
script = script()
|
||||
form = script.as_form(data, None)
|
||||
script_instance = script.python_class()
|
||||
form = script_instance.as_form(data, None)
|
||||
|
||||
# Create the job
|
||||
job = Job.objects.create(
|
||||
object=module,
|
||||
name=script.class_name,
|
||||
user=User.objects.filter(is_superuser=True).order_by('pk')[0],
|
||||
object=script,
|
||||
name=script_instance.class_name,
|
||||
user=user,
|
||||
job_id=uuid.uuid4()
|
||||
)
|
||||
|
||||
@ -149,7 +148,7 @@ class Command(BaseCommand):
|
||||
# Execute the script. If commit is True, wrap it with the event_tracking context manager to ensure we process
|
||||
# change logging, webhooks, etc.
|
||||
with event_tracking(request):
|
||||
_run_script()
|
||||
_run_script(script_instance)
|
||||
else:
|
||||
logger.error('Data is not valid:')
|
||||
for field, errors in form.errors.get_json_data().items():
|
||||
|
@ -767,6 +767,7 @@ class FHRPGroupAssignmentTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'priority': 100,
|
||||
}
|
||||
user_permissions = ('ipam.view_fhrpgroup', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@ -36,6 +36,6 @@ schema = strawberry.Schema(
|
||||
query=Query,
|
||||
config=StrawberryConfig(auto_camel_case=False),
|
||||
extensions=[
|
||||
DjangoOptimizerExtension,
|
||||
DjangoOptimizerExtension(prefetch_custom_queryset=True),
|
||||
]
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ from utilities.string import trailing_slash
|
||||
# Environment setup
|
||||
#
|
||||
|
||||
VERSION = '4.0.10'
|
||||
VERSION = '4.0.11'
|
||||
HOSTNAME = platform.node()
|
||||
# Set the base directory two levels up
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
@ -6,6 +6,7 @@ from rest_framework import status
|
||||
|
||||
from core.models import ObjectType
|
||||
from dcim.models import Site, Location
|
||||
from ipam.models import ASN, RIR
|
||||
from users.models import ObjectPermission
|
||||
from utilities.testing import disable_warnings, APITestCase, TestCase
|
||||
|
||||
@ -45,7 +46,6 @@ class GraphQLTestCase(TestCase):
|
||||
class GraphQLAPITestCase(APITestCase):
|
||||
|
||||
@override_settings(LOGIN_REQUIRED=True)
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*', 'auth.user'])
|
||||
def test_graphql_filter_objects(self):
|
||||
"""
|
||||
Test the operation of filters for GraphQL API requests.
|
||||
@ -66,6 +66,7 @@ class GraphQLAPITestCase(APITestCase):
|
||||
obj_perm.save()
|
||||
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(Site))
|
||||
|
||||
# A valid request should return the filtered list
|
||||
url = reverse('graphql')
|
||||
@ -75,6 +76,7 @@ class GraphQLAPITestCase(APITestCase):
|
||||
data = json.loads(response.content)
|
||||
self.assertNotIn('errors', data)
|
||||
self.assertEqual(len(data['data']['location_list']), 1)
|
||||
self.assertIsNotNone(data['data']['location_list'][0]['site'])
|
||||
|
||||
# An invalid request should return an empty list
|
||||
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)
|
||||
data = json.loads(response.content)
|
||||
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)
|
||||
|
@ -986,14 +986,7 @@ brace-expansion@^2.0.1:
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
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:
|
||||
braces@^3.0.3, braces@~3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
|
||||
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
|
||||
@ -1564,7 +1557,7 @@ file-entry-cache@^6.0.1:
|
||||
dependencies:
|
||||
flat-cache "^3.0.4"
|
||||
|
||||
fill-range@^7.0.1, fill-range@^7.1.1:
|
||||
fill-range@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
|
||||
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
|
||||
@ -2174,11 +2167,11 @@ meros@^1.1.4:
|
||||
integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==
|
||||
|
||||
micromatch@^4.0.4:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
|
||||
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
|
||||
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
|
||||
dependencies:
|
||||
braces "^3.0.2"
|
||||
braces "^3.0.3"
|
||||
picomatch "^2.3.1"
|
||||
|
||||
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
|
@ -3,7 +3,7 @@
|
||||
{% if counts %}
|
||||
<div class="list-group list-group-flush">
|
||||
{% 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">
|
||||
{{ model|meta:"verbose_name_plural"|bettertitle }}
|
||||
{% if count is None %}
|
||||
|
@ -210,6 +210,7 @@ class ContactAssignmentTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'priority': ContactPriorityChoices.PRIORITY_INACTIVE,
|
||||
}
|
||||
user_permissions = ('tenancy.view_contact', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
BIN
netbox/translations/cs/LC_MESSAGES/django.mo
Normal file
BIN
netbox/translations/cs/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
netbox/translations/da/LC_MESSAGES/django.mo
Normal file
BIN
netbox/translations/da/LC_MESSAGES/django.mo
Normal file
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
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
BIN
netbox/translations/it/LC_MESSAGES/django.mo
Normal file
BIN
netbox/translations/it/LC_MESSAGES/django.mo
Normal file
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
BIN
netbox/translations/nl/LC_MESSAGES/django.mo
Normal file
BIN
netbox/translations/nl/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
netbox/translations/pl/LC_MESSAGES/django.mo
Normal file
BIN
netbox/translations/pl/LC_MESSAGES/django.mo
Normal file
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
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
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
@ -253,6 +253,7 @@ class VMInterfaceTest(APIViewTestCases.APIViewTestCase):
|
||||
'description': 'New description',
|
||||
}
|
||||
graphql_base_name = 'vm_interface'
|
||||
user_permissions = ('virtualization.view_virtualmachine', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -342,6 +343,7 @@ class VirtualDiskTest(APIViewTestCases.APIViewTestCase):
|
||||
'size': 888,
|
||||
}
|
||||
graphql_base_name = 'virtual_disk'
|
||||
user_permissions = ('virtualization.view_virtualmachine', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@ -116,6 +116,7 @@ class TunnelTerminationTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'role': TunnelTerminationRoleChoices.ROLE_PEER,
|
||||
}
|
||||
user_permissions = ('vpn.view_tunnel', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -430,6 +431,7 @@ class IPSecPolicyTest(APIViewTestCases.APIViewTestCase):
|
||||
class IPSecProfileTest(APIViewTestCases.APIViewTestCase):
|
||||
model = IPSecProfile
|
||||
brief_fields = ['description', 'display', 'id', 'name', 'url']
|
||||
user_permissions = ('vpn.view_ikepolicy', 'vpn.view_ipsecpolicy')
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -558,6 +560,7 @@ class L2VPNTest(APIViewTestCases.APIViewTestCase):
|
||||
class L2VPNTerminationTest(APIViewTestCases.APIViewTestCase):
|
||||
model = L2VPNTermination
|
||||
brief_fields = ['display', 'id', 'l2vpn', 'url']
|
||||
user_permissions = ('dcim.view_location', 'vpn.view_l2vpn')
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@ -114,6 +114,7 @@ class WirelessLinkTest(APIViewTestCases.APIViewTestCase):
|
||||
bulk_update_data = {
|
||||
'status': 'planned',
|
||||
}
|
||||
user_permissions = ('dcim.view_interface', )
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
@ -1,4 +1,4 @@
|
||||
Django==5.0.8
|
||||
Django==5.0.9
|
||||
django-cors-headers==4.4.0
|
||||
django-debug-toolbar==4.4.6
|
||||
django-filter==24.2
|
||||
@ -8,7 +8,7 @@ django-mptt==0.16.0
|
||||
django-pglocks==1.0.4
|
||||
django-prometheus==2.3.1
|
||||
django-redis==5.4.0
|
||||
django-rich==1.10.0
|
||||
django-rich==1.11.0
|
||||
django-rq==2.10.2
|
||||
django-taggit==5.0.1
|
||||
django-tables2==2.7.0
|
||||
@ -20,8 +20,8 @@ feedparser==6.0.11
|
||||
gunicorn==23.0.0
|
||||
Jinja2==3.1.4
|
||||
Markdown==3.7
|
||||
mkdocs-material==9.5.33
|
||||
mkdocstrings[python-legacy]==0.25.2
|
||||
mkdocs-material==9.5.34
|
||||
mkdocstrings[python-legacy]==0.26.0
|
||||
netaddr==1.3.0
|
||||
nh3==0.2.18
|
||||
Pillow==10.4.0
|
||||
@ -30,7 +30,7 @@ PyYAML==6.0.2
|
||||
requests==2.32.3
|
||||
social-auth-app-django==5.4.2
|
||||
social-auth-core==4.5.4
|
||||
strawberry-graphql==0.237.2
|
||||
strawberry-graphql==0.239.2
|
||||
strawberry-graphql-django==0.47.1
|
||||
svgwrite==1.4.3
|
||||
tablib==3.6.1
|
||||
|
Loading…
Reference in New Issue
Block a user