mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-27 02:48:38 -06:00
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:
parent
9c3b67048a
commit
e1441537ef
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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'
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
||||
|
@ -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
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
1968
netbox/locale/zh_Hans/LC_MESSAGES/django.po
Normal file
1968
netbox/locale/zh_Hans/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -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')
|
||||
)
|
||||
|
@ -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', '中文简体'),
|
||||
)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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')
|
||||
)
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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.")
|
||||
|
@ -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)'),
|
||||
)
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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', {
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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())
|
||||
|
Loading…
Reference in New Issue
Block a user