Merge branch 'develop' into feature

This commit is contained in:
jeremystretch 2022-03-30 15:45:40 -04:00
commit 3ff4fd814e
18 changed files with 125 additions and 55 deletions

View File

@ -6,7 +6,7 @@ Prior to upgrading your NetBox instance, be sure to carefully review all [releas
## Update Dependencies to Required Versions ## Update Dependencies to Required Versions
NetBox v3.0 and later requires the following: NetBox v3.0 and later require the following:
| Dependency | Minimum Version | | Dependency | Minimum Version |
|------------|-----------------| |------------|-----------------|
@ -67,6 +67,11 @@ sudo git checkout master
sudo git pull origin master sudo git pull origin master
``` ```
!!! info "Checking out an older release"
If you need to upgrade to an older version rather than the current stable release, you can check out any valid [git tag](https://github.com/netbox-community/netbox/tags), each of which represents a release. For example, to checkout the code for NetBox v2.11.11, do:
sudo git checkout v2.11.11
## Run the Upgrade Script ## Run the Upgrade Script
Once the new code is in place, verify that any optional Python packages required by your deployment (e.g. `napalm` or `django-auth-ldap`) are listed in `local_requirements.txt`. Then, run the upgrade script: Once the new code is in place, verify that any optional Python packages required by your deployment (e.g. `napalm` or `django-auth-ldap`) are listed in `local_requirements.txt`. Then, run the upgrade script:

View File

@ -2,7 +2,7 @@
Custom links allow users to display arbitrary hyperlinks to external content within NetBox object views. These are helpful for cross-referencing related records in systems outside NetBox. For example, you might create a custom link on the device view which links to the current device in a Network Monitoring System (NMS). Custom links allow users to display arbitrary hyperlinks to external content within NetBox object views. These are helpful for cross-referencing related records in systems outside NetBox. For example, you might create a custom link on the device view which links to the current device in a Network Monitoring System (NMS).
Custom links are created by navigating to Customization > Custom Links. Each link is associated with a particular NetBox object type (site, device, prefix, etc.) and will be displayed on relevant views. Each link has display text and a URL, and data from the Netbox item being viewed can be included in the link using [Jinja2 template code](https://jinja2docs.readthedocs.io/en/stable/) through the variable `obj`, and custom fields through `obj.cf`. Custom links are created by navigating to Customization > Custom Links. Each link is associated with a particular NetBox object type (site, device, prefix, etc.) and will be displayed on relevant views. Each link has display text and a URL, and data from the NetBox item being viewed can be included in the link using [Jinja2 template code](https://jinja2docs.readthedocs.io/en/stable/) through the variable `obj`, and custom fields through `obj.cf`.
For example, you might define a link like this: For example, you might define a link like this:
@ -33,6 +33,10 @@ The following context data is available within the template when rendering a cus
| `user` | The current user (if authenticated) | | `user` | The current user (if authenticated) |
| `perms` | The [permissions](https://docs.djangoproject.com/en/stable/topics/auth/default/#permissions) assigned to the user | | `perms` | The [permissions](https://docs.djangoproject.com/en/stable/topics/auth/default/#permissions) assigned to the user |
While most of the context variables listed above will have consistent attributes, the object will be an instance of the specific object being viewed when the link is rendered. Different models have different fields and properties, so you may need to some research to determine the attributes available for use within your template for a specific object type.
Checking the REST API representation of an object is generally a convenient way to determine what attributes are available. You can also reference the NetBox source code directly for a comprehensive list.
## Conditional Rendering ## Conditional Rendering
Only links which render with non-empty text are included on the page. You can employ conditional Jinja2 logic to control the conditions under which a link gets rendered. Only links which render with non-empty text are included on the page. You can employ conditional Jinja2 logic to control the conditions under which a link gets rendered.

View File

@ -3,7 +3,7 @@
A token is a unique identifier mapped to a NetBox user account. Each user may have one or more tokens which he or she can use for authentication when making REST API requests. To create a token, navigate to the API tokens page under your user profile. A token is a unique identifier mapped to a NetBox user account. Each user may have one or more tokens which he or she can use for authentication when making REST API requests. To create a token, navigate to the API tokens page under your user profile.
!!! note !!! note
The creation and modification of API tokens can be restricted per user by an administrator. If you don't see an option to create an API token, ask an administrator to grant you access. All users can create and manage REST API tokens under the user control panel in the UI. The ability to view, add, change, or delete tokens via the REST API itself is controlled by the relevant model permissions, assigned to users and/or groups in the admin UI. These permissions should be used with great care to avoid accidentally permitting a user to create tokens for other user accounts.
Each token contains a 160-bit key represented as 40 hexadecimal characters. When creating a token, you'll typically leave the key field blank so that a random key will be automatically generated. However, NetBox allows you to specify a key in case you need to restore a previously deleted token to operation. Each token contains a 160-bit key represented as 40 hexadecimal characters. When creating a token, you'll typically leave the key field blank so that a random key will be automatically generated. However, NetBox allows you to specify a key in case you need to restore a previously deleted token to operation.

View File

@ -2,6 +2,17 @@
## v3.1.11 (FUTURE) ## v3.1.11 (FUTURE)
### Enhancements
* [#8163](https://github.com/netbox-community/netbox/issues/8163) - Show bridge interface members under interface view
* [#8785](https://github.com/netbox-community/netbox/issues/8785) - Permit wildcard values in IP address DNS names
* [#8830](https://github.com/netbox-community/netbox/issues/8830) - Add Checkpoint ClusterXL protocol for FHRP groups
* [#8974](https://github.com/netbox-community/netbox/issues/8974) - Use monospace font for text areas in config revision form
### Bug Fixes
* [#8866](https://github.com/netbox-community/netbox/issues/8866) - Prevent exception when searching for a rack position with no rack specified under device edit view
--- ---
## v3.1.10 (2022-03-25) ## v3.1.10 (2022-03-25)

View File

@ -784,6 +784,10 @@ class Interface(ModularComponentModel, BaseInterface, LinkTermination, PathEndpo
def is_lag(self): def is_lag(self):
return self.type == InterfaceTypeChoices.TYPE_LAG return self.type == InterfaceTypeChoices.TYPE_LAG
@property
def is_bridge(self):
return self.type == InterfaceTypeChoices.TYPE_BRIDGE
@property @property
def link(self): def link(self):
return self.cable or self.wireless_link return self.cable or self.wireless_link

View File

@ -2077,6 +2077,14 @@ class InterfaceView(generic.ObjectView):
orderable=False orderable=False
) )
# Get bridge interfaces
bridge_interfaces = Interface.objects.restrict(request.user, 'view').filter(bridge=instance)
bridge_interfaces_tables = tables.InterfaceTable(
bridge_interfaces,
exclude=('device', 'parent'),
orderable=False
)
# Get child interfaces # Get child interfaces
child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance) child_interfaces = Interface.objects.restrict(request.user, 'view').filter(parent=instance)
child_interfaces_tables = tables.InterfaceTable( child_interfaces_tables = tables.InterfaceTable(
@ -2101,6 +2109,7 @@ class InterfaceView(generic.ObjectView):
return { return {
'ipaddress_table': ipaddress_table, 'ipaddress_table': ipaddress_table,
'bridge_interfaces_table': bridge_interfaces_tables,
'child_interfaces_table': child_interfaces_tables, 'child_interfaces_table': child_interfaces_tables,
'vlan_table': vlan_table, 'vlan_table': vlan_table,
} }

View File

@ -23,15 +23,18 @@ class ConfigRevisionAdmin(admin.ModelAdmin):
}), }),
('Banners', { ('Banners', {
'fields': ('BANNER_LOGIN', 'BANNER_TOP', 'BANNER_BOTTOM'), 'fields': ('BANNER_LOGIN', 'BANNER_TOP', 'BANNER_BOTTOM'),
'classes': ('monospace',),
}), }),
('Pagination', { ('Pagination', {
'fields': ('PAGINATE_COUNT', 'MAX_PAGE_SIZE'), 'fields': ('PAGINATE_COUNT', 'MAX_PAGE_SIZE'),
}), }),
('Validation', { ('Validation', {
'fields': ('CUSTOM_VALIDATORS',), 'fields': ('CUSTOM_VALIDATORS',),
'classes': ('monospace',),
}), }),
('NAPALM', { ('NAPALM', {
'fields': ('NAPALM_USERNAME', 'NAPALM_PASSWORD', 'NAPALM_TIMEOUT', 'NAPALM_ARGS'), 'fields': ('NAPALM_USERNAME', 'NAPALM_PASSWORD', 'NAPALM_TIMEOUT', 'NAPALM_ARGS'),
'classes': ('monospace',),
}), }),
('User Preferences', { ('User Preferences', {
'fields': ('DEFAULT_USER_PREFERENCES',), 'fields': ('DEFAULT_USER_PREFERENCES',),

View File

@ -106,14 +106,22 @@ class FHRPGroupProtocolChoices(ChoiceSet):
PROTOCOL_HSRP = 'hsrp' PROTOCOL_HSRP = 'hsrp'
PROTOCOL_GLBP = 'glbp' PROTOCOL_GLBP = 'glbp'
PROTOCOL_CARP = 'carp' PROTOCOL_CARP = 'carp'
PROTOCOL_CLUSTERXL = 'clusterxl'
PROTOCOL_OTHER = 'other' PROTOCOL_OTHER = 'other'
CHOICES = ( CHOICES = (
('Standard', (
(PROTOCOL_VRRP2, 'VRRPv2'), (PROTOCOL_VRRP2, 'VRRPv2'),
(PROTOCOL_VRRP3, 'VRRPv3'), (PROTOCOL_VRRP3, 'VRRPv3'),
(PROTOCOL_CARP, 'CARP'),
)),
('CheckPoint', (
(PROTOCOL_CLUSTERXL, 'ClusterXL'),
)),
('Cisco', (
(PROTOCOL_HSRP, 'HSRP'), (PROTOCOL_HSRP, 'HSRP'),
(PROTOCOL_GLBP, 'GLBP'), (PROTOCOL_GLBP, 'GLBP'),
(PROTOCOL_CARP, 'CARP'), )),
(PROTOCOL_OTHER, 'Other'), (PROTOCOL_OTHER, 'Other'),
) )

View File

@ -50,7 +50,7 @@ class Migration(migrations.Migration):
('status', models.CharField(default='active', max_length=50)), ('status', models.CharField(default='active', max_length=50)),
('role', models.CharField(blank=True, max_length=50)), ('role', models.CharField(blank=True, max_length=50)),
('assigned_object_id', models.PositiveIntegerField(blank=True, null=True)), ('assigned_object_id', models.PositiveIntegerField(blank=True, null=True)),
('dns_name', models.CharField(blank=True, max_length=255, validators=[django.core.validators.RegexValidator(code='invalid', message='Only alphanumeric characters, hyphens, periods, and underscores are allowed in DNS names', regex='^[0-9A-Za-z._-]+$')])), ('dns_name', models.CharField(blank=True, max_length=255, validators=[django.core.validators.RegexValidator(code='invalid', message='Only alphanumeric characters, asterisks, hyphens, periods, and underscores are allowed in DNS names', regex='^([0-9A-Za-z_-]+|\\*)(\\.[0-9A-Za-z_-]+)*\\.?$')])),
('description', models.CharField(blank=True, max_length=200)), ('description', models.CharField(blank=True, max_length=200)),
], ],
options={ options={

View File

@ -24,7 +24,7 @@ class MinPrefixLengthValidator(BaseValidator):
DNSValidator = RegexValidator( DNSValidator = RegexValidator(
regex='^[0-9A-Za-z._-]+$', regex=r'^([0-9A-Za-z_-]+|\*)(\.[0-9A-Za-z_-]+)*\.?$',
message='Only alphanumeric characters, hyphens, periods, and underscores are allowed in DNS names', message='Only alphanumeric characters, asterisks, hyphens, periods, and underscores are allowed in DNS names',
code='invalid' code='invalid'
) )

View File

@ -22,7 +22,9 @@ PARAMS = (
default='', default='',
description="Additional content to display on the login page", description="Additional content to display on the login page",
field_kwargs={ field_kwargs={
'widget': forms.Textarea(), 'widget': forms.Textarea(
attrs={'class': 'vLargeTextField'}
),
}, },
), ),
ConfigParam( ConfigParam(
@ -31,7 +33,9 @@ PARAMS = (
default='', default='',
description="Additional content to display at the top of every page", description="Additional content to display at the top of every page",
field_kwargs={ field_kwargs={
'widget': forms.Textarea(), 'widget': forms.Textarea(
attrs={'class': 'vLargeTextField'}
),
}, },
), ),
ConfigParam( ConfigParam(
@ -40,7 +44,9 @@ PARAMS = (
default='', default='',
description="Additional content to display at the bottom of every page", description="Additional content to display at the bottom of every page",
field_kwargs={ field_kwargs={
'widget': forms.Textarea(), 'widget': forms.Textarea(
attrs={'class': 'vLargeTextField'}
),
}, },
), ),
@ -109,7 +115,12 @@ PARAMS = (
label='Custom validators', label='Custom validators',
default={}, default={},
description="Custom validation rules (JSON)", description="Custom validation rules (JSON)",
field=forms.JSONField field=forms.JSONField,
field_kwargs={
'widget': forms.Textarea(
attrs={'class': 'vLargeTextField'}
),
},
), ),
# NAPALM # NAPALM
@ -137,7 +148,12 @@ PARAMS = (
label='NAPALM arguments', label='NAPALM arguments',
default={}, default={},
description="Additional arguments to pass when invoking a NAPALM driver (as JSON data)", description="Additional arguments to pass when invoking a NAPALM driver (as JSON data)",
field=forms.JSONField field=forms.JSONField,
field_kwargs={
'widget': forms.Textarea(
attrs={'class': 'vLargeTextField'}
),
},
), ),
# User preferences # User preferences

Binary file not shown.

Binary file not shown.

View File

@ -557,10 +557,13 @@ export class APISelect {
private async handleSearch(event: Event) { private async handleSearch(event: Event) {
const { value: q } = event.target as HTMLInputElement; const { value: q } = event.target as HTMLInputElement;
const url = queryString.stringifyUrl({ url: this.queryUrl, query: { q } }); const url = queryString.stringifyUrl({ url: this.queryUrl, query: { q } });
if (!url.includes(`{{`)) {
await this.fetchOptions(url, 'merge'); await this.fetchOptions(url, 'merge');
this.slim.data.search(q); this.slim.data.search(q);
this.slim.render(); this.slim.render();
} }
return;
}
/** /**
* Determine if the user has scrolled to the bottom of the options list. If so, try to load * Determine if the user has scrolled to the bottom of the options list. If so, try to load

View File

@ -443,6 +443,13 @@
{% include 'inc/panel_table.html' with table=vlan_table heading="VLANs" %} {% include 'inc/panel_table.html' with table=vlan_table heading="VLANs" %}
</div> </div>
</div> </div>
{% if object.is_bridge %}
<div class="row mb-3">
<div class="col col-md-12">
{% include 'inc/panel_table.html' with table=bridge_interfaces_table heading="Bridge Interfaces" %}
</div>
</div>
{% endif %}
<div class="row mb-3"> <div class="row mb-3">
<div class="col col-md-12"> <div class="col col-md-12">
{% include 'inc/panel_table.html' with table=child_interfaces_table heading="Child Interfaces" %} {% include 'inc/panel_table.html' with table=child_interfaces_table heading="Child Interfaces" %}

View File

@ -7,13 +7,13 @@
<p> <p>
<i class="mdi mdi-alert"></i> <strong>Missing required packages</strong> - This installation of NetBox might be <i class="mdi mdi-alert"></i> <strong>Missing required packages</strong> - This installation of NetBox might be
missing one or more required Python packages. These packages are listed in <code>requirements.txt</code> and missing one or more required Python packages. These packages are listed in <code>requirements.txt</code> and
<code>local_requirements.txt</code>, and are normally installed as part of the installation or upgrade process. <code>local_requirements.txt</code>, and are normally installed as part of the installation or upgrade process. To
To verify installed packages, run <code>pip freeze</code> from the console and compare the output to the list of verify installed packages, run <code>pip freeze</code> from the console and compare the output to the list of
required packages. required packages.
</p> </p>
<p> <p>
<i class="mdi mdi-alert"></i> <strong>WSGI service not restarted after upgrade</strong> - If this installation <i class="mdi mdi-alert"></i> <strong>WSGI service not restarted after upgrade</strong> - If this installation has
has recently been upgraded, check that the WSGI service (e.g. gunicorn or uWSGI) has been restarted. This recently been upgraded, check that the WSGI service (e.g. gunicorn or uWSGI) has been restarted. This ensures that
ensures that the new code is running. the new code is running.
</p> </p>
{% endblock message %} {% endblock message %}

View File

@ -6,7 +6,7 @@
</p> </p>
<p> <p>
<i class="mdi mdi-alert"></i> <strong>Insufficient write permission to the media root</strong> - The configured <i class="mdi mdi-alert"></i> <strong>Insufficient write permission to the media root</strong> - The configured
media root is <code>{{ settings.MEDIA_ROOT }}</code>. Ensure that the user NetBox runs as has access to write media root is <code>{{ settings.MEDIA_ROOT }}</code>. Ensure that the user NetBox runs as has access to write files
files to all locations within this path. to all locations within this path.
</p> </p>
{% endblock message %} {% endblock message %}

View File

@ -5,13 +5,13 @@
A database programming error was detected while processing this request. Common causes include the following: A database programming error was detected while processing this request. Common causes include the following:
</p> </p>
<p> <p>
<i class="mdi mdi-alert"></i> <strong>Database migrations missing</strong> - When upgrading to a new NetBox release, the upgrade script must <i class="mdi mdi-alert"></i> <strong>Database migrations missing</strong> - When upgrading to a new NetBox release,
be run to apply any new database migrations. You can run migrations manually by executing the upgrade script must be run to apply any new database migrations. You can run migrations manually by executing
<code>python3 manage.py migrate</code> from the command line. <code>python3 manage.py migrate</code> from the command line.
</p> </p>
<p> <p>
<i class="mdi mdi-alert"></i> <strong>Unsupported PostgreSQL version</strong> - Ensure that PostgreSQL version 9.6 or higher is in use. You <i class="mdi mdi-alert"></i> <strong>Unsupported PostgreSQL version</strong> - Ensure that PostgreSQL version 10
can check this by connecting to the database using NetBox's credentials and issuing a query for or later is in use. You can check this by connecting to the database using NetBox's credentials and issuing a query
<code>SELECT VERSION()</code>. for <code>SELECT VERSION()</code>.
</p> </p>
{% endblock message %} {% endblock message %}