mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
commit
6ea0c0c3e9
@ -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.1.2
|
placeholder: v4.1.3
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
2
.github/ISSUE_TEMPLATE/02-bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/02-bug_report.yaml
vendored
@ -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.1.2
|
placeholder: v4.1.3
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
|
11
README.md
11
README.md
@ -7,7 +7,11 @@
|
|||||||
<a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a>
|
<a href="https://github.com/netbox-community/netbox/stargazers"><img src="https://img.shields.io/github/stars/netbox-community/netbox?style=flat" alt="GitHub stars" /></a>
|
||||||
<a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-15-blue" alt="Languages supported" /></a>
|
<a href="https://explore.transifex.com/netbox-community/netbox/"><img src="https://img.shields.io/badge/languages-15-blue" alt="Languages supported" /></a>
|
||||||
<a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/workflows/CI/badge.svg?branch=master" alt="CI status" /></a>
|
<a href="https://github.com/netbox-community/netbox/actions/workflows/ci.yml"><img src="https://github.com/netbox-community/netbox/workflows/CI/badge.svg?branch=master" alt="CI status" /></a>
|
||||||
<p></p>
|
<p>
|
||||||
|
<strong><a href="https://github.com/netbox-community/netbox/">NetBox Community</a></strong> |
|
||||||
|
<strong><a href="https://netboxlabs.com/netbox-cloud/">NetBox Cloud</a></strong> |
|
||||||
|
<strong><a href="https://netboxlabs.com/netbox-enterprise/">NetBox Enterprise</a></strong>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
NetBox exists to empower network engineers. Since its release in 2016, it has become the go-to solution for modeling and documenting network infrastructure for thousands of organizations worldwide. As a successor to legacy IPAM and DCIM applications, NetBox provides a cohesive, extensive, and accessible data model for all things networked. By providing a single robust user interface and programmable APIs for everything from cable maps to device configurations, NetBox serves as the central source of truth for the modern network.
|
NetBox exists to empower network engineers. Since its release in 2016, it has become the go-to solution for modeling and documenting network infrastructure for thousands of organizations worldwide. As a successor to legacy IPAM and DCIM applications, NetBox provides a cohesive, extensive, and accessible data model for all things networked. By providing a single robust user interface and programmable APIs for everything from cable maps to device configurations, NetBox serves as the central source of truth for the modern network.
|
||||||
@ -81,11 +85,6 @@ NetBox automatically logs the creation, modification, and deletion of all manage
|
|||||||
* The [official documentation](https://docs.netbox.dev) offers a comprehensive introduction.
|
* The [official documentation](https://docs.netbox.dev) offers a comprehensive introduction.
|
||||||
* Check out [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions) for even more projects to get the most out of NetBox!
|
* Check out [our wiki](https://github.com/netbox-community/netbox/wiki/Community-Contributions) for even more projects to get the most out of NetBox!
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="https://netboxlabs.com/netbox-cloud/"><img src="docs/media/misc/netbox_cloud.png" alt="NetBox Cloud" /></a><br />
|
|
||||||
Looking for a managed solution? Check out <strong><a href="https://netboxlabs.com/netbox-cloud/">NetBox Cloud</a></strong> or <strong><a href="https://netboxlabs.com/netbox-enterprise/">NetBox Enterprise</a></strong>!
|
|
||||||
</p>
|
|
||||||
|
|
||||||
## Get Involved
|
## Get Involved
|
||||||
|
|
||||||
* Follow [@NetBoxOfficial](https://twitter.com/NetBoxOfficial) on Twitter!
|
* Follow [@NetBoxOfficial](https://twitter.com/NetBoxOfficial) on Twitter!
|
||||||
|
@ -13,6 +13,9 @@ To enable remote data synchronization, the NetBox administrator first designates
|
|||||||
!!! info
|
!!! info
|
||||||
Data backends which connect to external sources typically require the installation of one or more supporting Python libraries. The Git backend requires the [`dulwich`](https://www.dulwich.io/) package, and the S3 backend requires the [`boto3`](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html) package. These must be installed within NetBox's environment to enable these backends.
|
Data backends which connect to external sources typically require the installation of one or more supporting Python libraries. The Git backend requires the [`dulwich`](https://www.dulwich.io/) package, and the S3 backend requires the [`boto3`](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html) package. These must be installed within NetBox's environment to enable these backends.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
If you are configuring Git and have `HTTP_PROXIES` configured to use the SOCKS protocol, you will also need to install the [`python_socks`](https://pypi.org/project/python-socks/) Python library.
|
||||||
|
|
||||||
Each type of remote source has its own configuration parameters. For instance, a git source will ask the user to specify a branch and authentication credentials. Once the source has been created, a synchronization job is run to automatically replicate remote files in the local database.
|
Each type of remote source has its own configuration parameters. For instance, a git source will ask the user to specify a branch and authentication credentials. Once the source has been created, a synchronization job is run to automatically replicate remote files in the local database.
|
||||||
|
|
||||||
The following NetBox models can be associated with replicated data files:
|
The following NetBox models can be associated with replicated data files:
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB |
@ -1,6 +1,6 @@
|
|||||||
# ASNs
|
# ASNs
|
||||||
|
|
||||||
An Autonomous System Number (ASN) is a numeric identifier used in the BGP protocol to identify which [autonomous system](https://en.wikipedia.org/wiki/Autonomous_system_%28Internet%29) a particular prefix is originating and transiting through. NetBox support both 32- and 64- ASNs.
|
An Autonomous System Number (ASN) is a numeric identifier used in the Border Gateway Protocol (BGP) to identify which [autonomous system](https://en.wikipedia.org/wiki/Autonomous_system_%28Internet%29) a particular prefix is originating from or transiting through. NetBox supports both 16- and 32-bit ASNs.
|
||||||
|
|
||||||
ASNs must be globally unique within NetBox, and may be allocated from within a [defined range](./asnrange.md). Each ASN may be assigned to multiple [sites](../dcim/site.md).
|
ASNs must be globally unique within NetBox, and may be allocated from within a [defined range](./asnrange.md). Each ASN may be assigned to multiple [sites](../dcim/site.md).
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ ASNs must be globally unique within NetBox, and may be allocated from within a [
|
|||||||
|
|
||||||
### AS Number
|
### AS Number
|
||||||
|
|
||||||
The 32- or 64-bit AS number.
|
The 16- or 32-bit AS number.
|
||||||
|
|
||||||
### RIR
|
### RIR
|
||||||
|
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
# NetBox v4.1
|
# NetBox v4.1
|
||||||
|
|
||||||
|
## v4.1.3 (2024-10-02)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* [#17639](https://github.com/netbox-community/netbox/issues/17639) - Add SOCKS support to proxy settings for Git remote data sources
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#17558](https://github.com/netbox-community/netbox/issues/17558) - Raise validation error when attempting to remove a custom field choice in use
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v4.1.2 (2024-09-26)
|
## v4.1.2 (2024-09-26)
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
|
@ -8,10 +8,13 @@ from urllib.parse import urlparse
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from netbox.data_backends import DataBackend
|
from netbox.data_backends import DataBackend
|
||||||
from netbox.utils import register_data_backend
|
from netbox.utils import register_data_backend
|
||||||
|
from utilities.constants import HTTP_PROXY_SUPPORTED_SCHEMAS, HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS
|
||||||
|
from utilities.socks import ProxyPoolManager
|
||||||
from .exceptions import SyncError
|
from .exceptions import SyncError
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -67,11 +70,18 @@ class GitBackend(DataBackend):
|
|||||||
|
|
||||||
# Initialize backend config
|
# Initialize backend config
|
||||||
config = ConfigDict()
|
config = ConfigDict()
|
||||||
|
self.use_socks = False
|
||||||
|
|
||||||
# Apply HTTP proxy (if configured)
|
# Apply HTTP proxy (if configured)
|
||||||
if settings.HTTP_PROXIES and self.url_scheme in ('http', 'https'):
|
if settings.HTTP_PROXIES:
|
||||||
if proxy := settings.HTTP_PROXIES.get(self.url_scheme):
|
if proxy := settings.HTTP_PROXIES.get(self.url_scheme, None):
|
||||||
config.set("http", "proxy", proxy)
|
if urlparse(proxy).scheme not in HTTP_PROXY_SUPPORTED_SCHEMAS:
|
||||||
|
raise ImproperlyConfigured(f"Unsupported Git DataSource proxy scheme: {urlparse(proxy).scheme}")
|
||||||
|
|
||||||
|
if self.url_scheme in ('http', 'https'):
|
||||||
|
config.set("http", "proxy", proxy)
|
||||||
|
if urlparse(proxy).scheme in HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS:
|
||||||
|
self.use_socks = True
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@ -87,6 +97,10 @@ class GitBackend(DataBackend):
|
|||||||
"errstream": porcelain.NoneStream(),
|
"errstream": porcelain.NoneStream(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# check if using socks for proxy - if so need to use custom pool_manager
|
||||||
|
if self.use_socks:
|
||||||
|
clone_args['pool_manager'] = ProxyPoolManager(settings.HTTP_PROXIES.get(self.url_scheme))
|
||||||
|
|
||||||
if self.url_scheme in ('http', 'https'):
|
if self.url_scheme in ('http', 'https'):
|
||||||
if self.params.get('username'):
|
if self.params.get('username'):
|
||||||
clone_args.update(
|
clone_args.update(
|
||||||
|
@ -785,6 +785,12 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Cache the initial set of choices for comparison under clean()
|
||||||
|
self._original_extra_choices = self.__dict__.get('extra_choices')
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('extras:customfieldchoiceset', args=[self.pk])
|
return reverse('extras:customfieldchoiceset', args=[self.pk])
|
||||||
|
|
||||||
@ -818,6 +824,32 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel
|
|||||||
if not self.base_choices and not self.extra_choices:
|
if not self.base_choices and not self.extra_choices:
|
||||||
raise ValidationError(_("Must define base or extra choices."))
|
raise ValidationError(_("Must define base or extra choices."))
|
||||||
|
|
||||||
|
# Check whether any choices have been removed. If so, check whether any of the removed
|
||||||
|
# choices are still set in custom field data for any object.
|
||||||
|
original_choices = set([
|
||||||
|
c[0] for c in self._original_extra_choices
|
||||||
|
]) if self._original_extra_choices else set()
|
||||||
|
current_choices = set([
|
||||||
|
c[0] for c in self.extra_choices
|
||||||
|
]) if self.extra_choices else set()
|
||||||
|
if removed_choices := original_choices - current_choices:
|
||||||
|
for custom_field in self.choices_for.all():
|
||||||
|
for object_type in custom_field.object_types.all():
|
||||||
|
model = object_type.model_class()
|
||||||
|
for choice in removed_choices:
|
||||||
|
# Form the query based on the type of custom field
|
||||||
|
if custom_field.type == CustomFieldTypeChoices.TYPE_MULTISELECT:
|
||||||
|
query_args = {f"custom_field_data__{custom_field.name}__contains": choice}
|
||||||
|
else:
|
||||||
|
query_args = {f"custom_field_data__{custom_field.name}": choice}
|
||||||
|
# Raise a ValidationError if there are any objects which still reference the removed choice
|
||||||
|
if model.objects.filter(models.Q(**query_args)).exists():
|
||||||
|
raise ValidationError(
|
||||||
|
_(
|
||||||
|
"Cannot remove choice {choice} as there are {model} objects which reference it."
|
||||||
|
).format(choice=choice, model=object_type)
|
||||||
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
# Sort choices if alphabetical ordering is enforced
|
# Sort choices if alphabetical ordering is enforced
|
||||||
|
@ -343,6 +343,74 @@ class CustomFieldTest(TestCase):
|
|||||||
instance.refresh_from_db()
|
instance.refresh_from_db()
|
||||||
self.assertIsNone(instance.custom_field_data.get(cf.name))
|
self.assertIsNone(instance.custom_field_data.get(cf.name))
|
||||||
|
|
||||||
|
def test_remove_selected_choice(self):
|
||||||
|
"""
|
||||||
|
Removing a ChoiceSet choice that is referenced by an object should raise
|
||||||
|
a ValidationError exception.
|
||||||
|
"""
|
||||||
|
CHOICES = (
|
||||||
|
('a', 'Option A'),
|
||||||
|
('b', 'Option B'),
|
||||||
|
('c', 'Option C'),
|
||||||
|
('d', 'Option D'),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a set of custom field choices
|
||||||
|
choice_set = CustomFieldChoiceSet.objects.create(
|
||||||
|
name='Custom Field Choice Set 1',
|
||||||
|
extra_choices=CHOICES
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a select custom field
|
||||||
|
cf = CustomField.objects.create(
|
||||||
|
name='select_field',
|
||||||
|
type=CustomFieldTypeChoices.TYPE_SELECT,
|
||||||
|
required=False,
|
||||||
|
choice_set=choice_set
|
||||||
|
)
|
||||||
|
cf.object_types.set([self.object_type])
|
||||||
|
|
||||||
|
# Create a multi-select custom field
|
||||||
|
cf_multiselect = CustomField.objects.create(
|
||||||
|
name='multiselect_field',
|
||||||
|
type=CustomFieldTypeChoices.TYPE_MULTISELECT,
|
||||||
|
required=False,
|
||||||
|
choice_set=choice_set
|
||||||
|
)
|
||||||
|
cf_multiselect.object_types.set([self.object_type])
|
||||||
|
|
||||||
|
# Assign a choice for both custom fields on an object
|
||||||
|
instance = Site.objects.first()
|
||||||
|
instance.custom_field_data[cf.name] = 'a'
|
||||||
|
instance.custom_field_data[cf_multiselect.name] = ['b', 'c']
|
||||||
|
instance.save()
|
||||||
|
|
||||||
|
# Attempting to delete a selected choice should fail
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
choice_set.extra_choices = (
|
||||||
|
('b', 'Option B'),
|
||||||
|
('c', 'Option C'),
|
||||||
|
('d', 'Option D'),
|
||||||
|
)
|
||||||
|
choice_set.full_clean()
|
||||||
|
|
||||||
|
# Attempting to delete either of the multi-select choices should fail
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
choice_set.extra_choices = (
|
||||||
|
('a', 'Option A'),
|
||||||
|
('b', 'Option B'),
|
||||||
|
('d', 'Option D'),
|
||||||
|
)
|
||||||
|
choice_set.full_clean()
|
||||||
|
|
||||||
|
# Removing a non-selected choice should succeed
|
||||||
|
choice_set.extra_choices = (
|
||||||
|
('a', 'Option A'),
|
||||||
|
('b', 'Option B'),
|
||||||
|
('c', 'Option C'),
|
||||||
|
)
|
||||||
|
choice_set.full_clean()
|
||||||
|
|
||||||
def test_object_field(self):
|
def test_object_field(self):
|
||||||
value = VLAN.objects.create(name='VLAN 1', vid=1).pk
|
value = VLAN.objects.create(name='VLAN 1', vid=1).pk
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
version: "4.1.2"
|
version: "4.1.3"
|
||||||
edition: "Community"
|
edition: "Community"
|
||||||
published: "2024-09-26"
|
published: "2024-10-02"
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-09-25 05:02+0000\n"
|
"POT-Creation-Date: 2024-10-02 05:01+0000\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -82,8 +82,8 @@ msgstr ""
|
|||||||
|
|
||||||
#: netbox/circuits/choices.py:21 netbox/dcim/choices.py:20
|
#: netbox/circuits/choices.py:21 netbox/dcim/choices.py:20
|
||||||
#: netbox/dcim/choices.py:102 netbox/dcim/choices.py:185
|
#: netbox/dcim/choices.py:102 netbox/dcim/choices.py:185
|
||||||
#: netbox/dcim/choices.py:231 netbox/dcim/choices.py:1522
|
#: netbox/dcim/choices.py:231 netbox/dcim/choices.py:1524
|
||||||
#: netbox/dcim/choices.py:1598 netbox/dcim/choices.py:1648
|
#: netbox/dcim/choices.py:1600 netbox/dcim/choices.py:1650
|
||||||
#: netbox/virtualization/choices.py:20 netbox/virtualization/choices.py:45
|
#: netbox/virtualization/choices.py:20 netbox/virtualization/choices.py:45
|
||||||
#: netbox/vpn/choices.py:18
|
#: netbox/vpn/choices.py:18
|
||||||
msgid "Planned"
|
msgid "Planned"
|
||||||
@ -96,7 +96,7 @@ msgstr ""
|
|||||||
#: netbox/circuits/choices.py:23 netbox/core/tables/tasks.py:22
|
#: netbox/circuits/choices.py:23 netbox/core/tables/tasks.py:22
|
||||||
#: netbox/dcim/choices.py:22 netbox/dcim/choices.py:103
|
#: netbox/dcim/choices.py:22 netbox/dcim/choices.py:103
|
||||||
#: netbox/dcim/choices.py:184 netbox/dcim/choices.py:230
|
#: netbox/dcim/choices.py:184 netbox/dcim/choices.py:230
|
||||||
#: netbox/dcim/choices.py:1597 netbox/dcim/choices.py:1647
|
#: netbox/dcim/choices.py:1599 netbox/dcim/choices.py:1649
|
||||||
#: netbox/extras/tables/tables.py:495 netbox/ipam/choices.py:31
|
#: netbox/extras/tables/tables.py:495 netbox/ipam/choices.py:31
|
||||||
#: netbox/ipam/choices.py:49 netbox/ipam/choices.py:69
|
#: netbox/ipam/choices.py:49 netbox/ipam/choices.py:69
|
||||||
#: netbox/ipam/choices.py:154 netbox/templates/extras/configcontext.html:25
|
#: netbox/ipam/choices.py:154 netbox/templates/extras/configcontext.html:25
|
||||||
@ -107,8 +107,8 @@ msgid "Active"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/choices.py:24 netbox/dcim/choices.py:183
|
#: netbox/circuits/choices.py:24 netbox/dcim/choices.py:183
|
||||||
#: netbox/dcim/choices.py:229 netbox/dcim/choices.py:1596
|
#: netbox/dcim/choices.py:229 netbox/dcim/choices.py:1598
|
||||||
#: netbox/dcim/choices.py:1649 netbox/virtualization/choices.py:24
|
#: netbox/dcim/choices.py:1651 netbox/virtualization/choices.py:24
|
||||||
#: netbox/virtualization/choices.py:43
|
#: netbox/virtualization/choices.py:43
|
||||||
msgid "Offline"
|
msgid "Offline"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -121,7 +121,7 @@ msgstr ""
|
|||||||
msgid "Decommissioned"
|
msgid "Decommissioned"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1609
|
#: netbox/circuits/choices.py:90 netbox/dcim/choices.py:1611
|
||||||
#: netbox/tenancy/choices.py:17
|
#: netbox/tenancy/choices.py:17
|
||||||
msgid "Primary"
|
msgid "Primary"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -1587,7 +1587,7 @@ msgstr ""
|
|||||||
#: netbox/core/choices.py:22 netbox/core/choices.py:59
|
#: netbox/core/choices.py:22 netbox/core/choices.py:59
|
||||||
#: netbox/core/constants.py:20 netbox/core/tables/tasks.py:34
|
#: netbox/core/constants.py:20 netbox/core/tables/tasks.py:34
|
||||||
#: netbox/dcim/choices.py:187 netbox/dcim/choices.py:233
|
#: netbox/dcim/choices.py:187 netbox/dcim/choices.py:233
|
||||||
#: netbox/dcim/choices.py:1599 netbox/virtualization/choices.py:47
|
#: netbox/dcim/choices.py:1601 netbox/virtualization/choices.py:47
|
||||||
msgid "Failed"
|
msgid "Failed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1653,42 +1653,42 @@ msgstr ""
|
|||||||
msgid "Cancelled"
|
msgid "Cancelled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/data_backends.py:29 netbox/core/tables/plugins.py:51
|
#: netbox/core/data_backends.py:32 netbox/core/tables/plugins.py:51
|
||||||
#: netbox/templates/core/plugin.html:87
|
#: netbox/templates/core/plugin.html:87
|
||||||
#: netbox/templates/dcim/interface.html:216
|
#: netbox/templates/dcim/interface.html:216
|
||||||
msgid "Local"
|
msgid "Local"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/data_backends.py:47 netbox/core/tables/change_logging.py:20
|
#: netbox/core/data_backends.py:50 netbox/core/tables/change_logging.py:20
|
||||||
#: netbox/templates/account/profile.html:15 netbox/templates/users/user.html:17
|
#: netbox/templates/account/profile.html:15 netbox/templates/users/user.html:17
|
||||||
#: netbox/users/tables.py:31
|
#: netbox/users/tables.py:31
|
||||||
msgid "Username"
|
msgid "Username"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/data_backends.py:49 netbox/core/data_backends.py:55
|
#: netbox/core/data_backends.py:52 netbox/core/data_backends.py:58
|
||||||
msgid "Only used for cloning with HTTP(S)"
|
msgid "Only used for cloning with HTTP(S)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/data_backends.py:53 netbox/templates/account/base.html:23
|
#: netbox/core/data_backends.py:56 netbox/templates/account/base.html:23
|
||||||
#: netbox/templates/account/password.html:12
|
#: netbox/templates/account/password.html:12
|
||||||
#: netbox/users/forms/model_forms.py:171
|
#: netbox/users/forms/model_forms.py:171
|
||||||
msgid "Password"
|
msgid "Password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/data_backends.py:59
|
#: netbox/core/data_backends.py:62
|
||||||
msgid "Branch"
|
msgid "Branch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/data_backends.py:106
|
#: netbox/core/data_backends.py:120
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Fetching remote data failed ({name}): {error}"
|
msgid "Fetching remote data failed ({name}): {error}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/data_backends.py:119
|
#: netbox/core/data_backends.py:133
|
||||||
msgid "AWS access key ID"
|
msgid "AWS access key ID"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/data_backends.py:123
|
#: netbox/core/data_backends.py:137
|
||||||
msgid "AWS secret access key"
|
msgid "AWS secret access key"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1911,7 +1911,7 @@ msgstr ""
|
|||||||
msgid "Rack Elevations"
|
msgid "Rack Elevations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/core/forms/model_forms.py:157 netbox/dcim/choices.py:1510
|
#: netbox/core/forms/model_forms.py:157 netbox/dcim/choices.py:1512
|
||||||
#: netbox/dcim/forms/bulk_edit.py:969 netbox/dcim/forms/bulk_edit.py:1357
|
#: netbox/dcim/forms/bulk_edit.py:969 netbox/dcim/forms/bulk_edit.py:1357
|
||||||
#: netbox/dcim/forms/bulk_edit.py:1375 netbox/dcim/tables/racks.py:158
|
#: netbox/dcim/forms/bulk_edit.py:1375 netbox/dcim/tables/racks.py:158
|
||||||
#: netbox/netbox/navigation/menu.py:291 netbox/netbox/navigation/menu.py:295
|
#: netbox/netbox/navigation/menu.py:291 netbox/netbox/navigation/menu.py:295
|
||||||
@ -2477,7 +2477,7 @@ msgid "Staging"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:23 netbox/dcim/choices.py:189
|
#: netbox/dcim/choices.py:23 netbox/dcim/choices.py:189
|
||||||
#: netbox/dcim/choices.py:234 netbox/dcim/choices.py:1523
|
#: netbox/dcim/choices.py:234 netbox/dcim/choices.py:1525
|
||||||
#: netbox/virtualization/choices.py:23 netbox/virtualization/choices.py:48
|
#: netbox/virtualization/choices.py:23 netbox/virtualization/choices.py:48
|
||||||
msgid "Decommissioning"
|
msgid "Decommissioning"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2541,7 +2541,7 @@ msgstr ""
|
|||||||
msgid "Millimeters"
|
msgid "Millimeters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:115 netbox/dcim/choices.py:1545
|
#: netbox/dcim/choices.py:115 netbox/dcim/choices.py:1547
|
||||||
msgid "Inches"
|
msgid "Inches"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2630,7 +2630,7 @@ msgid "Side to rear"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:209 netbox/dcim/choices.py:253
|
#: netbox/dcim/choices.py:209 netbox/dcim/choices.py:253
|
||||||
#: netbox/dcim/choices.py:1295
|
#: netbox/dcim/choices.py:1297
|
||||||
msgid "Passive"
|
msgid "Passive"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2659,8 +2659,8 @@ msgid "Proprietary"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:575 netbox/dcim/choices.py:818
|
#: netbox/dcim/choices.py:575 netbox/dcim/choices.py:818
|
||||||
#: netbox/dcim/choices.py:1211 netbox/dcim/choices.py:1213
|
#: netbox/dcim/choices.py:1213 netbox/dcim/choices.py:1215
|
||||||
#: netbox/dcim/choices.py:1439 netbox/dcim/choices.py:1441
|
#: netbox/dcim/choices.py:1441 netbox/dcim/choices.py:1443
|
||||||
#: netbox/netbox/navigation/menu.py:200
|
#: netbox/netbox/navigation/menu.py:200
|
||||||
msgid "Other"
|
msgid "Other"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2673,11 +2673,11 @@ msgstr ""
|
|||||||
msgid "Physical"
|
msgid "Physical"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:849 netbox/dcim/choices.py:1016
|
#: netbox/dcim/choices.py:849 netbox/dcim/choices.py:1017
|
||||||
msgid "Virtual"
|
msgid "Virtual"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:850 netbox/dcim/choices.py:1089
|
#: netbox/dcim/choices.py:850 netbox/dcim/choices.py:1091
|
||||||
#: netbox/dcim/forms/bulk_edit.py:1515 netbox/dcim/forms/filtersets.py:1330
|
#: netbox/dcim/forms/bulk_edit.py:1515 netbox/dcim/forms/filtersets.py:1330
|
||||||
#: netbox/dcim/forms/model_forms.py:988 netbox/dcim/forms/model_forms.py:1397
|
#: netbox/dcim/forms/model_forms.py:988 netbox/dcim/forms/model_forms.py:1397
|
||||||
#: netbox/netbox/navigation/menu.py:140 netbox/netbox/navigation/menu.py:144
|
#: netbox/netbox/navigation/menu.py:140 netbox/netbox/navigation/menu.py:144
|
||||||
@ -2685,11 +2685,11 @@ msgstr ""
|
|||||||
msgid "Wireless"
|
msgid "Wireless"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1014
|
#: netbox/dcim/choices.py:1015
|
||||||
msgid "Virtual interfaces"
|
msgid "Virtual interfaces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1017 netbox/dcim/forms/bulk_edit.py:1410
|
#: netbox/dcim/choices.py:1018 netbox/dcim/forms/bulk_edit.py:1410
|
||||||
#: netbox/dcim/forms/bulk_import.py:840 netbox/dcim/forms/model_forms.py:974
|
#: netbox/dcim/forms/bulk_import.py:840 netbox/dcim/forms/model_forms.py:974
|
||||||
#: netbox/dcim/tables/devices.py:657 netbox/templates/dcim/interface.html:106
|
#: netbox/dcim/tables/devices.py:657 netbox/templates/dcim/interface.html:106
|
||||||
#: netbox/templates/virtualization/vminterface.html:43
|
#: netbox/templates/virtualization/vminterface.html:43
|
||||||
@ -2699,27 +2699,27 @@ msgstr ""
|
|||||||
msgid "Bridge"
|
msgid "Bridge"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1018
|
#: netbox/dcim/choices.py:1019
|
||||||
msgid "Link Aggregation Group (LAG)"
|
msgid "Link Aggregation Group (LAG)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1022
|
#: netbox/dcim/choices.py:1023
|
||||||
msgid "Ethernet (fixed)"
|
msgid "Ethernet (fixed)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1037
|
#: netbox/dcim/choices.py:1038
|
||||||
msgid "Ethernet (modular)"
|
msgid "Ethernet (modular)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1073
|
#: netbox/dcim/choices.py:1075
|
||||||
msgid "Ethernet (backplane)"
|
msgid "Ethernet (backplane)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1105
|
#: netbox/dcim/choices.py:1107
|
||||||
msgid "Cellular"
|
msgid "Cellular"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1157 netbox/dcim/forms/filtersets.py:383
|
#: netbox/dcim/choices.py:1159 netbox/dcim/forms/filtersets.py:383
|
||||||
#: netbox/dcim/forms/filtersets.py:809 netbox/dcim/forms/filtersets.py:963
|
#: netbox/dcim/forms/filtersets.py:809 netbox/dcim/forms/filtersets.py:963
|
||||||
#: netbox/dcim/forms/filtersets.py:1542
|
#: netbox/dcim/forms/filtersets.py:1542
|
||||||
#: netbox/templates/dcim/inventoryitem.html:52
|
#: netbox/templates/dcim/inventoryitem.html:52
|
||||||
@ -2727,130 +2727,130 @@ msgstr ""
|
|||||||
msgid "Serial"
|
msgid "Serial"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1172
|
#: netbox/dcim/choices.py:1174
|
||||||
msgid "Coaxial"
|
msgid "Coaxial"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1192
|
#: netbox/dcim/choices.py:1194
|
||||||
msgid "Stacking"
|
msgid "Stacking"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1242
|
#: netbox/dcim/choices.py:1244
|
||||||
msgid "Half"
|
msgid "Half"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1243
|
#: netbox/dcim/choices.py:1245
|
||||||
msgid "Full"
|
msgid "Full"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1244 netbox/netbox/preferences.py:31
|
#: netbox/dcim/choices.py:1246 netbox/netbox/preferences.py:31
|
||||||
#: netbox/wireless/choices.py:480
|
#: netbox/wireless/choices.py:480
|
||||||
msgid "Auto"
|
msgid "Auto"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1255
|
#: netbox/dcim/choices.py:1257
|
||||||
msgid "Access"
|
msgid "Access"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1256 netbox/ipam/tables/vlans.py:172
|
#: netbox/dcim/choices.py:1258 netbox/ipam/tables/vlans.py:172
|
||||||
#: netbox/ipam/tables/vlans.py:217
|
#: netbox/ipam/tables/vlans.py:217
|
||||||
#: netbox/templates/dcim/inc/interface_vlans_table.html:7
|
#: netbox/templates/dcim/inc/interface_vlans_table.html:7
|
||||||
msgid "Tagged"
|
msgid "Tagged"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1257
|
#: netbox/dcim/choices.py:1259
|
||||||
msgid "Tagged (All)"
|
msgid "Tagged (All)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1286
|
#: netbox/dcim/choices.py:1288
|
||||||
msgid "IEEE Standard"
|
msgid "IEEE Standard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1297
|
#: netbox/dcim/choices.py:1299
|
||||||
msgid "Passive 24V (2-pair)"
|
msgid "Passive 24V (2-pair)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1298
|
#: netbox/dcim/choices.py:1300
|
||||||
msgid "Passive 24V (4-pair)"
|
msgid "Passive 24V (4-pair)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1299
|
#: netbox/dcim/choices.py:1301
|
||||||
msgid "Passive 48V (2-pair)"
|
msgid "Passive 48V (2-pair)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1300
|
#: netbox/dcim/choices.py:1302
|
||||||
msgid "Passive 48V (4-pair)"
|
msgid "Passive 48V (4-pair)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1370 netbox/dcim/choices.py:1480
|
#: netbox/dcim/choices.py:1372 netbox/dcim/choices.py:1482
|
||||||
msgid "Copper"
|
msgid "Copper"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1393
|
#: netbox/dcim/choices.py:1395
|
||||||
msgid "Fiber Optic"
|
msgid "Fiber Optic"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1426 netbox/dcim/choices.py:1509
|
#: netbox/dcim/choices.py:1428 netbox/dcim/choices.py:1511
|
||||||
msgid "USB"
|
msgid "USB"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1496
|
#: netbox/dcim/choices.py:1498
|
||||||
msgid "Fiber"
|
msgid "Fiber"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1521 netbox/dcim/forms/filtersets.py:1227
|
#: netbox/dcim/choices.py:1523 netbox/dcim/forms/filtersets.py:1227
|
||||||
msgid "Connected"
|
msgid "Connected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1540 netbox/wireless/choices.py:497
|
#: netbox/dcim/choices.py:1542 netbox/wireless/choices.py:497
|
||||||
msgid "Kilometers"
|
msgid "Kilometers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1541 netbox/templates/dcim/cable_trace.html:65
|
#: netbox/dcim/choices.py:1543 netbox/templates/dcim/cable_trace.html:65
|
||||||
#: netbox/wireless/choices.py:498
|
#: netbox/wireless/choices.py:498
|
||||||
msgid "Meters"
|
msgid "Meters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1542
|
#: netbox/dcim/choices.py:1544
|
||||||
msgid "Centimeters"
|
msgid "Centimeters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1543 netbox/wireless/choices.py:499
|
#: netbox/dcim/choices.py:1545 netbox/wireless/choices.py:499
|
||||||
msgid "Miles"
|
msgid "Miles"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1544 netbox/templates/dcim/cable_trace.html:66
|
#: netbox/dcim/choices.py:1546 netbox/templates/dcim/cable_trace.html:66
|
||||||
#: netbox/wireless/choices.py:500
|
#: netbox/wireless/choices.py:500
|
||||||
msgid "Feet"
|
msgid "Feet"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1560 netbox/templates/dcim/device.html:327
|
#: netbox/dcim/choices.py:1562 netbox/templates/dcim/device.html:327
|
||||||
#: netbox/templates/dcim/rack.html:107
|
#: netbox/templates/dcim/rack.html:107
|
||||||
msgid "Kilograms"
|
msgid "Kilograms"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1561
|
#: netbox/dcim/choices.py:1563
|
||||||
msgid "Grams"
|
msgid "Grams"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1562 netbox/templates/dcim/device.html:328
|
#: netbox/dcim/choices.py:1564 netbox/templates/dcim/device.html:328
|
||||||
#: netbox/templates/dcim/rack.html:108
|
#: netbox/templates/dcim/rack.html:108
|
||||||
msgid "Pounds"
|
msgid "Pounds"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1563
|
#: netbox/dcim/choices.py:1565
|
||||||
msgid "Ounces"
|
msgid "Ounces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1610
|
#: netbox/dcim/choices.py:1612
|
||||||
msgid "Redundant"
|
msgid "Redundant"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1631
|
#: netbox/dcim/choices.py:1633
|
||||||
msgid "Single phase"
|
msgid "Single phase"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/dcim/choices.py:1632
|
#: netbox/dcim/choices.py:1634
|
||||||
msgid "Three-phase"
|
msgid "Three-phase"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -8233,10 +8233,17 @@ msgstr ""
|
|||||||
msgid "custom field choice sets"
|
msgid "custom field choice sets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/customfields.py:819
|
#: netbox/extras/models/customfields.py:825
|
||||||
msgid "Must define base or extra choices."
|
msgid "Must define base or extra choices."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: netbox/extras/models/customfields.py:849
|
||||||
|
#, python-brace-format
|
||||||
|
msgid ""
|
||||||
|
"Cannot remove choice {choice} as there are {model} objects which reference "
|
||||||
|
"it."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/extras/models/dashboard.py:18
|
#: netbox/extras/models/dashboard.py:18
|
||||||
msgid "layout"
|
msgid "layout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -13749,11 +13756,6 @@ msgstr ""
|
|||||||
msgid "Disk Space"
|
msgid "Disk Space"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/templates/virtualization/cluster.html:72
|
|
||||||
msgctxt "Abbreviation for gigabyte"
|
|
||||||
msgid "GB"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: netbox/templates/virtualization/cluster/base.html:18
|
#: netbox/templates/virtualization/cluster/base.html:18
|
||||||
msgid "Add Virtual Machine"
|
msgid "Add Virtual Machine"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -14493,7 +14495,7 @@ msgid "Invalid value for a multiple choice field: {value}"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/utilities/forms/fields/csv.py:57
|
#: netbox/utilities/forms/fields/csv.py:57
|
||||||
#: netbox/utilities/forms/fields/csv.py:74
|
#: netbox/utilities/forms/fields/csv.py:78
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Object not found: %(value)s"
|
msgid "Object not found: %(value)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -14504,11 +14506,16 @@ msgid ""
|
|||||||
"\"{value}\" is not a unique value for this field; multiple objects were found"
|
"\"{value}\" is not a unique value for this field; multiple objects were found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/utilities/forms/fields/csv.py:97
|
#: netbox/utilities/forms/fields/csv.py:69
|
||||||
msgid "Object type must be specified as \"<app>.<model>\""
|
#, python-brace-format
|
||||||
|
msgid "\"{field_name}\" is an invalid accessor field name."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: netbox/utilities/forms/fields/csv.py:101
|
#: netbox/utilities/forms/fields/csv.py:101
|
||||||
|
msgid "Object type must be specified as \"<app>.<model>\""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: netbox/utilities/forms/fields/csv.py:105
|
||||||
msgid "Invalid object type"
|
msgid "Invalid object type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -93,3 +93,7 @@ HTML_ALLOWED_ATTRIBUTES = {
|
|||||||
"td": {"align"},
|
"td": {"align"},
|
||||||
"th": {"align"},
|
"th": {"align"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HTTP_PROXY_SUPPORTED_SOCK_SCHEMAS = ['socks4', 'socks4a', 'socks4h', 'socks5', 'socks5a', 'socks5h']
|
||||||
|
HTTP_PROXY_SOCK_RDNS_SCHEMAS = ['socks4h', 'socks4a', 'socks5h', 'socks5a']
|
||||||
|
HTTP_PROXY_SUPPORTED_SCHEMAS = ['http', 'https', 'socks4', 'socks4a', 'socks4h', 'socks5', 'socks5a', 'socks5h']
|
||||||
|
101
netbox/utilities/socks.py
Normal file
101
netbox/utilities/socks.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
from urllib3 import PoolManager, HTTPConnectionPool, HTTPSConnectionPool
|
||||||
|
from urllib3.connection import HTTPConnection, HTTPSConnection
|
||||||
|
from .constants import HTTP_PROXY_SOCK_RDNS_SCHEMAS
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('netbox.utilities')
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyHTTPConnection(HTTPConnection):
|
||||||
|
"""
|
||||||
|
A Proxy connection class that uses a SOCK proxy - used to create
|
||||||
|
a urllib3 PoolManager that routes connections via the proxy.
|
||||||
|
This is for an HTTP (not HTTPS) connection
|
||||||
|
"""
|
||||||
|
use_rdns = False
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
socks_options = kwargs.pop('_socks_options')
|
||||||
|
self._proxy_url = socks_options['proxy_url']
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _new_conn(self):
|
||||||
|
try:
|
||||||
|
from python_socks.sync import Proxy
|
||||||
|
except ModuleNotFoundError as e:
|
||||||
|
logger.info("Configuring an HTTP proxy using SOCKS requires the python_socks library. Check that it has been installed.")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
proxy = Proxy.from_url(self._proxy_url, rdns=self.use_rdns)
|
||||||
|
return proxy.connect(
|
||||||
|
dest_host=self.host,
|
||||||
|
dest_port=self.port,
|
||||||
|
timeout=self.timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyHTTPSConnection(ProxyHTTPConnection, HTTPSConnection):
|
||||||
|
"""
|
||||||
|
A Proxy connection class for an HTTPS (not HTTP) connection.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RdnsProxyHTTPConnection(ProxyHTTPConnection):
|
||||||
|
"""
|
||||||
|
A Proxy connection class for an HTTP remote-dns connection.
|
||||||
|
I.E. socks4a, socks4h, socks5a, socks5h
|
||||||
|
"""
|
||||||
|
use_rdns = True
|
||||||
|
|
||||||
|
|
||||||
|
class RdnsProxyHTTPSConnection(ProxyHTTPSConnection):
|
||||||
|
"""
|
||||||
|
A Proxy connection class for an HTTPS remote-dns connection.
|
||||||
|
I.E. socks4a, socks4h, socks5a, socks5h
|
||||||
|
"""
|
||||||
|
use_rdns = True
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyHTTPConnectionPool(HTTPConnectionPool):
|
||||||
|
ConnectionCls = ProxyHTTPConnection
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyHTTPSConnectionPool(HTTPSConnectionPool):
|
||||||
|
ConnectionCls = ProxyHTTPSConnection
|
||||||
|
|
||||||
|
|
||||||
|
class RdnsProxyHTTPConnectionPool(HTTPConnectionPool):
|
||||||
|
ConnectionCls = RdnsProxyHTTPConnection
|
||||||
|
|
||||||
|
|
||||||
|
class RdnsProxyHTTPSConnectionPool(HTTPSConnectionPool):
|
||||||
|
ConnectionCls = RdnsProxyHTTPSConnection
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyPoolManager(PoolManager):
|
||||||
|
def __init__(self, proxy_url, timeout=5, num_pools=10, headers=None, **connection_pool_kw):
|
||||||
|
# python_socks uses rdns param to denote remote DNS parsing and
|
||||||
|
# doesn't accept the 'h' or 'a' in the proxy URL
|
||||||
|
if use_rdns := urlparse(proxy_url).scheme in HTTP_PROXY_SOCK_RDNS_SCHEMAS:
|
||||||
|
proxy_url = proxy_url.replace('socks5h:', 'socks5:').replace('socks5a:', 'socks5:')
|
||||||
|
proxy_url = proxy_url.replace('socks4h:', 'socks4:').replace('socks4a:', 'socks4:')
|
||||||
|
|
||||||
|
connection_pool_kw['_socks_options'] = {'proxy_url': proxy_url}
|
||||||
|
connection_pool_kw['timeout'] = timeout
|
||||||
|
|
||||||
|
super().__init__(num_pools, headers, **connection_pool_kw)
|
||||||
|
|
||||||
|
if use_rdns:
|
||||||
|
self.pool_classes_by_scheme = {
|
||||||
|
'http': RdnsProxyHTTPConnectionPool,
|
||||||
|
'https': RdnsProxyHTTPSConnectionPool,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
self.pool_classes_by_scheme = {
|
||||||
|
'http': ProxyHTTPConnectionPool,
|
||||||
|
'https': ProxyHTTPSConnectionPool,
|
||||||
|
}
|
@ -10,7 +10,7 @@ django-prometheus==2.3.1
|
|||||||
django-redis==5.4.0
|
django-redis==5.4.0
|
||||||
django-rich==1.11.0
|
django-rich==1.11.0
|
||||||
django-rq==2.10.2
|
django-rq==2.10.2
|
||||||
django-taggit==6.0.0
|
django-taggit==6.1.0
|
||||||
django-tables2==2.7.0
|
django-tables2==2.7.0
|
||||||
django-timezone-field==7.0
|
django-timezone-field==7.0
|
||||||
djangorestframework==3.15.2
|
djangorestframework==3.15.2
|
||||||
@ -20,12 +20,12 @@ 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.38
|
mkdocs-material==9.5.39
|
||||||
mkdocstrings[python-legacy]==0.26.1
|
mkdocstrings[python-legacy]==0.26.1
|
||||||
netaddr==1.3.0
|
netaddr==1.3.0
|
||||||
nh3==0.2.18
|
nh3==0.2.18
|
||||||
Pillow==10.4.0
|
Pillow==10.4.0
|
||||||
psycopg[c,pool]==3.2.2
|
psycopg[c,pool]==3.2.3
|
||||||
PyYAML==6.0.2
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user