Feature: Add chinese translation to db and view

Add chinese translation code to netbox

Signed-off-by: yj.bai <bai.yongjun@99cloud.net>
This commit is contained in:
yj.bai 2020-08-05 19:24:32 +08:00
parent 9c3b67048a
commit e1441537ef
48 changed files with 2916 additions and 892 deletions

View File

@ -1,9 +1,10 @@
from django.apps import AppConfig
from django.utils.translation import gettext as _
class CircuitsConfig(AppConfig):
name = "circuits"
verbose_name = "Circuits"
verbose_name = _('Circuits')
def ready(self):
import circuits.signals

View File

@ -1,5 +1,6 @@
import django_filters
from django.db.models import Q
from django.utils.translation import gettext as _
from dcim.models import Region, Site
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
@ -21,31 +22,31 @@ __all__ = (
class ProviderFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='circuits__terminations__site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='circuits__terminations__site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='circuits__terminations__site',
queryset=Site.objects.all(),
label='Site',
label=_('Site'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='circuits__terminations__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
tag = TagFilter()
@ -75,27 +76,27 @@ class CircuitTypeFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
provider_id = django_filters.ModelMultipleChoiceFilter(
queryset=Provider.objects.all(),
label='Provider (ID)',
label=_('Provider (ID)'),
)
provider = django_filters.ModelMultipleChoiceFilter(
field_name='provider__slug',
queryset=Provider.objects.all(),
to_field_name='slug',
label='Provider (slug)',
label=_('Provider (slug)'),
)
type_id = django_filters.ModelMultipleChoiceFilter(
queryset=CircuitType.objects.all(),
label='Circuit type (ID)',
label=_('Circuit type (ID)'),
)
type = django_filters.ModelMultipleChoiceFilter(
field_name='type__slug',
queryset=CircuitType.objects.all(),
to_field_name='slug',
label='Circuit type (slug)',
label=_('Circuit type (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=CircuitStatusChoices,
@ -104,26 +105,26 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, Cr
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='terminations__site',
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='terminations__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='terminations__site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='terminations__site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
tag = TagFilter()
@ -147,21 +148,21 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, Cr
class CircuitTerminationFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
circuit_id = django_filters.ModelMultipleChoiceFilter(
queryset=Circuit.objects.all(),
label='Circuit',
label=_('Circuit'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
class Meta:

View File

@ -1,4 +1,5 @@
from django import forms
from django.utils.translation import gettext as _
from dcim.models import Region, Site
from extras.forms import (
@ -64,30 +65,30 @@ class ProviderBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdi
)
asn = forms.IntegerField(
required=False,
label='ASN'
label=_('ASN')
)
account = forms.CharField(
max_length=30,
required=False,
label='Account number'
label=_('Account number')
)
portal_url = forms.URLField(
required=False,
label='Portal'
label=_('Portal')
)
noc_contact = forms.CharField(
required=False,
widget=SmallTextarea,
label='NOC contact'
label=_('NOC contact')
)
admin_contact = forms.CharField(
required=False,
widget=SmallTextarea,
label='Admin contact'
label=_('Admin contact')
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
label=_('Comments')
)
class Meta:
@ -100,7 +101,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Provider
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@ -123,7 +124,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
)
asn = forms.IntegerField(
required=False,
label='ASN'
label=_('ASN')
)
tag = TagFilterField(model)
@ -149,7 +150,7 @@ class CircuitTypeCSVForm(CSVModelForm):
model = CircuitType
fields = CircuitType.csv_headers
help_texts = {
'name': 'Name of circuit type',
'name': _('Name of circuit type'),
}
@ -189,23 +190,23 @@ class CircuitCSVForm(CustomFieldModelCSVForm):
provider = CSVModelChoiceField(
queryset=Provider.objects.all(),
to_field_name='name',
help_text='Assigned provider'
help_text=_('Assigned provider')
)
type = CSVModelChoiceField(
queryset=CircuitType.objects.all(),
to_field_name='name',
help_text='Type of circuit'
help_text=_('Type of circuit')
)
status = CSVChoiceField(
choices=CircuitStatusChoices,
required=False,
help_text='Operational status'
help_text=_('Operational status')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned tenant'
help_text=_('Assigned tenant')
)
class Meta:
@ -240,7 +241,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
)
commit_rate = forms.IntegerField(
required=False,
label='Commit rate (Kbps)'
label=_('Commit rate (Kbps)')
)
description = forms.CharField(
max_length=100,
@ -248,7 +249,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
label=_('Comments')
)
class Meta:
@ -264,7 +265,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
]
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
type = DynamicModelMultipleChoiceField(
queryset=CircuitType.objects.all(),
@ -309,7 +310,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
commit_rate = forms.IntegerField(
required=False,
min_value=0,
label='Commit rate (Kbps)'
label=_('Commit rate (Kbps)')
)
tag = TagFilterField(model)

View File

@ -1,6 +1,7 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.urls import reverse
from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.constants import CONNECTION_STATUS_CHOICES
@ -38,25 +39,25 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
asn = ASNField(
blank=True,
null=True,
verbose_name='ASN',
help_text='32-bit autonomous system number'
verbose_name=_('ASN'),
help_text=_('32-bit autonomous system number')
)
account = models.CharField(
max_length=30,
blank=True,
verbose_name='Account number'
verbose_name=_('Account number')
)
portal_url = models.URLField(
blank=True,
verbose_name='Portal URL'
verbose_name=_('Portal URL')
)
noc_contact = models.TextField(
blank=True,
verbose_name='NOC contact'
verbose_name=_('NOC contact')
)
admin_contact = models.TextField(
blank=True,
verbose_name='Admin contact'
verbose_name=_('Admin contact')
)
comments = models.TextField(
blank=True
@ -143,7 +144,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
"""
cid = models.CharField(
max_length=50,
verbose_name='Circuit ID'
verbose_name=_('Circuit ID')
)
provider = models.ForeignKey(
to='circuits.Provider',
@ -170,7 +171,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
install_date = models.DateField(
blank=True,
null=True,
verbose_name='Date installed'
verbose_name=_('Date installed')
)
commit_rate = models.PositiveIntegerField(
blank=True,
@ -258,7 +259,7 @@ class CircuitTermination(CableTermination):
term_side = models.CharField(
max_length=1,
choices=CircuitTerminationSideChoices,
verbose_name='Termination'
verbose_name=_('Termination')
)
site = models.ForeignKey(
to='dcim.Site',
@ -277,23 +278,23 @@ class CircuitTermination(CableTermination):
blank=True
)
port_speed = models.PositiveIntegerField(
verbose_name='Port speed (Kbps)'
verbose_name=_('Port speed (Kbps)')
)
upstream_speed = models.PositiveIntegerField(
blank=True,
null=True,
verbose_name='Upstream speed (Kbps)',
help_text='Upstream speed, if different from port speed'
verbose_name=_('Upstream speed (Kbps)'),
help_text=_('Upstream speed, if different from port speed')
)
xconnect_id = models.CharField(
max_length=50,
blank=True,
verbose_name='Cross-connect ID'
verbose_name=_('Cross-connect ID')
)
pp_info = models.CharField(
max_length=100,
blank=True,
verbose_name='Patch panel/port(s)'
verbose_name=_('Patch panel/port(s)')
)
description = models.CharField(
max_length=200,

View File

@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from django.utils.translation import gettext as _
from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, TagColumn, ToggleColumn
@ -29,7 +30,7 @@ class ProviderTable(BaseTable):
name = tables.LinkColumn()
circuit_count = tables.Column(
accessor=Accessor('count_circuits'),
verbose_name='Circuits'
verbose_name=_('Circuits')
)
tags = TagColumn(
url_name='circuits:provider_list'
@ -51,7 +52,7 @@ class CircuitTypeTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
circuit_count = tables.Column(
verbose_name='Circuits'
verbose_name=_('Circuits')
)
actions = tables.TemplateColumn(
template_code=CIRCUITTYPE_ACTIONS,
@ -72,7 +73,7 @@ class CircuitTypeTable(BaseTable):
class CircuitTable(BaseTable):
pk = ToggleColumn()
cid = tables.LinkColumn(
verbose_name='ID'
verbose_name=_('ID')
)
provider = tables.LinkColumn(
viewname='circuits:provider',
@ -85,10 +86,10 @@ class CircuitTable(BaseTable):
template_code=COL_TENANT
)
a_side = tables.Column(
verbose_name='A Side'
verbose_name=_('A Side')
)
z_side = tables.Column(
verbose_name='Z Side'
verbose_name=_('Z Side')
)
tags = TagColumn(
url_name='circuits:circuit_list'

View File

@ -5,6 +5,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction
from django.db.models import Count, OuterRef, Subquery
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import gettext as _
from django.views.generic import View
from django_tables2 import RequestConfig
@ -250,7 +251,7 @@ def circuit_terminations_swap(request, pk):
else:
termination_z.term_side = 'A'
termination_z.save()
messages.success(request, "Swapped terminations for circuit {}.".format(circuit))
messages.success(request, _('Swapped terminations for circuit {}.').format(circuit))
return redirect('circuits:circuit', pk=circuit.pk)
else:

View File

@ -1,9 +1,9 @@
from django.apps import AppConfig
from django.utils.translation import gettext as _
class DCIMConfig(AppConfig):
name = "dcim"
verbose_name = "DCIM"
verbose_name = _('DCIM')
def ready(self):

View File

@ -1,5 +1,6 @@
import django_filters
from django.contrib.auth.models import User
from django.utils.translation import gettext as _
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet
from tenancy.filters import TenancyFilterSet
@ -63,13 +64,13 @@ __all__ = (
class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=Region.objects.all(),
label='Parent region (ID)',
label=_('Parent region (ID)'),
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=Region.objects.all(),
to_field_name='slug',
label='Parent region (slug)',
label=_('Parent region (slug)'),
)
class Meta:
@ -80,7 +81,7 @@ class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
status = django_filters.MultipleChoiceFilter(
choices=SiteStatusChoices,
@ -90,14 +91,14 @@ class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
queryset=Region.objects.all(),
field_name='region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
tag = TagFilter()
@ -134,34 +135,34 @@ class RackGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
label='Rack group (ID)',
label=_('Rack group (ID)'),
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=RackGroup.objects.all(),
to_field_name='slug',
label='Rack group (slug)',
label=_('Rack group (slug)'),
)
class Meta:
@ -179,43 +180,43 @@ class RackRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='group',
lookup_expr='in',
label='Rack group (ID)',
label=_('Rack group (ID)'),
)
group = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='group',
lookup_expr='in',
to_field_name='slug',
label='Rack group (slug)',
label=_('Rack group (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=RackStatusChoices,
@ -223,13 +224,13 @@ class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=RackRole.objects.all(),
label='Role (ID)',
label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=RackRole.objects.all(),
to_field_name='slug',
label='Role (slug)',
label=_('Role (slug)'),
)
serial = django_filters.CharFilter(
lookup_expr='iexact'
@ -258,45 +259,45 @@ class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
class RackReservationFilterSet(BaseFilterSet, TenancyFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
rack_id = django_filters.ModelMultipleChoiceFilter(
queryset=Rack.objects.all(),
label='Rack (ID)',
label=_('Rack (ID)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack__site',
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='rack__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='rack__group',
lookup_expr='in',
label='Rack group (ID)',
label=_('Rack group (ID)'),
)
group = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='rack__group',
lookup_expr='in',
to_field_name='slug',
label='Rack group (slug)',
label=_('Rack group (slug)'),
)
user_id = django_filters.ModelMultipleChoiceFilter(
queryset=User.objects.all(),
label='User (ID)',
label=_('User (ID)'),
)
user = django_filters.ModelMultipleChoiceFilter(
field_name='user',
queryset=User.objects.all(),
to_field_name='username',
label='User (name)',
label=_('User (name)'),
)
class Meta:
@ -324,45 +325,45 @@ class ManufacturerFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class DeviceTypeFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
label='Manufacturer (ID)',
label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
label='Manufacturer (slug)',
label=_('Manufacturer (slug)'),
)
console_ports = django_filters.BooleanFilter(
method='_console_ports',
label='Has console ports',
label=_('Has console ports'),
)
console_server_ports = django_filters.BooleanFilter(
method='_console_server_ports',
label='Has console server ports',
label=_('Has console server ports'),
)
power_ports = django_filters.BooleanFilter(
method='_power_ports',
label='Has power ports',
label=_('Has power ports'),
)
power_outlets = django_filters.BooleanFilter(
method='_power_outlets',
label='Has power outlets',
label=_('Has power outlets'),
)
interfaces = django_filters.BooleanFilter(
method='_interfaces',
label='Has interfaces',
label=_('Has interfaces'),
)
pass_through_ports = django_filters.BooleanFilter(
method='_pass_through_ports',
label='Has pass-through ports',
label=_('Has pass-through ports'),
)
device_bays = django_filters.BooleanFilter(
method='_device_bays',
label='Has device bays',
label=_('Has device bays'),
)
tag = TagFilter()
@ -411,7 +412,7 @@ class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet):
devicetype_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceType.objects.all(),
field_name='device_type_id',
label='Device type (ID)',
label=_('Device type (ID)'),
)
@ -482,13 +483,13 @@ class PlatformFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer',
queryset=Manufacturer.objects.all(),
label='Manufacturer (ID)',
label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
label='Manufacturer (slug)',
label=_('Manufacturer (slug)'),
)
class Meta:
@ -505,87 +506,87 @@ class DeviceFilterSet(
):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
field_name='device_type__manufacturer',
queryset=Manufacturer.objects.all(),
label='Manufacturer (ID)',
label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='device_type__manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
label='Manufacturer (slug)',
label=_('Manufacturer (slug)'),
)
device_type_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceType.objects.all(),
label='Device type (ID)',
label=_('Device type (ID)'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
field_name='device_role_id',
queryset=DeviceRole.objects.all(),
label='Role (ID)',
label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='device_role__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
label='Role (slug)',
label=_('Role (slug)'),
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label='Platform (ID)',
label=_('Platform (ID)'),
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platform__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
label='Platform (slug)',
label=_('Platform (slug)'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
label=_('Site name (slug)'),
)
rack_group_id = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='rack__group',
lookup_expr='in',
label='Rack group (ID)',
label=_('Rack group (ID)'),
)
rack_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack',
queryset=Rack.objects.all(),
label='Rack (ID)',
label=_('Rack (ID)'),
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
queryset=Cluster.objects.all(),
label='VM cluster (ID)',
label=_('VM cluster (ID)'),
)
model = django_filters.ModelMultipleChoiceFilter(
field_name='device_type__slug',
queryset=DeviceType.objects.all(),
to_field_name='slug',
label='Device model (slug)',
label=_('Device model (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=DeviceStatusChoices,
@ -593,55 +594,55 @@ class DeviceFilterSet(
)
is_full_depth = django_filters.BooleanFilter(
field_name='device_type__is_full_depth',
label='Is full depth',
label=_('Is full depth'),
)
mac_address = MultiValueMACAddressFilter(
field_name='interfaces__mac_address',
label='MAC address',
label=_('MAC address'),
)
serial = django_filters.CharFilter(
lookup_expr='iexact'
)
has_primary_ip = django_filters.BooleanFilter(
method='_has_primary_ip',
label='Has a primary IP',
label=_('Has a primary IP'),
)
virtual_chassis_id = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_chassis',
queryset=VirtualChassis.objects.all(),
label='Virtual chassis (ID)',
label=_('Virtual chassis (ID)'),
)
virtual_chassis_member = django_filters.BooleanFilter(
method='_virtual_chassis_member',
label='Is a virtual chassis member'
label=_('Is a virtual chassis member')
)
console_ports = django_filters.BooleanFilter(
method='_console_ports',
label='Has console ports',
label=_('Has console ports'),
)
console_server_ports = django_filters.BooleanFilter(
method='_console_server_ports',
label='Has console server ports',
label=_('Has console server ports'),
)
power_ports = django_filters.BooleanFilter(
method='_power_ports',
label='Has power ports',
label=_('Has power ports'),
)
power_outlets = django_filters.BooleanFilter(
method='_power_outlets',
label='Has power outlets',
label=_('Has power outlets'),
)
interfaces = django_filters.BooleanFilter(
method='_interfaces',
label='Has interfaces',
label=_('Has interfaces'),
)
pass_through_ports = django_filters.BooleanFilter(
method='_pass_through_ports',
label='Has pass-through ports',
label=_('Has pass-through ports'),
)
device_bays = django_filters.BooleanFilter(
method='_device_bays',
label='Has device bays',
label=_('Has device bays'),
)
tag = TagFilter()
@ -703,41 +704,41 @@ class DeviceFilterSet(
class DeviceComponentFilterSet(django_filters.FilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site',
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
label=_('Site name (slug)'),
)
device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label='Device (ID)',
label=_('Device (ID)'),
)
device = django_filters.ModelMultipleChoiceFilter(
field_name='device__name',
queryset=Device.objects.all(),
to_field_name='name',
label='Device (name)',
label=_('Device (name)'),
)
tag = TagFilter()
@ -817,19 +818,19 @@ class PowerOutletFilterSet(BaseFilterSet, DeviceComponentFilterSet):
class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
# Override device and device_id filters from DeviceComponentFilterSet to match against any peer virtual chassis
# members
device = MultiValueCharFilter(
method='filter_device',
field_name='name',
label='Device',
label=_('Device'),
)
device_id = MultiValueNumberFilter(
method='filter_device_id',
field_name='pk',
label='Device (ID)',
label=_('Device (ID)'),
)
cabled = django_filters.BooleanFilter(
field_name='cable',
@ -838,22 +839,22 @@ class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet):
)
kind = django_filters.CharFilter(
method='filter_kind',
label='Kind of interface',
label=_('Kind of interface'),
)
lag_id = django_filters.ModelMultipleChoiceFilter(
field_name='lag',
queryset=Interface.objects.all(),
label='LAG interface (ID)',
label=_('LAG interface (ID)'),
)
mac_address = MultiValueMACAddressFilter()
tag = TagFilter()
vlan_id = django_filters.CharFilter(
method='filter_vlan_id',
label='Assigned VLAN'
label=_('Assigned VLAN')
)
vlan = django_filters.CharFilter(
method='filter_vlan',
label='Assigned VID'
label=_('Assigned VID')
)
type = django_filters.MultipleChoiceFilter(
choices=InterfaceTypeChoices,
@ -946,54 +947,54 @@ class DeviceBayFilterSet(BaseFilterSet, DeviceComponentFilterSet):
class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='device__site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='device__site',
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='device__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
label=_('Site name (slug)'),
)
device_id = django_filters.ModelChoiceFilter(
queryset=Device.objects.all(),
label='Device (ID)',
label=_('Device (ID)'),
)
device = django_filters.ModelChoiceFilter(
queryset=Device.objects.all(),
to_field_name='name',
label='Device (name)',
label=_('Device (name)'),
)
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=InventoryItem.objects.all(),
label='Parent inventory item (ID)',
label=_('Parent inventory item (ID)'),
)
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
queryset=Manufacturer.objects.all(),
label='Manufacturer (ID)',
label=_('Manufacturer (ID)'),
)
manufacturer = django_filters.ModelMultipleChoiceFilter(
field_name='manufacturer__slug',
queryset=Manufacturer.objects.all(),
to_field_name='slug',
label='Manufacturer (slug)',
label=_('Manufacturer (slug)'),
)
serial = django_filters.CharFilter(
lookup_expr='iexact'
@ -1019,42 +1020,42 @@ class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
class VirtualChassisFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='master__site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='master__site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='master__site',
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='master__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
label=_('Site name (slug)'),
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
field_name='master__tenant',
queryset=Tenant.objects.all(),
label='Tenant (ID)',
label=_('Tenant (ID)'),
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='master__tenant__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
label=_('Tenant (slug)'),
)
tag = TagFilter()
@ -1075,7 +1076,7 @@ class VirtualChassisFilterSet(BaseFilterSet):
class CableFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
type = django_filters.MultipleChoiceFilter(
choices=CableTypeChoices
@ -1138,7 +1139,7 @@ class CableFilterSet(BaseFilterSet):
class ConsoleConnectionFilterSet(BaseFilterSet):
site = django_filters.CharFilter(
method='filter_site',
label='Site (slug)',
label=_('Site (slug)'),
)
device_id = MultiValueNumberFilter(
method='filter_device'
@ -1169,7 +1170,7 @@ class ConsoleConnectionFilterSet(BaseFilterSet):
class PowerConnectionFilterSet(BaseFilterSet):
site = django_filters.CharFilter(
method='filter_site',
label='Site (slug)',
label=_('Site (slug)'),
)
device_id = MultiValueNumberFilter(
method='filter_device'
@ -1200,7 +1201,7 @@ class PowerConnectionFilterSet(BaseFilterSet):
class InterfaceConnectionFilterSet(BaseFilterSet):
site = django_filters.CharFilter(
method='filter_site',
label='Site (slug)',
label=_('Site (slug)'),
)
device_id = MultiValueNumberFilter(
method='filter_device'
@ -1234,36 +1235,36 @@ class InterfaceConnectionFilterSet(BaseFilterSet):
class PowerPanelFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
label=_('Site name (slug)'),
)
rack_group_id = TreeNodeMultipleChoiceFilter(
queryset=RackGroup.objects.all(),
field_name='rack_group',
lookup_expr='in',
label='Rack group (ID)',
label=_('Rack group (ID)'),
)
class Meta:
@ -1282,40 +1283,40 @@ class PowerPanelFilterSet(BaseFilterSet):
class PowerFeedFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='power_panel__site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='power_panel__site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='power_panel__site',
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='power_panel__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site name (slug)',
label=_('Site name (slug)'),
)
power_panel_id = django_filters.ModelMultipleChoiceFilter(
queryset=PowerPanel.objects.all(),
label='Power panel (ID)',
label=_('Power panel (ID)'),
)
rack_id = django_filters.ModelMultipleChoiceFilter(
field_name='rack',
queryset=Rack.objects.all(),
label='Rack (ID)',
label=_('Rack (ID)'),
)
tag = TagFilter()

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ from django.db import models
from django.db.models import Count, F, ProtectedError, Sum
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
from mptt.models import MPTTModel, TreeForeignKey
from taggit.managers import TaggableManager
from timezone_field import TimeZoneField
@ -182,13 +183,13 @@ class Site(ChangeLoggedModel, CustomFieldModel):
facility = models.CharField(
max_length=50,
blank=True,
help_text='Local facility ID or description'
help_text=_('Local facility ID or description')
)
asn = ASNField(
blank=True,
null=True,
verbose_name='ASN',
help_text='32-bit autonomous system number'
verbose_name=_('ASN'),
help_text=_('32-bit autonomous system number')
)
time_zone = TimeZoneField(
blank=True
@ -210,14 +211,14 @@ class Site(ChangeLoggedModel, CustomFieldModel):
decimal_places=6,
blank=True,
null=True,
help_text='GPS coordinate (latitude)'
help_text=_('GPS coordinate (latitude)')
)
longitude = models.DecimalField(
max_digits=9,
decimal_places=6,
blank=True,
null=True,
help_text='GPS coordinate (longitude)'
help_text=_('GPS coordinate (longitude)')
)
contact_name = models.CharField(
max_length=50,
@ -229,7 +230,7 @@ class Site(ChangeLoggedModel, CustomFieldModel):
)
contact_email = models.EmailField(
blank=True,
verbose_name='Contact E-mail'
verbose_name=_('Contact E-mail')
)
comments = models.TextField(
blank=True
@ -428,8 +429,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
max_length=50,
blank=True,
null=True,
verbose_name='Facility ID',
help_text='Locally-assigned identifier'
verbose_name=_('Facility ID'),
help_text=_('Locally-assigned identifier')
)
site = models.ForeignKey(
to='dcim.Site',
@ -442,7 +443,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
related_name='racks',
blank=True,
null=True,
help_text='Assigned group'
help_text=_('Assigned group')
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@ -462,53 +463,53 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
related_name='racks',
blank=True,
null=True,
help_text='Functional role'
help_text=_('Functional role')
)
serial = models.CharField(
max_length=50,
blank=True,
verbose_name='Serial number'
verbose_name=_('Serial number')
)
asset_tag = models.CharField(
max_length=50,
blank=True,
null=True,
unique=True,
verbose_name='Asset tag',
help_text='A unique tag used to identify this rack'
verbose_name=_('Asset tag'),
help_text=_('A unique tag used to identify this rack')
)
type = models.CharField(
choices=RackTypeChoices,
max_length=50,
blank=True,
verbose_name='Type'
verbose_name=_('Type')
)
width = models.PositiveSmallIntegerField(
choices=RackWidthChoices,
default=RackWidthChoices.WIDTH_19IN,
verbose_name='Width',
help_text='Rail-to-rail width'
verbose_name=_('Width'),
help_text=_('Rail-to-rail width')
)
u_height = models.PositiveSmallIntegerField(
default=RACK_U_HEIGHT_DEFAULT,
verbose_name='Height (U)',
verbose_name=_('Height (U)'),
validators=[MinValueValidator(1), MaxValueValidator(100)],
help_text='Height in rack units'
help_text=_('Height in rack units')
)
desc_units = models.BooleanField(
default=False,
verbose_name='Descending units',
help_text='Units are numbered top-to-bottom'
verbose_name=_('Descending units'),
help_text=_('Units are numbered top-to-bottom')
)
outer_width = models.PositiveSmallIntegerField(
blank=True,
null=True,
help_text='Outer dimension of rack (width)'
help_text=_('Outer dimension of rack (width)')
)
outer_depth = models.PositiveSmallIntegerField(
blank=True,
null=True,
help_text='Outer dimension of rack (depth)'
help_text=_('Outer dimension of rack (depth)')
)
outer_unit = models.CharField(
max_length=50,
@ -949,24 +950,24 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
part_number = models.CharField(
max_length=50,
blank=True,
help_text='Discrete part number (optional)'
help_text=_('Discrete part number (optional)')
)
u_height = models.PositiveSmallIntegerField(
default=1,
verbose_name='Height (U)'
verbose_name=_('Height (U)')
)
is_full_depth = models.BooleanField(
default=True,
verbose_name='Is full depth',
help_text='Device consumes both front and rear rack faces'
verbose_name=_('Is full depth'),
help_text=_('Device consumes both front and rear rack faces')
)
subdevice_role = models.CharField(
max_length=50,
choices=SubdeviceRoleChoices,
blank=True,
verbose_name='Parent/child status',
help_text='Parent devices house child devices in device bays. Leave blank '
'if this device type is neither a parent nor a child.'
verbose_name=_('Parent/child status'),
help_text=_('Parent devices house child devices in device bays. Leave blank '
'if this device type is neither a parent nor a child.')
)
front_image = models.ImageField(
upload_to='devicetype-images',
@ -1200,8 +1201,8 @@ class DeviceRole(ChangeLoggedModel):
)
vm_role = models.BooleanField(
default=True,
verbose_name='VM Role',
help_text='Virtual machines may be assigned to this role'
verbose_name=_('VM Role'),
help_text=_('Virtual machines may be assigned to this role')
)
description = models.CharField(
max_length=200,
@ -1246,19 +1247,19 @@ class Platform(ChangeLoggedModel):
related_name='platforms',
blank=True,
null=True,
help_text='Optionally limit this platform to devices of a certain manufacturer'
help_text=_('Optionally limit this platform to devices of a certain manufacturer')
)
napalm_driver = models.CharField(
max_length=50,
blank=True,
verbose_name='NAPALM driver',
help_text='The name of the NAPALM driver to use when interacting with devices'
verbose_name=_('NAPALM driver'),
help_text=_('The name of the NAPALM driver to use when interacting with devices')
)
napalm_args = JSONField(
blank=True,
null=True,
verbose_name='NAPALM arguments',
help_text='Additional arguments to pass when initiating the NAPALM driver (JSON format)'
verbose_name=_('NAPALM arguments'),
help_text=_('Additional arguments to pass when initiating the NAPALM driver (JSON format)')
)
description = models.CharField(
max_length=200,
@ -1338,15 +1339,15 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
serial = models.CharField(
max_length=50,
blank=True,
verbose_name='Serial number'
verbose_name=_('Serial number')
)
asset_tag = models.CharField(
max_length=50,
blank=True,
null=True,
unique=True,
verbose_name='Asset tag',
help_text='A unique tag used to identify this device'
verbose_name=_('Asset tag'),
help_text=_('A unique tag used to identify this device')
)
site = models.ForeignKey(
to='dcim.Site',
@ -1364,14 +1365,14 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
blank=True,
null=True,
validators=[MinValueValidator(1)],
verbose_name='Position (U)',
help_text='The lowest-numbered unit occupied by the device'
verbose_name=_('Position (U)'),
help_text=_('The lowest-numbered unit occupied by the device')
)
face = models.CharField(
max_length=50,
blank=True,
choices=DeviceFaceChoices,
verbose_name='Rack face'
verbose_name=_('Rack face')
)
status = models.CharField(
max_length=50,
@ -1384,7 +1385,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
related_name='primary_ip4_for',
blank=True,
null=True,
verbose_name='Primary IPv4'
verbose_name=_('Primary IPv4')
)
primary_ip6 = models.OneToOneField(
to='ipam.IPAddress',
@ -1392,7 +1393,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
related_name='primary_ip6_for',
blank=True,
null=True,
verbose_name='Primary IPv6'
verbose_name=_('Primary IPv6')
)
cluster = models.ForeignKey(
to='virtualization.Cluster',
@ -1904,7 +1905,7 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
max_utilization = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(100)],
default=POWERFEED_MAX_UTILIZATION_DEFAULT,
help_text="Maximum permissible draw (percentage)"
help_text=_('Maximum permissible draw (percentage)')
)
available_power = models.PositiveIntegerField(
default=0,

View File

@ -1,6 +1,7 @@
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils.translation import gettext as _
from dcim.choices import *
from dcim.constants import *
@ -154,13 +155,13 @@ class PowerPortTemplate(ComponentTemplateModel):
blank=True,
null=True,
validators=[MinValueValidator(1)],
help_text="Maximum power draw (watts)"
help_text=_('Maximum power draw (watts)')
)
allocated_draw = models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1)],
help_text="Allocated power draw (watts)"
help_text=_('Allocated power draw (watts)')
)
class Meta:
@ -213,7 +214,7 @@ class PowerOutletTemplate(ComponentTemplateModel):
max_length=50,
choices=PowerOutletFeedLegChoices,
blank=True,
help_text="Phase (for three-phase feeds)"
help_text=_('Phase (for three-phase feeds)')
)
class Meta:
@ -269,7 +270,7 @@ class InterfaceTemplate(ComponentTemplateModel):
)
mgmt_only = models.BooleanField(
default=False,
verbose_name='Management only'
verbose_name=_('Management only')
)
class Meta:

View File

@ -6,6 +6,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import Sum
from django.urls import reverse
from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.choices import *
@ -252,7 +253,7 @@ class ConsolePort(CableTermination, ComponentModel):
max_length=50,
choices=ConsolePortTypeChoices,
blank=True,
help_text='Physical port type'
help_text=_('Physical port type')
)
connected_endpoint = models.OneToOneField(
to='dcim.ConsoleServerPort',
@ -311,7 +312,7 @@ class ConsoleServerPort(CableTermination, ComponentModel):
max_length=50,
choices=ConsolePortTypeChoices,
blank=True,
help_text='Physical port type'
help_text=_('Physical port type')
)
connection_status = models.NullBooleanField(
choices=CONNECTION_STATUS_CHOICES,
@ -363,19 +364,19 @@ class PowerPort(CableTermination, ComponentModel):
max_length=50,
choices=PowerPortTypeChoices,
blank=True,
help_text='Physical port type'
help_text=_('Physical port type')
)
maximum_draw = models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1)],
help_text="Maximum power draw (watts)"
help_text=_('Maximum power draw (watts)')
)
allocated_draw = models.PositiveSmallIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1)],
help_text="Allocated power draw (watts)"
help_text=_('Allocated power draw (watts)')
)
_connected_poweroutlet = models.OneToOneField(
to='dcim.PowerOutlet',
@ -523,7 +524,7 @@ class PowerOutlet(CableTermination, ComponentModel):
max_length=50,
choices=PowerOutletTypeChoices,
blank=True,
help_text='Physical port type'
help_text=_('Physical port type')
)
power_port = models.ForeignKey(
to='dcim.PowerPort',
@ -536,7 +537,7 @@ class PowerOutlet(CableTermination, ComponentModel):
max_length=50,
choices=PowerOutletFeedLegChoices,
blank=True,
help_text="Phase (for three-phase feeds)"
help_text=_('Phase (for three-phase feeds)')
)
connection_status = models.NullBooleanField(
choices=CONNECTION_STATUS_CHOICES,
@ -629,7 +630,7 @@ class Interface(CableTermination, ComponentModel):
related_name='member_interfaces',
null=True,
blank=True,
verbose_name='Parent LAG'
verbose_name=_('Parent LAG')
)
type = models.CharField(
max_length=50,
@ -641,18 +642,18 @@ class Interface(CableTermination, ComponentModel):
mac_address = MACAddressField(
null=True,
blank=True,
verbose_name='MAC Address'
verbose_name=_('MAC Address')
)
mtu = models.PositiveIntegerField(
blank=True,
null=True,
validators=[MinValueValidator(1), MaxValueValidator(65536)],
verbose_name='MTU'
verbose_name=_('MTU')
)
mgmt_only = models.BooleanField(
default=False,
verbose_name='OOB Management',
help_text='This interface is used only for out-of-band management'
verbose_name=_('OOB Management'),
help_text=_('This interface is used only for out-of-band management')
)
mode = models.CharField(
max_length=50,
@ -665,13 +666,13 @@ class Interface(CableTermination, ComponentModel):
related_name='interfaces_as_untagged',
null=True,
blank=True,
verbose_name='Untagged VLAN'
verbose_name=_('Untagged VLAN')
)
tagged_vlans = models.ManyToManyField(
to='ipam.VLAN',
related_name='interfaces_as_tagged',
blank=True,
verbose_name='Tagged VLANs'
verbose_name=_('Tagged VLANs')
)
tags = TaggableManager(through=TaggedItem)
@ -976,7 +977,7 @@ class DeviceBay(ComponentModel):
)
name = models.CharField(
max_length=50,
verbose_name='Name'
verbose_name=_('Name')
)
_name = NaturalOrderingField(
target_field='name',
@ -1056,7 +1057,7 @@ class InventoryItem(ComponentModel):
)
name = models.CharField(
max_length=50,
verbose_name='Name'
verbose_name=_('Name')
)
_name = NaturalOrderingField(
target_field='name',
@ -1072,13 +1073,13 @@ class InventoryItem(ComponentModel):
)
part_id = models.CharField(
max_length=50,
verbose_name='Part ID',
verbose_name=_('Part ID'),
blank=True,
help_text='Manufacturer-assigned part identifier'
help_text=_('Manufacturer-assigned part identifier')
)
serial = models.CharField(
max_length=50,
verbose_name='Serial number',
verbose_name=_('Serial number'),
blank=True
)
asset_tag = models.CharField(
@ -1086,12 +1087,12 @@ class InventoryItem(ComponentModel):
unique=True,
blank=True,
null=True,
verbose_name='Asset tag',
help_text='A unique tag used to identify this item'
verbose_name=_('Asset tag'),
help_text=_('A unique tag used to identify this item')
)
discovered = models.BooleanField(
default=False,
help_text='This item was automatically discovered'
help_text=_('This item was automatically discovered')
)
tags = TaggableManager(through=TaggedItem)

View File

@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from django.utils.translation import gettext as _
from tenancy.tables import COL_TENANT
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ColoredLabelColumn, TagColumn, ToggleColumn
@ -196,7 +197,7 @@ class RegionTable(BaseTable):
orderable=False
)
site_count = tables.Column(
verbose_name='Sites'
verbose_name=_('Sites')
)
actions = tables.TemplateColumn(
template_code=REGION_ACTIONS,
@ -255,10 +256,10 @@ class RackGroupTable(BaseTable):
site = tables.LinkColumn(
viewname='dcim:site',
args=[Accessor('site.slug')],
verbose_name='Site'
verbose_name=_('Site')
)
rack_count = tables.Column(
verbose_name='Racks'
verbose_name=_('Racks')
)
actions = tables.TemplateColumn(
template_code=RACKGROUP_ACTIONS,
@ -315,7 +316,7 @@ class RackTable(BaseTable):
role = ColoredLabelColumn()
u_height = tables.TemplateColumn(
template_code="{{ record.u_height }}U",
verbose_name='Height'
verbose_name=_('Height')
)
class Meta(BaseTable.Meta):
@ -330,17 +331,17 @@ class RackTable(BaseTable):
class RackDetailTable(RackTable):
device_count = tables.TemplateColumn(
template_code=RACK_DEVICE_COUNT,
verbose_name='Devices'
verbose_name=_('Devices')
)
get_utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
orderable=False,
verbose_name='Space'
verbose_name=_('Space')
)
get_power_utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
orderable=False,
verbose_name='Power'
verbose_name=_('Power')
)
tags = TagColumn(
url_name='dcim:rack_list'
@ -382,7 +383,7 @@ class RackReservationTable(BaseTable):
)
unit_list = tables.Column(
orderable=False,
verbose_name='Units'
verbose_name=_('Units')
)
actions = tables.TemplateColumn(
template_code=RACKRESERVATION_ACTIONS,
@ -408,13 +409,13 @@ class ManufacturerTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
devicetype_count = tables.Column(
verbose_name='Device Types'
verbose_name=_('Device Types')
)
inventoryitem_count = tables.Column(
verbose_name='Inventory Items'
verbose_name=_('Inventory Items')
)
platform_count = tables.Column(
verbose_name='Platforms'
verbose_name=_('Platforms')
)
slug = tables.Column()
actions = tables.TemplateColumn(
@ -439,14 +440,14 @@ class DeviceTypeTable(BaseTable):
model = tables.LinkColumn(
viewname='dcim:devicetype',
args=[Accessor('pk')],
verbose_name='Device Type'
verbose_name=_('Device Type')
)
is_full_depth = BooleanColumn(
verbose_name='Full Depth'
verbose_name=_('Full Depth')
)
instance_count = tables.TemplateColumn(
template_code=DEVICETYPE_INSTANCES_TEMPLATE,
verbose_name='Instances'
verbose_name=_('Instances')
)
tags = TagColumn(
url_name='dcim:devicetype_list'
@ -608,7 +609,7 @@ class InterfaceImportTable(BaseTable):
virtual_machine = tables.LinkColumn(
viewname='virtualization:virtualmachine',
args=[Accessor('virtual_machine.pk')],
verbose_name='Virtual Machine'
verbose_name=_('Virtual Machine')
)
class Meta(BaseTable.Meta):
@ -626,7 +627,7 @@ class FrontPortTemplateTable(BaseTable):
order_by=('_name',)
)
rear_port_position = tables.Column(
verbose_name='Position'
verbose_name=_('Position')
)
actions = tables.TemplateColumn(
template_code=get_component_template_actions('frontporttemplate'),
@ -706,15 +707,15 @@ class DeviceRoleTable(BaseTable):
pk = ToggleColumn()
device_count = tables.TemplateColumn(
template_code=DEVICEROLE_DEVICE_COUNT,
verbose_name='Devices'
verbose_name=_('Devices')
)
vm_count = tables.TemplateColumn(
template_code=DEVICEROLE_VM_COUNT,
verbose_name='VMs'
verbose_name=_('VMs')
)
color = tables.TemplateColumn(
template_code=COLOR_LABEL,
verbose_name='Label'
verbose_name=_('Label')
)
vm_role = BooleanColumn()
actions = tables.TemplateColumn(
@ -737,11 +738,11 @@ class PlatformTable(BaseTable):
pk = ToggleColumn()
device_count = tables.TemplateColumn(
template_code=PLATFORM_DEVICE_COUNT,
verbose_name='Devices'
verbose_name=_('Devices')
)
vm_count = tables.TemplateColumn(
template_code=PLATFORM_VM_COUNT,
verbose_name='VMs'
verbose_name=_('VMs')
)
actions = tables.TemplateColumn(
template_code=PLATFORM_ACTIONS,
@ -785,28 +786,28 @@ class DeviceTable(BaseTable):
args=[Accessor('rack.pk')]
)
device_role = ColoredLabelColumn(
verbose_name='Role'
verbose_name=_('Role')
)
device_type = tables.LinkColumn(
viewname='dcim:devicetype',
args=[Accessor('device_type.pk')],
verbose_name='Type',
verbose_name=_('Type'),
text=lambda record: record.device_type.display_name
)
primary_ip = tables.TemplateColumn(
template_code=DEVICE_PRIMARY_IP,
orderable=False,
verbose_name='IP Address'
verbose_name=_('IP Address')
)
primary_ip4 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip4.pk')],
verbose_name='IPv4 Address'
verbose_name=_('IPv4 Address')
)
primary_ip6 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip6.pk')],
verbose_name='IPv6 Address'
verbose_name=_('IPv6 Address')
)
cluster = tables.LinkColumn(
viewname='virtualization:cluster',
@ -817,10 +818,10 @@ class DeviceTable(BaseTable):
args=[Accessor('virtual_chassis.pk')]
)
vc_position = tables.Column(
verbose_name='VC Position'
verbose_name=_('VC Position')
)
vc_priority = tables.Column(
verbose_name='VC Priority'
verbose_name=_('VC Priority')
)
tags = TagColumn(
url_name='dcim:device_list'
@ -857,10 +858,10 @@ class DeviceImportTable(BaseTable):
args=[Accessor('rack.pk')]
)
device_role = tables.Column(
verbose_name='Role'
verbose_name=_('Role')
)
device_type = tables.Column(
verbose_name='Type'
verbose_name=_('Type')
)
class Meta(BaseTable.Meta):
@ -1031,29 +1032,29 @@ class CableTable(BaseTable):
id = tables.LinkColumn(
viewname='dcim:cable',
args=[Accessor('pk')],
verbose_name='ID'
verbose_name=_('ID')
)
termination_a_parent = tables.TemplateColumn(
template_code=CABLE_TERMINATION_PARENT,
accessor=Accessor('termination_a'),
orderable=False,
verbose_name='Side A'
verbose_name=_('Side A')
)
termination_a = tables.LinkColumn(
accessor=Accessor('termination_a'),
orderable=False,
verbose_name='Termination A'
verbose_name=_('Termination A')
)
termination_b_parent = tables.TemplateColumn(
template_code=CABLE_TERMINATION_PARENT,
accessor=Accessor('termination_b'),
orderable=False,
verbose_name='Side B'
verbose_name=_('Side B')
)
termination_b = tables.LinkColumn(
accessor=Accessor('termination_b'),
orderable=False,
verbose_name='Termination B'
verbose_name=_('Termination B')
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
@ -1085,17 +1086,17 @@ class ConsoleConnectionTable(BaseTable):
viewname='dcim:device',
accessor=Accessor('connected_endpoint.device'),
args=[Accessor('connected_endpoint.device.pk')],
verbose_name='Console Server'
verbose_name=_('Console Server')
)
connected_endpoint = tables.Column(
verbose_name='Port'
verbose_name=_('Port')
)
device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
name = tables.Column(
verbose_name='Console Port'
verbose_name=_('Console Port')
)
class Meta(BaseTable.Meta):
@ -1109,18 +1110,18 @@ class PowerConnectionTable(BaseTable):
accessor=Accessor('connected_endpoint.device'),
args=[Accessor('connected_endpoint.device.pk')],
order_by='_connected_poweroutlet__device',
verbose_name='PDU'
verbose_name=_('PDU')
)
outlet = tables.Column(
accessor=Accessor('_connected_poweroutlet'),
verbose_name='Outlet'
verbose_name=_('Outlet')
)
device = tables.LinkColumn(
viewname='dcim:device',
args=[Accessor('device.pk')]
)
name = tables.Column(
verbose_name='Power Port'
verbose_name=_('Power Port')
)
class Meta(BaseTable.Meta):
@ -1133,25 +1134,25 @@ class InterfaceConnectionTable(BaseTable):
viewname='dcim:device',
accessor=Accessor('device'),
args=[Accessor('device.pk')],
verbose_name='Device A'
verbose_name=_('Device A')
)
interface_a = tables.LinkColumn(
viewname='dcim:interface',
accessor=Accessor('name'),
args=[Accessor('pk')],
verbose_name='Interface A'
verbose_name=_('Interface A')
)
device_b = tables.LinkColumn(
viewname='dcim:device',
accessor=Accessor('_connected_interface.device'),
args=[Accessor('_connected_interface.device.pk')],
verbose_name='Device B'
verbose_name=_('Device B')
)
interface_b = tables.LinkColumn(
viewname='dcim:interface',
accessor=Accessor('_connected_interface'),
args=[Accessor('_connected_interface.pk')],
verbose_name='Interface B'
verbose_name=_('Interface B')
)
class Meta(BaseTable.Meta):
@ -1195,7 +1196,7 @@ class VirtualChassisTable(BaseTable):
linkify=True
)
member_count = tables.Column(
verbose_name='Members'
verbose_name=_('Members')
)
tags = TagColumn(
url_name='dcim:virtualchassis_list'
@ -1220,7 +1221,7 @@ class PowerPanelTable(BaseTable):
)
powerfeed_count = tables.TemplateColumn(
template_code=POWERPANEL_POWERFEED_COUNT,
verbose_name='Feeds'
verbose_name=_('Feeds')
)
class Meta(BaseTable.Meta):
@ -1254,7 +1255,7 @@ class PowerFeedTable(BaseTable):
template_code="{{ value }}%"
)
available_power = tables.Column(
verbose_name='Available power (VA)'
verbose_name=_('Available power (VA)')
)
tags = TagColumn(
url_name='dcim:powerfeed_list'

View File

@ -14,6 +14,7 @@ from django.urls import reverse
from django.utils.html import escape
from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
from django.views.generic import View
from circuits.models import Circuit
@ -74,7 +75,7 @@ class BulkRenameView(GetReturnURLMixin, View):
for obj in selected_objects:
obj.name = obj.new_name
obj.save()
messages.success(request, "Renamed {} {}".format(
messages.success(request, _('Renamed {} {}').format(
len(selected_objects),
model._meta.verbose_name_plural
))
@ -119,7 +120,7 @@ class BulkDisconnectView(GetReturnURLMixin, View):
obj.cable.delete()
count += 1
messages.success(request, "Disconnected {} {}".format(
messages.success(request, _('Disconnected {} {}').format(
count, self.model._meta.verbose_name_plural
))
@ -1853,7 +1854,7 @@ class DeviceBayPopulateView(PermissionRequiredMixin, View):
device_bay.installed_device = form.cleaned_data['installed_device']
device_bay.save()
messages.success(request, "Added {} to {}.".format(device_bay.installed_device, device_bay))
messages.success(request, _('Added {} to {}.').format(device_bay.installed_device, device_bay))
return redirect('dcim:device', pk=device_bay.device.pk)
@ -1888,7 +1889,7 @@ class DeviceBayDepopulateView(PermissionRequiredMixin, View):
removed_device = device_bay.installed_device
device_bay.installed_device = None
device_bay.save()
messages.success(request, "{} has been removed from {}.".format(removed_device, device_bay))
messages.success(request, _('{} has been removed from {}.').format(removed_device, device_bay))
return redirect('dcim:device', pk=device_bay.device.pk)

View File

@ -1,5 +1,6 @@
from django import forms
from django.contrib import admin
from django.utils.translation import gettext as _
from utilities.forms import LaxURLField
from .models import CustomField, CustomFieldChoice, CustomLink, Graph, ExportTemplate, ReportResult, Webhook
@ -20,7 +21,7 @@ def order_content_types(field):
class WebhookForm(forms.ModelForm):
payload_url = LaxURLField(
label='URL'
label=_('URL')
)
class Meta:
@ -116,11 +117,11 @@ class CustomLinkForm(forms.ModelForm):
'url': forms.Textarea,
}
help_texts = {
'weight': 'A numeric weight to influence the ordering of this link among its peers. Lower weights appear '
'first in a list.',
'text': 'Jinja2 template code for the link text. Reference the object as <code>{{ obj }}</code>. Links '
'which render as empty text will not be displayed.',
'url': 'Jinja2 template code for the link URL. Reference the object as <code>{{ obj }}</code>.',
'weight': _('A numeric weight to influence the ordering of this link among its peers. Lower weights appear '
'first in a list.'),
'text': _('Jinja2 template code for the link text. Reference the object as <code>{{ obj }}</code>. Links '
'which render as empty text will not be displayed.'),
'url': _('Jinja2 template code for the link URL. Reference the object as <code>{{ obj }}</code>.'),
}
def __init__(self, *args, **kwargs):

View File

@ -1,6 +1,7 @@
import django_filters
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.utils.translation import gettext as _
from dcim.models import DeviceRole, Platform, Region, Site
from tenancy.models import Tenant, TenantGroup
@ -107,7 +108,7 @@ class ExportTemplateFilterSet(BaseFilterSet):
class TagFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
class Meta:
@ -126,95 +127,95 @@ class TagFilterSet(BaseFilterSet):
class ConfigContextFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = django_filters.ModelMultipleChoiceFilter(
field_name='regions',
queryset=Region.objects.all(),
label='Region',
label=_('Region'),
)
region = django_filters.ModelMultipleChoiceFilter(
field_name='regions__slug',
queryset=Region.objects.all(),
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='sites',
queryset=Site.objects.all(),
label='Site',
label=_('Site'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='sites__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
field_name='roles',
queryset=DeviceRole.objects.all(),
label='Role',
label=_('Role'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='roles__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
label='Role (slug)',
label=_('Role (slug)'),
)
platform_id = django_filters.ModelMultipleChoiceFilter(
field_name='platforms',
queryset=Platform.objects.all(),
label='Platform',
label=_('Platform'),
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platforms__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
label='Platform (slug)',
label=_('Platform (slug)'),
)
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups',
queryset=ClusterGroup.objects.all(),
label='Cluster group',
label=_('Cluster group'),
)
cluster_group = django_filters.ModelMultipleChoiceFilter(
field_name='cluster_groups__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
label='Cluster group (slug)',
label=_('Cluster group (slug)'),
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
field_name='clusters',
queryset=Cluster.objects.all(),
label='Cluster',
label=_('Cluster'),
)
tenant_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups',
queryset=TenantGroup.objects.all(),
label='Tenant group',
label=_('Tenant group'),
)
tenant_group = django_filters.ModelMultipleChoiceFilter(
field_name='tenant_groups__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
label='Tenant group (slug)',
label=_('Tenant group (slug)'),
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
field_name='tenants',
queryset=Tenant.objects.all(),
label='Tenant',
label=_('Tenant'),
)
tenant = django_filters.ModelMultipleChoiceFilter(
field_name='tenants__slug',
queryset=Tenant.objects.all(),
to_field_name='slug',
label='Tenant (slug)',
label=_('Tenant (slug)'),
)
tag = django_filters.ModelMultipleChoiceFilter(
field_name='tags__slug',
queryset=Tag.objects.all(),
to_field_name='slug',
label='Tag (slug)',
label=_('Tag (slug)'),
)
class Meta:
@ -238,7 +239,7 @@ class ConfigContextFilterSet(BaseFilterSet):
class LocalConfigContextFilterSet(django_filters.FilterSet):
local_context_data = django_filters.BooleanFilter(
method='_local_context_data',
label='Has local config context data',
label=_('Has local config context data'),
)
def _local_context_data(self, queryset, name, value):
@ -248,7 +249,7 @@ class LocalConfigContextFilterSet(django_filters.FilterSet):
class ObjectChangeFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
time = django_filters.DateTimeFromToRangeFilter()

View File

@ -1,6 +1,7 @@
from django import forms
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext as _
from mptt.forms import TreeNodeMultipleChoiceField
from taggit.forms import TagField as TagField_
@ -181,7 +182,7 @@ class TagFilterForm(BootstrapMixin, forms.Form):
model = Tag
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
@ -285,7 +286,7 @@ class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
class ConfigContextFilterForm(BootstrapMixin, forms.Form):
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@ -330,7 +331,7 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
cluster_id = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(),
required=False,
label='Cluster'
label=_('Cluster')
)
tenant_group = DynamicModelMultipleChoiceField(
queryset=TenantGroup.objects.all(),
@ -365,7 +366,7 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
class LocalConfigContextFilterForm(forms.Form):
local_context_data = forms.NullBooleanField(
required=False,
label='Has local config context data',
label=_('Has local config context data'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
@ -393,15 +394,15 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
model = ObjectChange
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
time_after = forms.DateTimeField(
label='After',
label=_('After'),
required=False,
widget=DateTimePicker()
)
time_before = forms.DateTimeField(
label='Before',
label=_('Before'),
required=False,
widget=DateTimePicker()
)
@ -420,7 +421,7 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
queryset=ContentType.objects.order_by('model'),
required=False,
widget=ContentTypeSelect(),
label='Object Type'
label=_('Object Type')
)
@ -432,8 +433,8 @@ class ScriptForm(BootstrapMixin, forms.Form):
_commit = forms.BooleanField(
required=False,
initial=True,
label="Commit changes",
help_text="Commit changes to the database (uncheck for a dry-run)"
label=_('Commit changes'),
help_text=_('Commit changes to the database (uncheck for a dry-run)')
)
def __init__(self, *args, **kwargs):

View File

@ -7,6 +7,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.validators import ValidationError
from django.db import models
from django.utils.translation import gettext as _
from utilities.forms import CSVChoiceField, DatePicker, LaxURLField, StaticSelect2, add_blank_choice
from extras.choices import *
@ -70,9 +71,9 @@ class CustomField(models.Model):
obj_type = models.ManyToManyField(
to=ContentType,
related_name='custom_fields',
verbose_name='Object(s)',
verbose_name=_('Object(s)'),
limit_choices_to=FeatureQuery('custom_fields'),
help_text='The object(s) to which this field applies.'
help_text=_('The object(s) to which this field applies.')
)
type = models.CharField(
max_length=50,
@ -86,8 +87,8 @@ class CustomField(models.Model):
label = models.CharField(
max_length=50,
blank=True,
help_text='Name of the field as displayed to users (if not provided, '
'the field\'s name will be used)'
help_text=_('Name of the field as displayed to users (if not provided, '
'the field\'s name will be used)')
)
description = models.CharField(
max_length=200,
@ -95,24 +96,24 @@ class CustomField(models.Model):
)
required = models.BooleanField(
default=False,
help_text='If true, this field is required when creating new objects '
'or editing an existing object.'
help_text=_('If true, this field is required when creating new objects '
'or editing an existing object.')
)
filter_logic = models.CharField(
max_length=50,
choices=CustomFieldFilterLogicChoices,
default=CustomFieldFilterLogicChoices.FILTER_LOOSE,
help_text='Loose matches any instance of a given string; exact '
'matches the entire field.'
help_text=_('Loose matches any instance of a given string; exact '
'matches the entire field.')
)
default = models.CharField(
max_length=100,
blank=True,
help_text='Default value for the field. Use "true" or "false" for booleans.'
help_text=_('Default value for the field. Use "true" or "false" for booleans.')
)
weight = models.PositiveSmallIntegerField(
default=100,
help_text='Fields with higher weights appear lower in a form.'
help_text=_('Fields with higher weights appear lower in a form.')
)
objects = CustomFieldManager()
@ -284,7 +285,7 @@ class CustomFieldChoice(models.Model):
)
weight = models.PositiveSmallIntegerField(
default=100,
help_text='Higher weights appear lower in the list'
help_text=_('Higher weights appear lower in the list')
)
class Meta:

View File

@ -10,6 +10,7 @@ from django.db import models
from django.http import HttpResponse
from django.template import Template, Context
from django.urls import reverse
from django.utils.translation import gettext as _
from rest_framework.utils.encoders import JSONEncoder
from utilities.utils import deepmerge, render_jinja2
@ -32,9 +33,9 @@ class Webhook(models.Model):
obj_type = models.ManyToManyField(
to=ContentType,
related_name='webhooks',
verbose_name='Object types',
verbose_name=_('Object types'),
limit_choices_to=FeatureQuery('webhooks'),
help_text="The object(s) to which this Webhook applies."
help_text=_('The object(s) to which this Webhook applies.')
)
name = models.CharField(
max_length=150,
@ -42,20 +43,20 @@ class Webhook(models.Model):
)
type_create = models.BooleanField(
default=False,
help_text="Call this webhook when a matching object is created."
help_text=_('Call this webhook when a matching object is created.')
)
type_update = models.BooleanField(
default=False,
help_text="Call this webhook when a matching object is updated."
help_text=_('Call this webhook when a matching object is updated.')
)
type_delete = models.BooleanField(
default=False,
help_text="Call this webhook when a matching object is deleted."
help_text=_('Call this webhook when a matching object is deleted.')
)
payload_url = models.CharField(
max_length=500,
verbose_name='URL',
help_text="A POST will be sent to this URL when the webhook is called."
verbose_name=_('URL'),
help_text=_('A POST will be sent to this URL when the webhook is called.')
)
enabled = models.BooleanField(
default=True
@ -64,47 +65,47 @@ class Webhook(models.Model):
max_length=30,
choices=WebhookHttpMethodChoices,
default=WebhookHttpMethodChoices.METHOD_POST,
verbose_name='HTTP method'
verbose_name=_('HTTP method')
)
http_content_type = models.CharField(
max_length=100,
default=HTTP_CONTENT_TYPE_JSON,
verbose_name='HTTP content type',
help_text='The complete list of official content types is available '
'<a href="https://www.iana.org/assignments/media-types/media-types.xhtml">here</a>.'
verbose_name=_('HTTP content type'),
help_text=_('The complete list of official content types is available '
'<a href="https://www.iana.org/assignments/media-types/media-types.xhtml">here</a>.')
)
additional_headers = models.TextField(
blank=True,
help_text="User-supplied HTTP headers to be sent with the request in addition to the HTTP content type. "
help_text=_("User-supplied HTTP headers to be sent with the request in addition to the HTTP content type."
"Headers should be defined in the format <code>Name: Value</code>. Jinja2 template processing is "
"support with the same context as the request body (below)."
"support with the same context as the request body (below).")
)
body_template = models.TextField(
blank=True,
help_text='Jinja2 template for a custom request body. If blank, a JSON object representing the change will be '
help_text=_('Jinja2 template for a custom request body. If blank, a JSON object representing the change will be '
'included. Available context data includes: <code>event</code>, <code>model</code>, '
'<code>timestamp</code>, <code>username</code>, <code>request_id</code>, and <code>data</code>.'
'<code>timestamp</code>, <code>username</code>, <code>request_id</code>, and <code>data</code>.')
)
secret = models.CharField(
max_length=255,
blank=True,
help_text="When provided, the request will include a 'X-Hook-Signature' "
help_text=_("When provided, the request will include a 'X-Hook-Signature' "
"header containing a HMAC hex digest of the payload body using "
"the secret as the key. The secret is not transmitted in "
"the request."
"the request.")
)
ssl_verification = models.BooleanField(
default=True,
verbose_name='SSL verification',
help_text="Enable SSL certificate verification. Disable with caution!"
verbose_name=_('SSL verification'),
help_text=_('Enable SSL certificate verification. Disable with caution!')
)
ca_file_path = models.CharField(
max_length=4096,
null=True,
blank=True,
verbose_name='CA File Path',
help_text='The specific CA certificate file to use for SSL verification. '
'Leave blank to use the system defaults.'
verbose_name=_('CA File Path'),
help_text=_('The specific CA certificate file to use for SSL verification. '
'Leave blank to use the system defaults.')
)
class Meta:
@ -168,12 +169,12 @@ class CustomLink(models.Model):
)
text = models.CharField(
max_length=500,
help_text="Jinja2 template code for link text"
help_text=_('Jinja2 template code for link text')
)
url = models.CharField(
max_length=500,
verbose_name='URL',
help_text="Jinja2 template code for link URL"
verbose_name=_('URL'),
help_text=_('Jinja2 template code for link URL')
)
weight = models.PositiveSmallIntegerField(
default=100
@ -181,16 +182,16 @@ class CustomLink(models.Model):
group_name = models.CharField(
max_length=50,
blank=True,
help_text="Links with the same group will appear as a dropdown menu"
help_text=_('Links with the same group will appear as a dropdown menu')
)
button_class = models.CharField(
max_length=30,
choices=CustomLinkButtonClassChoices,
default=CustomLinkButtonClassChoices.CLASS_DEFAULT,
help_text="The class of the first link in a group will be used for the dropdown button"
help_text=_('The class of the first link in a group will be used for the dropdown button')
)
new_window = models.BooleanField(
help_text="Force link to open in a new window"
help_text=_('Force link to open in a new window')
)
class Meta:
@ -215,7 +216,7 @@ class Graph(models.Model):
)
name = models.CharField(
max_length=100,
verbose_name='Name'
verbose_name=_('Name')
)
template_language = models.CharField(
max_length=50,
@ -224,11 +225,11 @@ class Graph(models.Model):
)
source = models.CharField(
max_length=500,
verbose_name='Source URL'
verbose_name=_('Source URL')
)
link = models.URLField(
blank=True,
verbose_name='Link URL'
verbose_name=_('Link URL')
)
class Meta:
@ -284,18 +285,18 @@ class ExportTemplate(models.Model):
default=TemplateLanguageChoices.LANGUAGE_JINJA2
)
template_code = models.TextField(
help_text='The list of objects being exported is passed as a context variable named <code>queryset</code>.'
help_text=_('The list of objects being exported is passed as a context variable named <code>queryset</code>.')
)
mime_type = models.CharField(
max_length=50,
blank=True,
verbose_name='MIME type',
help_text='Defaults to <code>text/plain</code>'
verbose_name=_('MIME type'),
help_text=_('Defaults to <code>text/plain</code>')
)
file_extension = models.CharField(
max_length=15,
blank=True,
help_text='Extension to append to the rendered filename'
help_text=_('Extension to append to the rendered filename')
)
class Meta:

View File

@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from django.utils.translation import gettext as _
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
from .models import ConfigContext, ObjectChange, Tag, TaggedItem
@ -84,10 +85,10 @@ class TaggedItemTable(BaseTable):
content_object = tables.TemplateColumn(
template_code=TAGGED_ITEM,
orderable=False,
verbose_name='Object'
verbose_name=_('Object')
)
content_type = tables.Column(
verbose_name='Type'
verbose_name=_('Type')
)
class Meta(BaseTable.Meta):
@ -99,7 +100,7 @@ class ConfigContextTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
is_active = BooleanColumn(
verbose_name='Active'
verbose_name=_('Active')
)
class Meta(BaseTable.Meta):
@ -119,15 +120,15 @@ class ObjectChangeTable(BaseTable):
template_code=OBJECTCHANGE_ACTION
)
changed_object_type = tables.Column(
verbose_name='Type'
verbose_name=_('Type')
)
object_repr = tables.TemplateColumn(
template_code=OBJECTCHANGE_OBJECT,
verbose_name='Object'
verbose_name=_('Object')
)
request_id = tables.TemplateColumn(
template_code=OBJECTCHANGE_REQUEST_ID,
verbose_name='Request ID'
verbose_name=_('Request ID')
)
class Meta(BaseTable.Meta):

View File

@ -1,6 +1,7 @@
from django.apps import AppConfig
from django.utils.translation import gettext as _
class IPAMConfig(AppConfig):
name = "ipam"
verbose_name = "IPAM"
verbose_name = _('IPAM')

View File

@ -2,6 +2,7 @@ import django_filters
import netaddr
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.utils.translation import gettext as _
from netaddr.core import AddrFormatError
from dcim.models import Device, Interface, Region, Site
@ -32,7 +33,7 @@ __all__ = (
class VRFFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
tag = TagFilter()
@ -60,7 +61,7 @@ class RIRFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
family = django_filters.NumberFilter(
field_name='prefix',
@ -68,17 +69,17 @@ class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
)
prefix = django_filters.CharFilter(
method='filter_prefix',
label='Prefix',
label=_('Prefix'),
)
rir_id = django_filters.ModelMultipleChoiceFilter(
queryset=RIR.objects.all(),
label='RIR (ID)',
label=_('RIR (ID)'),
)
rir = django_filters.ModelMultipleChoiceFilter(
field_name='rir__slug',
queryset=RIR.objects.all(),
to_field_name='slug',
label='RIR (slug)',
label=_('RIR (slug)'),
)
tag = TagFilter()
@ -110,7 +111,7 @@ class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
class Meta:
@ -121,7 +122,7 @@ class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
family = django_filters.NumberFilter(
field_name='prefix',
@ -129,74 +130,74 @@ class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Cre
)
prefix = django_filters.CharFilter(
method='filter_prefix',
label='Prefix',
label=_('Prefix'),
)
within = django_filters.CharFilter(
method='search_within',
label='Within prefix',
label=_('Within prefix'),
)
within_include = django_filters.CharFilter(
method='search_within_include',
label='Within and including prefix',
label=_('Within and including prefix'),
)
contains = django_filters.CharFilter(
method='search_contains',
label='Prefixes which contain this prefix or IP',
label=_('Prefixes which contain this prefix or IP'),
)
mask_length = django_filters.NumberFilter(
method='filter_mask_length',
label='Mask length',
label=_('Mask length'),
)
vrf_id = django_filters.ModelMultipleChoiceFilter(
queryset=VRF.objects.all(),
label='VRF',
label=_('VRF'),
)
vrf = django_filters.ModelMultipleChoiceFilter(
field_name='vrf__rd',
queryset=VRF.objects.all(),
to_field_name='rd',
label='VRF (RD)',
label=_('VRF (RD)'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
vlan_id = django_filters.ModelMultipleChoiceFilter(
queryset=VLAN.objects.all(),
label='VLAN (ID)',
label=_('VLAN (ID)'),
)
vlan_vid = django_filters.NumberFilter(
field_name='vlan__vid',
label='VLAN number (1-4095)',
label=_('VLAN number (1-4095)'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=Role.objects.all(),
label='Role (ID)',
label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=Role.objects.all(),
to_field_name='slug',
label='Role (slug)',
label=_('Role (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=PrefixStatusChoices,
@ -271,7 +272,7 @@ class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Cre
class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
family = django_filters.NumberFilter(
field_name='address',
@ -279,60 +280,60 @@ class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet,
)
parent = django_filters.CharFilter(
method='search_by_parent',
label='Parent prefix',
label=_('Parent prefix'),
)
address = MultiValueCharFilter(
method='filter_address',
label='Address',
label=_('Address'),
)
mask_length = django_filters.NumberFilter(
method='filter_mask_length',
label='Mask length',
label=_('Mask length'),
)
vrf_id = django_filters.ModelMultipleChoiceFilter(
queryset=VRF.objects.all(),
label='VRF',
label=_('VRF'),
)
vrf = django_filters.ModelMultipleChoiceFilter(
field_name='vrf__rd',
queryset=VRF.objects.all(),
to_field_name='rd',
label='VRF (RD)',
label=_('VRF (RD)'),
)
device = MultiValueCharFilter(
method='filter_device',
field_name='name',
label='Device (name)',
label=_('Device (name)'),
)
device_id = MultiValueNumberFilter(
method='filter_device',
field_name='pk',
label='Device (ID)',
label=_('Device (ID)'),
)
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
field_name='interface__virtual_machine',
queryset=VirtualMachine.objects.all(),
label='Virtual machine (ID)',
label=_('Virtual machine (ID)'),
)
virtual_machine = django_filters.ModelMultipleChoiceFilter(
field_name='interface__virtual_machine__name',
queryset=VirtualMachine.objects.all(),
to_field_name='name',
label='Virtual machine (name)',
label=_('Virtual machine (name)'),
)
interface = django_filters.ModelMultipleChoiceFilter(
field_name='interface__name',
queryset=Interface.objects.all(),
to_field_name='name',
label='Interface (ID)',
label=_('Interface (ID)'),
)
interface_id = django_filters.ModelMultipleChoiceFilter(
queryset=Interface.objects.all(),
label='Interface (ID)',
label=_('Interface (ID)'),
)
assigned_to_interface = django_filters.BooleanFilter(
method='_assigned_to_interface',
label='Is assigned to an interface',
label=_('Is assigned to an interface'),
)
status = django_filters.MultipleChoiceFilter(
choices=IPAddressStatusChoices,
@ -397,24 +398,24 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
class Meta:
@ -425,50 +426,50 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
group_id = django_filters.ModelMultipleChoiceFilter(
queryset=VLANGroup.objects.all(),
label='Group (ID)',
label=_('Group (ID)'),
)
group = django_filters.ModelMultipleChoiceFilter(
field_name='group__slug',
queryset=VLANGroup.objects.all(),
to_field_name='slug',
label='Group',
label=_('Group'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=Role.objects.all(),
label='Role (ID)',
label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=Role.objects.all(),
to_field_name='slug',
label='Role (slug)',
label=_('Role (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=VLANStatusChoices,
@ -494,27 +495,27 @@ class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
class ServiceFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label='Device (ID)',
label=_('Device (ID)'),
)
device = django_filters.ModelMultipleChoiceFilter(
field_name='device__name',
queryset=Device.objects.all(),
to_field_name='name',
label='Device (name)',
label=_('Device (name)'),
)
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
queryset=VirtualMachine.objects.all(),
label='Virtual machine (ID)',
label=_('Virtual machine (ID)'),
)
virtual_machine = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_machine__name',
queryset=VirtualMachine.objects.all(),
to_field_name='name',
label='Virtual machine (name)',
label=_('Virtual machine (name)'),
)
tag = TagFilter()

View File

@ -1,5 +1,6 @@
from django import forms
from django.core.validators import MaxValueValidator, MinValueValidator
from django.utils.translation import gettext as _
from dcim.models import Device, Interface, Rack, Region, Site
from extras.forms import (
@ -55,7 +56,7 @@ class VRFCSVForm(CustomFieldModelCSVForm):
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned tenant'
help_text=_('Assigned tenant')
)
class Meta:
@ -75,7 +76,7 @@ class VRFBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm
enforce_unique = forms.NullBooleanField(
required=False,
widget=BulkEditNullBooleanSelect(),
label='Enforce unique space'
label=_('Enforce unique space')
)
description = forms.CharField(
max_length=100,
@ -93,7 +94,7 @@ class VRFFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
field_order = ['q', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
tag = TagFilterField(model)
@ -119,14 +120,14 @@ class RIRCSVForm(CSVModelForm):
model = RIR
fields = RIR.csv_headers
help_texts = {
'name': 'RIR name',
'name': _('RIR name'),
}
class RIRFilterForm(BootstrapMixin, forms.Form):
is_private = forms.NullBooleanField(
required=False,
label='Private',
label=_('Private'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
@ -163,7 +164,7 @@ class AggregateCSVForm(CustomFieldModelCSVForm):
rir = CSVModelChoiceField(
queryset=RIR.objects.all(),
to_field_name='name',
help_text='Assigned RIR'
help_text=_('Assigned RIR')
)
class Meta:
@ -179,7 +180,7 @@ class AggregateBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
rir = DynamicModelChoiceField(
queryset=RIR.objects.all(),
required=False,
label='RIR'
label=_('RIR')
)
date_added = forms.DateField(
required=False
@ -202,19 +203,19 @@ class AggregateFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Aggregate
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
family = forms.ChoiceField(
required=False,
choices=add_blank_choice(IPAddressFamilyChoices),
label='Address family',
label=_('Address family'),
widget=StaticSelect2()
)
rir = DynamicModelMultipleChoiceField(
queryset=RIR.objects.all(),
to_field_name='slug',
required=False,
label='RIR',
label=_('RIR'),
widget=APISelectMultiple(
value_field="slug",
)
@ -252,7 +253,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF'
label=_('VRF')
)
site = DynamicModelChoiceField(
queryset=Site.objects.all(),
@ -270,7 +271,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
vlan_group = DynamicModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
label='VLAN group',
label=_('VLAN group'),
widget=APISelect(
filter_for={
'vlan': 'group_id'
@ -283,7 +284,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
vlan = DynamicModelChoiceField(
queryset=VLAN.objects.all(),
required=False,
label='VLAN',
label=_('VLAN'),
widget=APISelect(
display_field='display_name'
)
@ -323,41 +324,41 @@ class PrefixCSVForm(CustomFieldModelCSVForm):
queryset=VRF.objects.all(),
to_field_name='name',
required=False,
help_text='Assigned VRF'
help_text=_('Assigned VRF')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned tenant'
help_text=_('Assigned tenant')
)
site = CSVModelChoiceField(
queryset=Site.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned site'
help_text=_('Assigned site')
)
vlan_group = CSVModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
to_field_name='name',
help_text="VLAN's group (if any)"
help_text=_("VLAN's group (if any)")
)
vlan = CSVModelChoiceField(
queryset=VLAN.objects.all(),
required=False,
to_field_name='vid',
help_text="Assigned VLAN"
help_text=_('Assigned VLAN')
)
status = CSVChoiceField(
choices=PrefixStatusChoices,
help_text='Operational status'
help_text=_('Operational status')
)
role = CSVModelChoiceField(
queryset=Role.objects.all(),
required=False,
to_field_name='name',
help_text='Functional role'
help_text=_('Functional role')
)
class Meta:
@ -389,7 +390,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF'
label=_('VRF')
)
prefix_length = forms.IntegerField(
min_value=PREFIX_LENGTH_MIN,
@ -412,7 +413,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
is_pool = forms.NullBooleanField(
required=False,
widget=BulkEditNullBooleanSelect(),
label='Is a pool'
label=_('Is a pool')
)
description = forms.CharField(
max_length=100,
@ -433,7 +434,7 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
]
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
within_include = forms.CharField(
required=False,
@ -442,24 +443,24 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
'placeholder': 'Prefix',
}
),
label='Search within'
label=_('Search within')
)
family = forms.ChoiceField(
required=False,
choices=add_blank_choice(IPAddressFamilyChoices),
label='Address family',
label=_('Address family'),
widget=StaticSelect2()
)
mask_length = forms.ChoiceField(
required=False,
choices=PREFIX_MASK_LENGTH_CHOICES,
label='Mask length',
label=_('Mask length'),
widget=StaticSelect2()
)
vrf_id = DynamicModelMultipleChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF',
label=_('VRF'),
widget=APISelectMultiple(
null_option=True,
)
@ -500,14 +501,14 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
)
is_pool = forms.NullBooleanField(
required=False,
label='Is a pool',
label=_('Is a pool'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
)
expand = forms.BooleanField(
required=False,
label='Expand prefix hierarchy'
label=_('Expand prefix hierarchy')
)
tag = TagFilterField(model)
@ -524,12 +525,12 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF'
label=_('VRF')
)
nat_site = DynamicModelChoiceField(
queryset=Site.objects.all(),
required=False,
label='Site',
label=_('Site'),
widget=APISelect(
filter_for={
'nat_rack': 'site_id',
@ -540,7 +541,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
nat_rack = DynamicModelChoiceField(
queryset=Rack.objects.all(),
required=False,
label='Rack',
label=_('Rack'),
widget=APISelect(
display_field='display_name',
filter_for={
@ -554,7 +555,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
nat_device = DynamicModelChoiceField(
queryset=Device.objects.all(),
required=False,
label='Device',
label=_('Device'),
widget=APISelect(
display_field='display_name',
filter_for={
@ -565,7 +566,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
nat_vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF',
label=_('VRF'),
widget=APISelect(
filter_for={
'nat_inside': 'vrf_id'
@ -575,14 +576,14 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
nat_inside = DynamicModelChoiceField(
queryset=IPAddress.objects.all(),
required=False,
label='IP Address',
label=_('IP Address'),
widget=APISelect(
display_field='address'
)
)
primary_for_parent = forms.BooleanField(
required=False,
label='Make this the primary IP for the device/VM'
label=_('Make this the primary IP for the device/VM')
)
tags = TagField(
required=False
@ -671,7 +672,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
class IPAddressBulkCreateForm(BootstrapMixin, forms.Form):
pattern = ExpandableIPAddressField(
label='Address pattern'
label=_('Address pattern')
)
@ -679,7 +680,7 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF'
label=_('VRF')
)
tags = TagField(
required=False
@ -705,43 +706,43 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
queryset=VRF.objects.all(),
to_field_name='name',
required=False,
help_text='Assigned VRF'
help_text=_('Assigned VRF')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
to_field_name='name',
required=False,
help_text='Assigned tenant'
help_text=_('Assigned tenant')
)
status = CSVChoiceField(
choices=IPAddressStatusChoices,
help_text='Operational status'
help_text=_('Operational status')
)
role = CSVChoiceField(
choices=IPAddressRoleChoices,
required=False,
help_text='Functional role'
help_text=_('Functional role')
)
device = CSVModelChoiceField(
queryset=Device.objects.all(),
required=False,
to_field_name='name',
help_text='Parent device of assigned interface (if any)'
help_text=_('Parent device of assigned interface (if any)')
)
virtual_machine = CSVModelChoiceField(
queryset=VirtualMachine.objects.all(),
required=False,
to_field_name='name',
help_text='Parent VM of assigned interface (if any)'
help_text=_('Parent VM of assigned interface (if any)')
)
interface = CSVModelChoiceField(
queryset=Interface.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned interface'
help_text=_('Assigned interface')
)
is_primary = forms.BooleanField(
help_text='Make this the primary IP for the assigned device',
help_text=_('Make this the primary IP for the assigned device'),
required=False
)
@ -779,7 +780,7 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
# Validate is_primary
if is_primary and not device and not virtual_machine:
raise forms.ValidationError("No device or virtual machine specified; cannot set as primary IP")
raise forms.ValidationError(_('No device or virtual machine specified; cannot set as primary IP'))
def save(self, *args, **kwargs):
@ -805,7 +806,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
vrf = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF'
label=_('VRF')
)
mask_length = forms.IntegerField(
min_value=IPADDRESS_MASK_LENGTH_MIN,
@ -845,12 +846,12 @@ class IPAddressAssignForm(BootstrapMixin, forms.Form):
vrf_id = DynamicModelChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF',
label=_('VRF'),
empty_label='Global'
)
q = forms.CharField(
required=False,
label='Search',
label=_('Search'),
)
@ -862,7 +863,7 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
]
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
parent = forms.CharField(
required=False,
@ -871,24 +872,24 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
'placeholder': 'Prefix',
}
),
label='Parent Prefix'
label=_('Parent Prefix')
)
family = forms.ChoiceField(
required=False,
choices=add_blank_choice(IPAddressFamilyChoices),
label='Address family',
label=_('Address family'),
widget=StaticSelect2()
)
mask_length = forms.ChoiceField(
required=False,
choices=IPADDRESS_MASK_LENGTH_CHOICES,
label='Mask length',
label=_('Mask length'),
widget=StaticSelect2()
)
vrf_id = DynamicModelMultipleChoiceField(
queryset=VRF.objects.all(),
required=False,
label='VRF',
label=_('VRF'),
widget=APISelectMultiple(
null_option=True,
)
@ -905,7 +906,7 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
)
assigned_to_interface = forms.NullBooleanField(
required=False,
label='Assigned to an interface',
label=_('Assigned to an interface'),
widget=StaticSelect2(
choices=BOOLEAN_WITH_BLANK_CHOICES
)
@ -936,7 +937,7 @@ class VLANGroupCSVForm(CSVModelForm):
queryset=Site.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned site'
help_text=_('Assigned site')
)
slug = SlugField()
@ -1018,37 +1019,37 @@ class VLANCSVForm(CustomFieldModelCSVForm):
queryset=Site.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned site'
help_text=_('Assigned site')
)
group = CSVModelChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned VLAN group'
help_text=_('Assigned VLAN group')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
to_field_name='name',
required=False,
help_text='Assigned tenant'
help_text=_('Assigned tenant')
)
status = CSVChoiceField(
choices=VLANStatusChoices,
help_text='Operational status'
help_text=_('Operational status')
)
role = CSVModelChoiceField(
queryset=Role.objects.all(),
required=False,
to_field_name='name',
help_text='Functional role'
help_text=_('Functional role')
)
class Meta:
model = VLAN
fields = VLAN.csv_headers
help_texts = {
'vid': 'Numeric VLAN ID (1-4095)',
'name': 'VLAN name',
'vid': _('Numeric VLAN ID (1-4095)'),
'name': _('VLAN name'),
}
def __init__(self, data=None, *args, **kwargs):
@ -1108,7 +1109,7 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
field_order = ['q', 'region', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@ -1134,7 +1135,7 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
group_id = DynamicModelMultipleChoiceField(
queryset=VLANGroup.objects.all(),
required=False,
label='VLAN group',
label=_('VLAN group'),
widget=APISelectMultiple(
null_option=True,
)
@ -1204,7 +1205,7 @@ class ServiceFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Service
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
protocol = forms.ChoiceField(
choices=add_blank_choice(ServiceProtocolChoices),
@ -1222,17 +1223,17 @@ class ServiceCSVForm(CustomFieldModelCSVForm):
queryset=Device.objects.all(),
required=False,
to_field_name='name',
help_text='Required if not assigned to a VM'
help_text=_('Required if not assigned to a VM')
)
virtual_machine = CSVModelChoiceField(
queryset=VirtualMachine.objects.all(),
required=False,
to_field_name='name',
help_text='Required if not assigned to a device'
help_text=_('Required if not assigned to a device')
)
protocol = CSVChoiceField(
choices=ServiceProtocolChoices,
help_text='IP protocol'
help_text=_('IP protocol')
)
class Meta:

View File

@ -6,6 +6,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import F, Q
from django.urls import reverse
from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.models import Device, Interface
@ -50,8 +51,8 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
unique=True,
blank=True,
null=True,
verbose_name='Route distinguisher',
help_text='Unique route distinguisher (as defined in RFC 4364)'
verbose_name=_('Route distinguisher'),
help_text=_('Unique route distinguisher (as defined in RFC 4364)')
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@ -62,8 +63,8 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
)
enforce_unique = models.BooleanField(
default=True,
verbose_name='Enforce unique space',
help_text='Prevent duplicate prefixes/IP addresses within this VRF'
verbose_name=_('Enforce unique space'),
help_text=_('Prevent duplicate prefixes/IP addresses within this VRF')
)
description = models.CharField(
max_length=200,
@ -84,7 +85,7 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
class Meta:
ordering = ('name', 'rd', 'pk') # (name, rd) may be non-unique
verbose_name = 'VRF'
verbose_name = _('VRF')
verbose_name_plural = 'VRFs'
def __str__(self):
@ -123,8 +124,8 @@ class RIR(ChangeLoggedModel):
)
is_private = models.BooleanField(
default=False,
verbose_name='Private',
help_text='IP space managed by this RIR is considered private'
verbose_name=_('Private'),
help_text=_('IP space managed by this RIR is considered private')
)
description = models.CharField(
max_length=200,
@ -135,7 +136,7 @@ class RIR(ChangeLoggedModel):
class Meta:
ordering = ['name']
verbose_name = 'RIR'
verbose_name = _('RIR')
verbose_name_plural = 'RIRs'
def __str__(self):
@ -164,7 +165,7 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
to='ipam.RIR',
on_delete=models.PROTECT,
related_name='aggregates',
verbose_name='RIR'
verbose_name=_('RIR')
)
date_added = models.DateField(
blank=True,
@ -299,7 +300,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
assigned to a VLAN where appropriate.
"""
prefix = IPNetworkField(
help_text='IPv4 or IPv6 network with mask'
help_text=_('IPv4 or IPv6 network with mask')
)
site = models.ForeignKey(
to='dcim.Site',
@ -314,7 +315,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
related_name='prefixes',
blank=True,
null=True,
verbose_name='VRF'
verbose_name=_('VRF')
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@ -329,14 +330,14 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
related_name='prefixes',
blank=True,
null=True,
verbose_name='VLAN'
verbose_name=_('VLAN')
)
status = models.CharField(
max_length=50,
choices=PrefixStatusChoices,
default=PrefixStatusChoices.STATUS_ACTIVE,
verbose_name='Status',
help_text='Operational status of this prefix'
verbose_name=_('Status'),
help_text=_('Operational status of this prefix')
)
role = models.ForeignKey(
to='ipam.Role',
@ -344,12 +345,12 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
related_name='prefixes',
blank=True,
null=True,
help_text='The primary function of this prefix'
help_text=_('The primary function of this prefix')
)
is_pool = models.BooleanField(
verbose_name='Is a pool',
verbose_name=_('Is a pool'),
default=False,
help_text='All IP addresses within this prefix are considered usable'
help_text=_('All IP addresses within this prefix are considered usable')
)
description = models.CharField(
max_length=200,
@ -570,7 +571,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
which has a NAT outside IP, that Interface's Device can use either the inside or outside IP as its primary IP.
"""
address = IPAddressField(
help_text='IPv4 or IPv6 address (with mask)'
help_text=_('IPv4 or IPv6 address (with mask)')
)
vrf = models.ForeignKey(
to='ipam.VRF',
@ -578,7 +579,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
related_name='ip_addresses',
blank=True,
null=True,
verbose_name='VRF'
verbose_name=_('VRF')
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
@ -591,13 +592,13 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
max_length=50,
choices=IPAddressStatusChoices,
default=IPAddressStatusChoices.STATUS_ACTIVE,
help_text='The operational status of this IP'
help_text=_('The operational status of this IP')
)
role = models.CharField(
max_length=50,
choices=IPAddressRoleChoices,
blank=True,
help_text='The functional role of this IP'
help_text=_('The functional role of this IP')
)
interface = models.ForeignKey(
to='dcim.Interface',
@ -612,15 +613,15 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
related_name='nat_outside',
blank=True,
null=True,
verbose_name='NAT (Inside)',
help_text='The IP for which this address is the "outside" IP'
verbose_name=_('NAT (Inside)'),
help_text=_('The IP for which this address is the "outside" IP')
)
dns_name = models.CharField(
max_length=255,
blank=True,
validators=[DNSValidator],
verbose_name='DNS Name',
help_text='Hostname or FQDN (not case-sensitive)'
verbose_name=_('DNS Name'),
help_text=_('Hostname or FQDN (not case-sensitive)')
)
description = models.CharField(
max_length=200,
@ -663,7 +664,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
class Meta:
ordering = ('address', 'pk') # address may be non-unique
verbose_name = 'IP address'
verbose_name = _('IP address')
verbose_name_plural = 'IP addresses'
def __str__(self):
@ -836,7 +837,7 @@ class VLANGroup(ChangeLoggedModel):
['site', 'name'],
['site', 'slug'],
]
verbose_name = 'VLAN group'
verbose_name = _('VLAN group')
verbose_name_plural = 'VLAN groups'
def __str__(self):
@ -889,7 +890,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
null=True
)
vid = models.PositiveSmallIntegerField(
verbose_name='ID',
verbose_name=_('ID'),
validators=[MinValueValidator(1), MaxValueValidator(4094)]
)
name = models.CharField(
@ -943,7 +944,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
['group', 'vid'],
['group', 'name'],
]
verbose_name = 'VLAN'
verbose_name = _('VLAN')
verbose_name_plural = 'VLANs'
def __str__(self):
@ -999,7 +1000,7 @@ class Service(ChangeLoggedModel, CustomFieldModel):
to='dcim.Device',
on_delete=models.CASCADE,
related_name='services',
verbose_name='device',
verbose_name=_('device'),
null=True,
blank=True
)
@ -1022,13 +1023,13 @@ class Service(ChangeLoggedModel, CustomFieldModel):
MinValueValidator(SERVICE_PORT_MIN),
MaxValueValidator(SERVICE_PORT_MAX)
],
verbose_name='Port number'
verbose_name=_('Port number')
)
ipaddresses = models.ManyToManyField(
to='ipam.IPAddress',
related_name='services',
blank=True,
verbose_name='IP addresses'
verbose_name=_('IP addresses')
)
description = models.CharField(
max_length=200,

View File

@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from django.utils.translation import gettext as _
from dcim.models import Interface
from tenancy.tables import COL_TENANT
@ -191,13 +192,13 @@ class VRFTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
rd = tables.Column(
verbose_name='RD'
verbose_name=_('RD')
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
)
enforce_unique = BooleanColumn(
verbose_name='Unique'
verbose_name=_('Unique')
)
tags = TagColumn(
url_name='ipam:vrf_list'
@ -217,10 +218,10 @@ class RIRTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
is_private = BooleanColumn(
verbose_name='Private'
verbose_name=_('Private')
)
aggregate_count = tables.Column(
verbose_name='Aggregates'
verbose_name=_('Aggregates')
)
actions = tables.TemplateColumn(
template_code=RIR_ACTIONS,
@ -237,32 +238,32 @@ class RIRTable(BaseTable):
class RIRDetailTable(RIRTable):
stats_total = tables.Column(
accessor='stats.total',
verbose_name='Total',
verbose_name=_('Total'),
footer=lambda table: sum(r.stats['total'] for r in table.data)
)
stats_active = tables.Column(
accessor='stats.active',
verbose_name='Active',
verbose_name=_('Active'),
footer=lambda table: sum(r.stats['active'] for r in table.data)
)
stats_reserved = tables.Column(
accessor='stats.reserved',
verbose_name='Reserved',
verbose_name=_('Reserved'),
footer=lambda table: sum(r.stats['reserved'] for r in table.data)
)
stats_deprecated = tables.Column(
accessor='stats.deprecated',
verbose_name='Deprecated',
verbose_name=_('Deprecated'),
footer=lambda table: sum(r.stats['deprecated'] for r in table.data)
)
stats_available = tables.Column(
accessor='stats.available',
verbose_name='Available',
verbose_name=_('Available'),
footer=lambda table: sum(r.stats['available'] for r in table.data)
)
utilization = tables.TemplateColumn(
template_code=RIR_UTILIZATION,
verbose_name='Utilization'
verbose_name=_('Utilization')
)
class Meta(RIRTable.Meta):
@ -283,11 +284,11 @@ class RIRDetailTable(RIRTable):
class AggregateTable(BaseTable):
pk = ToggleColumn()
prefix = tables.LinkColumn(
verbose_name='Aggregate'
verbose_name=_('Aggregate')
)
date_added = tables.DateColumn(
format="Y-m-d",
verbose_name='Added'
verbose_name=_('Added')
)
class Meta(BaseTable.Meta):
@ -297,7 +298,7 @@ class AggregateTable(BaseTable):
class AggregateDetailTable(AggregateTable):
child_count = tables.Column(
verbose_name='Prefixes'
verbose_name=_('Prefixes')
)
utilization = tables.TemplateColumn(
template_code=UTILIZATION_GRAPH,
@ -320,11 +321,11 @@ class RoleTable(BaseTable):
pk = ToggleColumn()
prefix_count = tables.TemplateColumn(
template_code=ROLE_PREFIX_COUNT,
verbose_name='Prefixes'
verbose_name=_('Prefixes')
)
vlan_count = tables.TemplateColumn(
template_code=ROLE_VLAN_COUNT,
verbose_name='VLANs'
verbose_name=_('VLANs')
)
actions = tables.TemplateColumn(
template_code=ROLE_ACTIONS,
@ -353,7 +354,7 @@ class PrefixTable(BaseTable):
)
vrf = tables.TemplateColumn(
template_code=VRF_LINK,
verbose_name='VRF'
verbose_name=_('VRF')
)
tenant = tables.TemplateColumn(
template_code=TENANT_LINK
@ -365,13 +366,13 @@ class PrefixTable(BaseTable):
vlan = tables.LinkColumn(
viewname='ipam:vlan',
args=[Accessor('vlan.pk')],
verbose_name='VLAN'
verbose_name=_('VLAN')
)
role = tables.TemplateColumn(
template_code=PREFIX_ROLE_LINK
)
is_pool = BooleanColumn(
verbose_name='Pool'
verbose_name=_('Pool')
)
add_prefetch = False
@ -415,11 +416,11 @@ class IPAddressTable(BaseTable):
pk = ToggleColumn()
address = tables.TemplateColumn(
template_code=IPADDRESS_LINK,
verbose_name='IP Address'
verbose_name=_('IP Address')
)
vrf = tables.TemplateColumn(
template_code=VRF_LINK,
verbose_name='VRF'
verbose_name=_('VRF')
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
@ -450,7 +451,7 @@ class IPAddressDetailTable(IPAddressTable):
viewname='ipam:ipaddress',
args=[Accessor('nat_inside.pk')],
orderable=False,
verbose_name='NAT (Inside)'
verbose_name=_('NAT (Inside)')
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
@ -472,7 +473,7 @@ class IPAddressDetailTable(IPAddressTable):
class IPAddressAssignTable(BaseTable):
address = tables.TemplateColumn(
template_code=IPADDRESS_ASSIGN_LINK,
verbose_name='IP Address'
verbose_name=_('IP Address')
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
@ -496,11 +497,11 @@ class InterfaceIPAddressTable(BaseTable):
List IP addresses assigned to a specific Interface.
"""
address = tables.LinkColumn(
verbose_name='IP Address'
verbose_name=_('IP Address')
)
vrf = tables.TemplateColumn(
template_code=VRF_LINK,
verbose_name='VRF'
verbose_name=_('VRF')
)
status = tables.TemplateColumn(
template_code=STATUS_LABEL
@ -526,7 +527,7 @@ class VLANGroupTable(BaseTable):
args=[Accessor('site.slug')]
)
vlan_count = tables.Column(
verbose_name='VLANs'
verbose_name=_('VLANs')
)
actions = tables.TemplateColumn(
template_code=VLANGROUP_ACTIONS,
@ -548,7 +549,7 @@ class VLANTable(BaseTable):
pk = ToggleColumn()
vid = tables.TemplateColumn(
template_code=VLAN_LINK,
verbose_name='ID'
verbose_name=_('ID')
)
site = tables.LinkColumn(
viewname='dcim:site',
@ -580,7 +581,7 @@ class VLANDetailTable(VLANTable):
prefixes = tables.TemplateColumn(
template_code=VLAN_PREFIXES,
orderable=False,
verbose_name='Prefixes'
verbose_name=_('Prefixes')
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT
@ -599,7 +600,7 @@ class VLANMemberTable(BaseTable):
order_by=['device', 'virtual_machine']
)
name = tables.LinkColumn(
verbose_name='Interface'
verbose_name=_('Interface')
)
untagged = tables.TemplateColumn(
template_code=VLAN_MEMBER_UNTAGGED,
@ -623,7 +624,7 @@ class InterfaceVLANTable(BaseTable):
vid = tables.LinkColumn(
viewname='ipam:vlan',
args=[Accessor('pk')],
verbose_name='ID'
verbose_name=_('ID')
)
tagged = BooleanColumn()
site = tables.LinkColumn(
@ -632,7 +633,7 @@ class InterfaceVLANTable(BaseTable):
)
group = tables.Column(
accessor=Accessor('group.name'),
verbose_name='Group'
verbose_name=_('Group')
)
tenant = tables.TemplateColumn(
template_code=COL_TENANT

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,48 @@
from django import forms
from django.utils.translation import gettext as _
from utilities.forms import BootstrapMixin
OBJ_TYPE_CHOICES = (
('', 'All Objects'),
('Circuits', (
('provider', 'Providers'),
('circuit', 'Circuits'),
('', _('All Objects')),
(_('Circuits'), (
('provider', _('Providers')),
('circuit', _('Circuits')),
)),
('DCIM', (
('site', 'Sites'),
('rack', 'Racks'),
('rackgroup', 'Rack Groups'),
('devicetype', 'Device types'),
('device', 'Devices'),
('virtualchassis', 'Virtual Chassis'),
('cable', 'Cables'),
('powerfeed', 'Power Feeds'),
(_('DCIM'), (
('site', _('Sites')),
('rack', _('Racks')),
('rackgroup', _('Rack Groups')),
('devicetype', _('Device types')),
('device', _('Devices')),
('virtualchassis', _('Virtual Chassis')),
('cable', _('Cables')),
('powerfeed', _('Power Feeds')),
)),
('IPAM', (
('vrf', 'VRFs'),
('aggregate', 'Aggregates'),
('prefix', 'Prefixes'),
('ipaddress', 'IP addresses'),
('vlan', 'VLANs'),
(_('IPAM'), (
('vrf', _('VRFs')),
('aggregate', _('Aggregates')),
('prefix', _('Prefixes')),
('ipaddress', _('IP addresses')),
('vlan', _('VLANs')),
)),
('Secrets', (
('secret', 'Secrets'),
(_('Secrets'), (
('secret', _('Secrets')),
)),
('Tenancy', (
('tenant', 'Tenants'),
(_('Tenancy'), (
('tenant', _('Tenants')),
)),
('Virtualization', (
('cluster', 'Clusters'),
('virtualmachine', 'Virtual machines'),
(_('Virtualization'), (
('cluster', _('Clusters')),
('virtualmachine', _('Virtual machines')),
)),
)
class SearchForm(BootstrapMixin, forms.Form):
q = forms.CharField(
label='Search'
label=_('Search')
)
obj_type = forms.ChoiceField(
choices=OBJ_TYPE_CHOICES, required=False, label='Type'
choices=OBJ_TYPE_CHOICES, required=False, label=_('Type')
)

View File

@ -681,3 +681,12 @@ for plugin_name in PLUGINS:
CACHEOPS.update({
"{}.{}".format(plugin_name, key): value for key, value in plugin_config.caching_config.items()
})
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
LANGUAGES = (
('en', 'English'),
('zh-Hans', '中文简体'),
)

View File

@ -1,5 +1,6 @@
import django_filters
from django.db.models import Q
from django.utils.translation import gettext as _
from dcim.models import Device
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
@ -20,30 +21,31 @@ class SecretRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
fields = ['id', 'name', 'slug']
class SecretFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
class SecretFilterSet(
BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=SecretRole.objects.all(),
label='Role (ID)',
label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=SecretRole.objects.all(),
to_field_name='slug',
label='Role (slug)',
label=_('Role (slug)'),
)
device_id = django_filters.ModelMultipleChoiceFilter(
queryset=Device.objects.all(),
label='Device (ID)',
label=_('Device (ID)'),
)
device = django_filters.ModelMultipleChoiceFilter(
field_name='device__name',
queryset=Device.objects.all(),
to_field_name='name',
label='Device (name)',
label=_('Device (name)'),
)
tag = TagFilter()

View File

@ -1,6 +1,7 @@
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from django import forms
from django.utils.translation import gettext as _
from dcim.models import Device
from extras.forms import (
@ -20,21 +21,21 @@ def validate_rsa_key(key, is_secret=True):
Validate the format and type of an RSA key.
"""
if key.startswith('ssh-rsa '):
raise forms.ValidationError("OpenSSH line format is not supported. Please ensure that your public is in PEM (base64) format.")
raise forms.ValidationError(_('OpenSSH line format is not supported. Please ensure that your public is in PEM (base64) format.'))
try:
key = RSA.importKey(key)
except ValueError:
raise forms.ValidationError("Invalid RSA key. Please ensure that your key is in PEM (base64) format.")
raise forms.ValidationError(_('Invalid RSA key. Please ensure that your key is in PEM (base64) format.'))
except Exception as e:
raise forms.ValidationError("Invalid key detected: {}".format(e))
if is_secret and not key.has_private():
raise forms.ValidationError("This looks like a public key. Please provide your private RSA key.")
raise forms.ValidationError(_('This looks like a public key. Please provide your private RSA key.'))
elif not is_secret and key.has_private():
raise forms.ValidationError("This looks like a private key. Please provide your public RSA key.")
raise forms.ValidationError(_('This looks like a private key. Please provide your public RSA key.'))
try:
PKCS1_OAEP.new(key)
except Exception:
raise forms.ValidationError("Error validating RSA key. Please ensure that your key supports PKCS#1 OAEP.")
raise forms.ValidationError(_('Error validating RSA key. Please ensure that your key supports PKCS#1 OAEP.'))
#
@ -74,7 +75,7 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm):
plaintext = forms.CharField(
max_length=SECRET_PLAINTEXT_MAX_LENGTH,
required=False,
label='Plaintext',
label=_('Plaintext'),
widget=forms.PasswordInput(
attrs={
'class': 'requires-session-key',
@ -84,7 +85,7 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm):
plaintext2 = forms.CharField(
max_length=SECRET_PLAINTEXT_MAX_LENGTH,
required=False,
label='Plaintext (verify)',
label=_('Plaintext (verify)'),
widget=forms.PasswordInput()
)
role = DynamicModelChoiceField(
@ -130,22 +131,22 @@ class SecretCSVForm(CustomFieldModelCSVForm):
device = CSVModelChoiceField(
queryset=Device.objects.all(),
to_field_name='name',
help_text='Assigned device'
help_text=_('Assigned device')
)
role = CSVModelChoiceField(
queryset=SecretRole.objects.all(),
to_field_name='name',
help_text='Assigned role'
help_text=_('Assigned role')
)
plaintext = forms.CharField(
help_text='Plaintext secret data'
help_text=_('Plaintext secret data')
)
class Meta:
model = Secret
fields = Secret.csv_headers
help_texts = {
'name': 'Name or username',
'name': _('Name or username'),
}
def save(self, *args, **kwargs):
@ -154,7 +155,8 @@ class SecretCSVForm(CustomFieldModelCSVForm):
return s
class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
class SecretBulkEditForm(
BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=Secret.objects.all(),
widget=forms.MultipleHiddenInput()
@ -178,7 +180,7 @@ class SecretFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Secret
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
role = DynamicModelMultipleChoiceField(
queryset=SecretRole.objects.all(),
@ -220,7 +222,7 @@ class UserKeyForm(BootstrapMixin, forms.ModelForm):
class ActivateUserKeyForm(forms.Form):
_selected_action = forms.ModelMultipleChoiceField(
queryset=UserKey.objects.all(),
label='User Keys'
label=_('User Keys')
)
secret_key = forms.CharField(
widget=forms.Textarea(
@ -228,5 +230,5 @@ class ActivateUserKeyForm(forms.Form):
'class': 'vLargeTextField',
}
),
label='Your private key'
label=_('Your private key')
)

View File

@ -12,6 +12,7 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.encoding import force_bytes
from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.models import Device
@ -51,7 +52,7 @@ class UserKey(models.Model):
editable=False
)
public_key = models.TextField(
verbose_name='RSA public key'
verbose_name=_('RSA public key')
)
master_key_cipher = models.BinaryField(
max_length=512,

View File

@ -1,4 +1,5 @@
import django_tables2 as tables
from django.utils.translation import gettext as _
from utilities.tables import BaseTable, TagColumn, ToggleColumn
from .models import SecretRole, Secret
@ -21,7 +22,7 @@ class SecretRoleTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
secret_count = tables.Column(
verbose_name='Secrets'
verbose_name=_('Secrets')
)
actions = tables.TemplateColumn(
template_code=SECRETROLE_ACTIONS,

View File

@ -6,6 +6,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import gettext as _
from django.views.generic import View
from utilities.views import (
@ -117,7 +118,7 @@ def secret_add(request):
secret.save()
form.save_m2m()
messages.success(request, "Added new secret: {}.".format(secret))
messages.success(request, _("Added new secret: {}.").format(secret))
if '_addanother' in request.POST:
return redirect('secrets:secret_add')
else:
@ -164,7 +165,7 @@ def secret_edit(request, pk):
secret.plaintext = form.cleaned_data['plaintext']
secret.encrypt(master_key)
secret.save()
messages.success(request, "Modified secret {}.".format(secret))
messages.success(request, _("Modified secret {}.").format(secret))
return redirect('secrets:secret', pk=secret.pk)
else:
form.add_error(None, "Invalid session key. Unable to encrypt secret data.")

View File

@ -1,5 +1,6 @@
import django_filters
from django.db.models import Q
from django.utils.translation import gettext as _
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
from utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter
@ -16,13 +17,13 @@ __all__ = (
class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
label='Tenant group (ID)',
label=_('Tenant group (ID)'),
)
parent = django_filters.ModelMultipleChoiceFilter(
field_name='parent__slug',
queryset=TenantGroup.objects.all(),
to_field_name='slug',
label='Tenant group group (slug)',
label=_('Tenant group group (slug)'),
)
class Meta:
@ -33,20 +34,20 @@ class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class TenantFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
group_id = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
lookup_expr='in',
label='Tenant group (ID)',
label=_('Tenant group (ID)'),
)
group = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='group',
lookup_expr='in',
to_field_name='slug',
label='Tenant group (slug)',
label=_('Tenant group (slug)'),
)
tag = TagFilter()
@ -73,22 +74,22 @@ class TenancyFilterSet(django_filters.FilterSet):
queryset=TenantGroup.objects.all(),
field_name='tenant__group',
lookup_expr='in',
label='Tenant Group (ID)',
label=_('Tenant Group (ID)'),
)
tenant_group = TreeNodeMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
field_name='tenant__group',
to_field_name='slug',
lookup_expr='in',
label='Tenant Group (slug)',
label=_('Tenant Group (slug)'),
)
tenant_id = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
label='Tenant (ID)',
label=_('Tenant (ID)'),
)
tenant = django_filters.ModelMultipleChoiceFilter(
queryset=Tenant.objects.all(),
field_name='tenant__slug',
to_field_name='slug',
label='Tenant (slug)',
label=_('Tenant (slug)'),
)

View File

@ -1,4 +1,5 @@
from django import forms
from django.utils.translation import gettext as _
from extras.forms import (
AddRemoveTagsForm, CustomFieldModelForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldModelCSVForm,
@ -37,7 +38,7 @@ class TenantGroupCSVForm(CSVModelForm):
queryset=TenantGroup.objects.all(),
required=False,
to_field_name='name',
help_text='Parent group'
help_text=_('Parent group')
)
slug = SlugField()
@ -74,7 +75,7 @@ class TenantCSVForm(CustomFieldModelCSVForm):
queryset=TenantGroup.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned group'
help_text=_('Assigned group')
)
class Meta:
@ -102,7 +103,7 @@ class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = Tenant
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
group = DynamicModelMultipleChoiceField(
queryset=TenantGroup.objects.all(),

View File

@ -1,4 +1,5 @@
import django_tables2 as tables
from django.utils.translation import gettext as _
from utilities.tables import BaseTable, TagColumn, ToggleColumn
from .models import Tenant, TenantGroup
@ -42,7 +43,7 @@ class TenantGroupTable(BaseTable):
orderable=False
)
tenant_count = tables.Column(
verbose_name='Tenants'
verbose_name=_('Tenants')
)
actions = tables.TemplateColumn(
template_code=TENANTGROUP_ACTIONS,

View File

@ -2,6 +2,7 @@ from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as UserAdmin_
from django.contrib.auth.models import User
from django.utils.translation import gettext as _
from .models import Token, UserConfig
@ -13,7 +14,7 @@ class UserConfigInline(admin.TabularInline):
model = UserConfig
readonly_fields = ('data',)
can_delete = False
verbose_name = 'Preferences'
verbose_name = _('Preferences')
@admin.register(User)
@ -27,7 +28,7 @@ class UserAdmin(UserAdmin_):
class TokenAdminForm(forms.ModelForm):
key = forms.CharField(
required=False,
help_text="If no key is provided, one will be generated automatically."
help_text=_('If no key is provided, one will be generated automatically.')
)
class Meta:

View File

@ -1,5 +1,6 @@
from django import forms
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
from django.utils.translation import gettext as _
from utilities.forms import BootstrapMixin, DateTimePicker
from .models import Token
@ -21,7 +22,7 @@ class PasswordChangeForm(BootstrapMixin, DjangoPasswordChangeForm):
class TokenForm(BootstrapMixin, forms.ModelForm):
key = forms.CharField(
required=False,
help_text="If no key is provided, one will be generated automatically."
help_text=_('If no key is provided, one will be generated automatically.')
)
class Meta:

View File

@ -8,6 +8,7 @@ from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone
from django.utils.translation import gettext as _
from utilities.utils import flatten_dict
@ -162,7 +163,7 @@ class Token(models.Model):
)
write_enabled = models.BooleanField(
default=True,
help_text='Permit create/update/delete operations using this key'
help_text=_('Permit create/update/delete operations using this key')
)
description = models.CharField(
max_length=200,

View File

@ -11,6 +11,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.http import is_safe_url
from django.utils.translation import gettext as _
from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import View
@ -64,7 +65,7 @@ class LoginView(View):
# Authenticate user
auth_login(request, form.get_user())
logger.info(f"User {request.user} successfully authenticated")
messages.info(request, "Logged in as {}.".format(request.user))
messages.info(request, _("Logged in as {}.").format(request.user))
logger.debug(f"Redirecting user to {redirect_to}")
return HttpResponseRedirect(redirect_to)
@ -88,7 +89,7 @@ class LogoutView(View):
username = request.user
auth_logout(request)
logger.info(f"User {username} has logged out")
messages.info(request, "You have logged out.")
messages.info(request, _('You have logged out.'))
# Delete session key cookie (if set) upon logout
response = HttpResponseRedirect(reverse('home'))
@ -130,7 +131,7 @@ class UserConfigView(LoginRequiredMixin, View):
if key in data:
userconfig.clear(key)
userconfig.save()
messages.success(request, "Your preferences have been updated.")
messages.success(request, _('Your preferences have been updated.'))
return redirect('user:preferences')
@ -156,7 +157,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
messages.success(request, "Your password has been changed successfully.")
messages.success(request, _('Your password has been changed successfully.'))
return redirect('user:profile')
return render(request, self.template_name, {
@ -206,7 +207,7 @@ class UserKeyEditView(LoginRequiredMixin, View):
uk = form.save(commit=False)
uk.user = request.user
uk.save()
messages.success(request, "Your user key has been saved.")
messages.success(request, _('Your user key has been saved.'))
return redirect('user:userkey')
return render(request, self.template_name, {
@ -237,7 +238,7 @@ class SessionKeyDeleteView(LoginRequiredMixin, View):
# Delete session key
sessionkey.delete()
messages.success(request, "Session key deleted")
messages.success(request, _('Session key deleted'))
# Delete cookie
response = redirect('user:userkey')
@ -304,7 +305,7 @@ class TokenEditView(LoginRequiredMixin, View):
token.user = request.user
token.save()
msg = "Modified token {}".format(token) if pk else "Created token {}".format(token)
msg = _("Modified token {}").format(token) if pk else _("Created token {}").format(token)
messages.success(request, msg)
if '_addanother' in request.POST:
@ -344,7 +345,7 @@ class TokenDeleteView(PermissionRequiredMixin, View):
form = ConfirmationForm(request.POST)
if form.is_valid():
token.delete()
messages.success(request, "Token deleted")
messages.success(request, _('Token deleted'))
return redirect('user:token_list')
return render(request, 'utilities/obj_delete.html', {

View File

@ -5,6 +5,7 @@ from django import forms
from django.conf import settings
from django.db import models
from django_filters.utils import get_model_field, resolve_field
from django.utils.translation import gettext as _
from extras.models import Tag
from utilities.constants import (
@ -274,7 +275,7 @@ class NameSlugSearchFilterSet(django_filters.FilterSet):
"""
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
def search(self, queryset, name, value):

View File

@ -14,6 +14,7 @@ from django.db.models import Count
from django.forms import BoundField
from django.forms.models import fields_for_model
from django.urls import reverse
from django.utils.translation import gettext as _
from .choices import ColorChoices, unpack_grouped_choices
from .validators import EnhancedURLValidator
@ -587,7 +588,7 @@ class TagFilterField(forms.MultipleChoiceField):
return [(str(tag.slug), '{} ({})'.format(tag.name, tag.count)) for tag in tags]
# Choices are fetched each time the form is initialized
super().__init__(label='Tags', choices=get_choices, required=False, *args, **kwargs)
super().__init__(label=_('Tags'), choices=get_choices, required=False, *args, **kwargs)
class DynamicModelChoiceMixin:
@ -740,7 +741,7 @@ class ImportForm(BootstrapMixin, forms.Form):
"""
data = forms.CharField(
widget=forms.Textarea,
help_text="Enter object data in JSON or YAML format. Note: Only a single object/document is supported."
help_text=_('Enter object data in JSON or YAML format. Note: Only a single object/document is supported.')
)
format = forms.ChoiceField(
choices=(
@ -791,7 +792,7 @@ class TableConfigForm(BootstrapMixin, forms.Form):
widget=forms.SelectMultiple(
attrs={'size': 10}
),
help_text="Use the buttons below to arrange columns in the desired order, then select all columns to display."
help_text=_('Use the buttons below to arrange columns in the desired order, then select all columns to display.')
)
def __init__(self, table, *args, **kwargs):

View File

@ -18,6 +18,7 @@ from django.utils.decorators import method_decorator
from django.utils.html import escape
from django.utils.http import is_safe_url
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
from django.views.decorators.csrf import requires_csrf_token
from django.views.defaults import ERROR_500_TEMPLATE_NAME
from django.views.generic import View
@ -206,7 +207,7 @@ class ObjectListView(View):
request.user.config.set(preference_name, form.cleaned_data['columns'], commit=True)
elif 'clear' in request.POST:
request.user.config.clear(preference_name, commit=True)
messages.success(request, "Your preferences have been updated.")
messages.success(request, _('Your preferences have been updated.'))
return redirect(request.get_full_path())
@ -532,7 +533,7 @@ class ObjectImportView(GetReturnURLMixin, View):
if not model_form.errors:
logger.info(f"Import object {obj} (PK: {obj.pk})")
messages.success(request, mark_safe('Imported object: <a href="{}">{}</a>'.format(
messages.success(request, mark_safe(_('Imported object: <a href="{}">{}</a>').format(
obj.get_absolute_url(), obj
)))
@ -946,7 +947,7 @@ class ComponentCreateView(GetReturnURLMixin, View):
for component_form in new_components:
component_form.save()
messages.success(request, "Added {} {}".format(
messages.success(request, _('Added {} {}').format(
len(new_components), self.model._meta.verbose_name_plural
))
if '_addanother' in request.POST:

View File

@ -1,5 +1,6 @@
import django_filters
from django.db.models import Q
from django.utils.translation import gettext as _
from dcim.models import DeviceRole, Interface, Platform, Region, Site
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet, LocalConfigContextFilterSet
@ -37,50 +38,50 @@ class ClusterGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
class ClusterFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
group_id = django_filters.ModelMultipleChoiceFilter(
queryset=ClusterGroup.objects.all(),
label='Parent group (ID)',
label=_('Parent group (ID)'),
)
group = django_filters.ModelMultipleChoiceFilter(
field_name='group__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
label='Parent group (slug)',
label=_('Parent group (slug)'),
)
type_id = django_filters.ModelMultipleChoiceFilter(
queryset=ClusterType.objects.all(),
label='Cluster type (ID)',
label=_('Cluster type (ID)'),
)
type = django_filters.ModelMultipleChoiceFilter(
field_name='type__slug',
queryset=ClusterType.objects.all(),
to_field_name='slug',
label='Cluster type (slug)',
label=_('Cluster type (slug)'),
)
tag = TagFilter()
@ -106,7 +107,7 @@ class VirtualMachineFilterSet(
):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
status = django_filters.MultipleChoiceFilter(
choices=VirtualMachineStatusChoices,
@ -115,76 +116,76 @@ class VirtualMachineFilterSet(
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__group',
queryset=ClusterGroup.objects.all(),
label='Cluster group (ID)',
label=_('Cluster group (ID)'),
)
cluster_group = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__group__slug',
queryset=ClusterGroup.objects.all(),
to_field_name='slug',
label='Cluster group (slug)',
label=_('Cluster group (slug)'),
)
cluster_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__type',
queryset=ClusterType.objects.all(),
label='Cluster type (ID)',
label=_('Cluster type (ID)'),
)
cluster_type = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__type__slug',
queryset=ClusterType.objects.all(),
to_field_name='slug',
label='Cluster type (slug)',
label=_('Cluster type (slug)'),
)
cluster_id = django_filters.ModelMultipleChoiceFilter(
queryset=Cluster.objects.all(),
label='Cluster (ID)',
label=_('Cluster (ID)'),
)
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='cluster__site__region',
lookup_expr='in',
label='Region (ID)',
label=_('Region (ID)'),
)
region = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='cluster__site__region',
lookup_expr='in',
to_field_name='slug',
label='Region (slug)',
label=_('Region (slug)'),
)
site_id = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__site',
queryset=Site.objects.all(),
label='Site (ID)',
label=_('Site (ID)'),
)
site = django_filters.ModelMultipleChoiceFilter(
field_name='cluster__site__slug',
queryset=Site.objects.all(),
to_field_name='slug',
label='Site (slug)',
label=_('Site (slug)'),
)
role_id = django_filters.ModelMultipleChoiceFilter(
queryset=DeviceRole.objects.all(),
label='Role (ID)',
label=_('Role (ID)'),
)
role = django_filters.ModelMultipleChoiceFilter(
field_name='role__slug',
queryset=DeviceRole.objects.all(),
to_field_name='slug',
label='Role (slug)',
label=_('Role (slug)'),
)
platform_id = django_filters.ModelMultipleChoiceFilter(
queryset=Platform.objects.all(),
label='Platform (ID)',
label=_('Platform (ID)'),
)
platform = django_filters.ModelMultipleChoiceFilter(
field_name='platform__slug',
queryset=Platform.objects.all(),
to_field_name='slug',
label='Platform (slug)',
label=_('Platform (slug)'),
)
mac_address = MultiValueMACAddressFilter(
field_name='interfaces__mac_address',
label='MAC address',
label=_('MAC address'),
)
tag = TagFilter()
@ -204,21 +205,21 @@ class VirtualMachineFilterSet(
class InterfaceFilterSet(BaseFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
label=_('Search'),
)
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_machine',
queryset=VirtualMachine.objects.all(),
label='Virtual machine (ID)',
label=_('Virtual machine (ID)'),
)
virtual_machine = django_filters.ModelMultipleChoiceFilter(
field_name='virtual_machine__name',
queryset=VirtualMachine.objects.all(),
to_field_name='name',
label='Virtual machine',
label=_('Virtual machine'),
)
mac_address = MultiValueMACAddressFilter(
label='MAC address',
label=_('MAC address'),
)
tag = TagFilter()

View File

@ -1,5 +1,6 @@
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from dcim.choices import InterfaceModeChoices
from dcim.constants import INTERFACE_MTU_MAX, INTERFACE_MTU_MIN
@ -98,25 +99,25 @@ class ClusterCSVForm(CustomFieldModelCSVForm):
type = CSVModelChoiceField(
queryset=ClusterType.objects.all(),
to_field_name='name',
help_text='Type of cluster'
help_text=_('Type of cluster')
)
group = CSVModelChoiceField(
queryset=ClusterGroup.objects.all(),
to_field_name='name',
required=False,
help_text='Assigned cluster group'
help_text=_('Assigned cluster group')
)
site = CSVModelChoiceField(
queryset=Site.objects.all(),
to_field_name='name',
required=False,
help_text='Assigned site'
help_text=_('Assigned site')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
to_field_name='name',
required=False,
help_text='Assigned tenant'
help_text=_('Assigned tenant')
)
class Meta:
@ -147,7 +148,7 @@ class ClusterBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
label=_('Comments')
)
class Meta:
@ -387,12 +388,12 @@ class VirtualMachineCSVForm(CustomFieldModelCSVForm):
status = CSVChoiceField(
choices=VirtualMachineStatusChoices,
required=False,
help_text='Operational status of device'
help_text=_('Operational status of device')
)
cluster = CSVModelChoiceField(
queryset=Cluster.objects.all(),
to_field_name='name',
help_text='Assigned cluster'
help_text=_('Assigned cluster')
)
role = CSVModelChoiceField(
queryset=DeviceRole.objects.filter(
@ -400,19 +401,19 @@ class VirtualMachineCSVForm(CustomFieldModelCSVForm):
),
required=False,
to_field_name='name',
help_text='Functional role'
help_text=_('Functional role')
)
tenant = CSVModelChoiceField(
queryset=Tenant.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned tenant'
help_text=_('Assigned tenant')
)
platform = CSVModelChoiceField(
queryset=Platform.objects.all(),
required=False,
to_field_name='name',
help_text='Assigned platform'
help_text=_('Assigned platform')
)
class Meta:
@ -456,19 +457,19 @@ class VirtualMachineBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldB
)
vcpus = forms.IntegerField(
required=False,
label='vCPUs'
label=_('vCPUs')
)
memory = forms.IntegerField(
required=False,
label='Memory (MB)'
label=_('Memory (MB)')
)
disk = forms.IntegerField(
required=False,
label='Disk (GB)'
label=_('Disk (GB)')
)
comments = CommentField(
widget=SmallTextarea,
label='Comments'
label=_('Comments')
)
class Meta:
@ -485,7 +486,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
]
q = forms.CharField(
required=False,
label='Search'
label=_('Search')
)
cluster_group = DynamicModelMultipleChoiceField(
queryset=ClusterGroup.objects.all(),
@ -508,7 +509,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
cluster_id = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(),
required=False,
label='Cluster'
label=_('Cluster')
)
region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
@ -558,7 +559,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
)
mac_address = forms.CharField(
required=False,
label='MAC address'
label=_('MAC address')
)
tag = TagFilterField(model)
@ -606,7 +607,7 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm):
'mode': StaticSelect2()
}
labels = {
'mode': '802.1Q Mode',
'mode': _('802.1Q Mode'),
}
help_texts = {
'mode': INTERFACE_MODE_HELP_TEXT,
@ -645,7 +646,7 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
widget=forms.HiddenInput()
)
name_pattern = ExpandableNameField(
label='Name'
label=_('Name')
)
type = forms.ChoiceField(
choices=VMInterfaceTypeChoices,
@ -660,11 +661,11 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
required=False,
min_value=INTERFACE_MTU_MIN,
max_value=INTERFACE_MTU_MAX,
label='MTU'
label=_('MTU')
)
mac_address = forms.CharField(
required=False,
label='MAC Address'
label=_('MAC Address')
)
description = forms.CharField(
max_length=100,
@ -732,7 +733,7 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
required=False,
min_value=INTERFACE_MTU_MIN,
max_value=INTERFACE_MTU_MAX,
label='MTU'
label=_('MTU')
)
description = forms.CharField(
max_length=100,
@ -795,7 +796,7 @@ class VirtualMachineBulkAddComponentForm(BootstrapMixin, forms.Form):
widget=forms.MultipleHiddenInput()
)
name_pattern = ExpandableNameField(
label='Name'
label=_('Name')
)
def clean_tags(self):

View File

@ -3,6 +3,7 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.translation import gettext as _
from taggit.managers import TaggableManager
from dcim.models import Device
@ -220,7 +221,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
max_length=50,
choices=VirtualMachineStatusChoices,
default=VirtualMachineStatusChoices.STATUS_ACTIVE,
verbose_name='Status'
verbose_name=_('Status')
)
role = models.ForeignKey(
to='dcim.DeviceRole',
@ -236,7 +237,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
related_name='+',
blank=True,
null=True,
verbose_name='Primary IPv4'
verbose_name=_('Primary IPv4')
)
primary_ip6 = models.OneToOneField(
to='ipam.IPAddress',
@ -244,22 +245,22 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
related_name='+',
blank=True,
null=True,
verbose_name='Primary IPv6'
verbose_name=_('Primary IPv6')
)
vcpus = models.PositiveSmallIntegerField(
blank=True,
null=True,
verbose_name='vCPUs'
verbose_name=_('vCPUs')
)
memory = models.PositiveIntegerField(
blank=True,
null=True,
verbose_name='Memory (MB)'
verbose_name=_('Memory (MB)')
)
disk = models.PositiveIntegerField(
blank=True,
null=True,
verbose_name='Disk (GB)'
verbose_name=_('Disk (GB)')
)
comments = models.TextField(
blank=True

View File

@ -1,5 +1,6 @@
import django_tables2 as tables
from django_tables2.utils import Accessor
from django.utils.translation import gettext as _
from dcim.models import Interface
from tenancy.tables import COL_TENANT
@ -51,7 +52,7 @@ class ClusterTypeTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
cluster_count = tables.Column(
verbose_name='Clusters'
verbose_name=_('Clusters')
)
actions = tables.TemplateColumn(
template_code=CLUSTERTYPE_ACTIONS,
@ -73,7 +74,7 @@ class ClusterGroupTable(BaseTable):
pk = ToggleColumn()
name = tables.LinkColumn()
cluster_count = tables.Column(
verbose_name='Clusters'
verbose_name=_('Clusters')
)
actions = tables.TemplateColumn(
template_code=CLUSTERGROUP_ACTIONS,
@ -104,11 +105,11 @@ class ClusterTable(BaseTable):
)
device_count = tables.TemplateColumn(
template_code=CLUSTER_DEVICE_COUNT,
verbose_name='Devices'
verbose_name=_('Devices')
)
vm_count = tables.TemplateColumn(
template_code=CLUSTER_VM_COUNT,
verbose_name='VMs'
verbose_name=_('VMs')
)
tags = TagColumn(
url_name='virtualization:cluster_list'
@ -148,16 +149,16 @@ class VirtualMachineDetailTable(VirtualMachineTable):
primary_ip4 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip4.pk')],
verbose_name='IPv4 Address'
verbose_name=_('IPv4 Address')
)
primary_ip6 = tables.LinkColumn(
viewname='ipam:ipaddress',
args=[Accessor('primary_ip6.pk')],
verbose_name='IPv6 Address'
verbose_name=_('IPv6 Address')
)
primary_ip = tables.TemplateColumn(
orderable=False,
verbose_name='IP Address',
verbose_name=_('IP Address'),
template_code=VIRTUALMACHINE_PRIMARY_IP
)
tags = TagColumn(

View File

@ -4,6 +4,7 @@ from django.db import transaction
from django.db.models import Count
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import gettext as _
from django.views.generic import View
from dcim.models import Device, Interface
@ -195,7 +196,7 @@ class ClusterAddDevicesView(PermissionRequiredMixin, View):
device.cluster = cluster
device.save()
messages.success(request, "Added {} devices to cluster {}".format(
messages.success(request, _('Added {} devices to cluster {}').format(
len(device_pks), cluster
))
return redirect(cluster.get_absolute_url())
@ -228,7 +229,7 @@ class ClusterRemoveDevicesView(PermissionRequiredMixin, View):
device.cluster = None
device.save()
messages.success(request, "Removed {} devices from cluster {}".format(
messages.success(request, _('Removed {} devices from cluster {}').format(
len(device_pks), cluster
))
return redirect(cluster.get_absolute_url())