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.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class CircuitsConfig(AppConfig):
|
class CircuitsConfig(AppConfig):
|
||||||
name = "circuits"
|
name = "circuits"
|
||||||
verbose_name = "Circuits"
|
verbose_name = _('Circuits')
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
import circuits.signals
|
import circuits.signals
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Region, Site
|
from dcim.models import Region, Site
|
||||||
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||||
@ -21,31 +22,31 @@ __all__ = (
|
|||||||
class ProviderFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class ProviderFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='circuits__terminations__site__region',
|
field_name='circuits__terminations__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='circuits__terminations__site__region',
|
field_name='circuits__terminations__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='circuits__terminations__site',
|
field_name='circuits__terminations__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site',
|
label=_('Site'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='circuits__terminations__site__slug',
|
field_name='circuits__terminations__site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -75,27 +76,27 @@ class CircuitTypeFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilterSet):
|
class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
provider_id = django_filters.ModelMultipleChoiceFilter(
|
provider_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
label='Provider (ID)',
|
label=_('Provider (ID)'),
|
||||||
)
|
)
|
||||||
provider = django_filters.ModelMultipleChoiceFilter(
|
provider = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='provider__slug',
|
field_name='provider__slug',
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Provider (slug)',
|
label=_('Provider (slug)'),
|
||||||
)
|
)
|
||||||
type_id = django_filters.ModelMultipleChoiceFilter(
|
type_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
label='Circuit type (ID)',
|
label=_('Circuit type (ID)'),
|
||||||
)
|
)
|
||||||
type = django_filters.ModelMultipleChoiceFilter(
|
type = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='type__slug',
|
field_name='type__slug',
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Circuit type (slug)',
|
label=_('Circuit type (slug)'),
|
||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=CircuitStatusChoices,
|
choices=CircuitStatusChoices,
|
||||||
@ -104,26 +105,26 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, Cr
|
|||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='terminations__site',
|
field_name='terminations__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='terminations__site__slug',
|
field_name='terminations__site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='terminations__site__region',
|
field_name='terminations__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='terminations__site__region',
|
field_name='terminations__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -147,21 +148,21 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldFilterSet, TenancyFilterSet, Cr
|
|||||||
class CircuitTerminationFilterSet(BaseFilterSet):
|
class CircuitTerminationFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
circuit_id = django_filters.ModelMultipleChoiceFilter(
|
circuit_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Circuit.objects.all(),
|
queryset=Circuit.objects.all(),
|
||||||
label='Circuit',
|
label=_('Circuit'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Region, Site
|
from dcim.models import Region, Site
|
||||||
from extras.forms import (
|
from extras.forms import (
|
||||||
@ -64,30 +65,30 @@ class ProviderBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdi
|
|||||||
)
|
)
|
||||||
asn = forms.IntegerField(
|
asn = forms.IntegerField(
|
||||||
required=False,
|
required=False,
|
||||||
label='ASN'
|
label=_('ASN')
|
||||||
)
|
)
|
||||||
account = forms.CharField(
|
account = forms.CharField(
|
||||||
max_length=30,
|
max_length=30,
|
||||||
required=False,
|
required=False,
|
||||||
label='Account number'
|
label=_('Account number')
|
||||||
)
|
)
|
||||||
portal_url = forms.URLField(
|
portal_url = forms.URLField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Portal'
|
label=_('Portal')
|
||||||
)
|
)
|
||||||
noc_contact = forms.CharField(
|
noc_contact = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
widget=SmallTextarea,
|
widget=SmallTextarea,
|
||||||
label='NOC contact'
|
label=_('NOC contact')
|
||||||
)
|
)
|
||||||
admin_contact = forms.CharField(
|
admin_contact = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
widget=SmallTextarea,
|
widget=SmallTextarea,
|
||||||
label='Admin contact'
|
label=_('Admin contact')
|
||||||
)
|
)
|
||||||
comments = CommentField(
|
comments = CommentField(
|
||||||
widget=SmallTextarea,
|
widget=SmallTextarea,
|
||||||
label='Comments'
|
label=_('Comments')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -100,7 +101,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
model = Provider
|
model = Provider
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
region = DynamicModelMultipleChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -123,7 +124,7 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
)
|
)
|
||||||
asn = forms.IntegerField(
|
asn = forms.IntegerField(
|
||||||
required=False,
|
required=False,
|
||||||
label='ASN'
|
label=_('ASN')
|
||||||
)
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
@ -149,7 +150,7 @@ class CircuitTypeCSVForm(CSVModelForm):
|
|||||||
model = CircuitType
|
model = CircuitType
|
||||||
fields = CircuitType.csv_headers
|
fields = CircuitType.csv_headers
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'name': 'Name of circuit type',
|
'name': _('Name of circuit type'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -189,23 +190,23 @@ class CircuitCSVForm(CustomFieldModelCSVForm):
|
|||||||
provider = CSVModelChoiceField(
|
provider = CSVModelChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned provider'
|
help_text=_('Assigned provider')
|
||||||
)
|
)
|
||||||
type = CSVModelChoiceField(
|
type = CSVModelChoiceField(
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Type of circuit'
|
help_text=_('Type of circuit')
|
||||||
)
|
)
|
||||||
status = CSVChoiceField(
|
status = CSVChoiceField(
|
||||||
choices=CircuitStatusChoices,
|
choices=CircuitStatusChoices,
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Operational status'
|
help_text=_('Operational status')
|
||||||
)
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned tenant'
|
help_text=_('Assigned tenant')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -240,7 +241,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
|||||||
)
|
)
|
||||||
commit_rate = forms.IntegerField(
|
commit_rate = forms.IntegerField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Commit rate (Kbps)'
|
label=_('Commit rate (Kbps)')
|
||||||
)
|
)
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
@ -248,7 +249,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
|||||||
)
|
)
|
||||||
comments = CommentField(
|
comments = CommentField(
|
||||||
widget=SmallTextarea,
|
widget=SmallTextarea,
|
||||||
label='Comments'
|
label=_('Comments')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -264,7 +265,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
|
|||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
type = DynamicModelMultipleChoiceField(
|
type = DynamicModelMultipleChoiceField(
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
@ -309,7 +310,7 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm
|
|||||||
commit_rate = forms.IntegerField(
|
commit_rate = forms.IntegerField(
|
||||||
required=False,
|
required=False,
|
||||||
min_value=0,
|
min_value=0,
|
||||||
label='Commit rate (Kbps)'
|
label=_('Commit rate (Kbps)')
|
||||||
)
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.contrib.contenttypes.fields import GenericRelation
|
from django.contrib.contenttypes.fields import GenericRelation
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from dcim.constants import CONNECTION_STATUS_CHOICES
|
from dcim.constants import CONNECTION_STATUS_CHOICES
|
||||||
@ -38,25 +39,25 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
|
|||||||
asn = ASNField(
|
asn = ASNField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='ASN',
|
verbose_name=_('ASN'),
|
||||||
help_text='32-bit autonomous system number'
|
help_text=_('32-bit autonomous system number')
|
||||||
)
|
)
|
||||||
account = models.CharField(
|
account = models.CharField(
|
||||||
max_length=30,
|
max_length=30,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Account number'
|
verbose_name=_('Account number')
|
||||||
)
|
)
|
||||||
portal_url = models.URLField(
|
portal_url = models.URLField(
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Portal URL'
|
verbose_name=_('Portal URL')
|
||||||
)
|
)
|
||||||
noc_contact = models.TextField(
|
noc_contact = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='NOC contact'
|
verbose_name=_('NOC contact')
|
||||||
)
|
)
|
||||||
admin_contact = models.TextField(
|
admin_contact = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Admin contact'
|
verbose_name=_('Admin contact')
|
||||||
)
|
)
|
||||||
comments = models.TextField(
|
comments = models.TextField(
|
||||||
blank=True
|
blank=True
|
||||||
@ -143,7 +144,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
|
|||||||
"""
|
"""
|
||||||
cid = models.CharField(
|
cid = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Circuit ID'
|
verbose_name=_('Circuit ID')
|
||||||
)
|
)
|
||||||
provider = models.ForeignKey(
|
provider = models.ForeignKey(
|
||||||
to='circuits.Provider',
|
to='circuits.Provider',
|
||||||
@ -170,7 +171,7 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
|
|||||||
install_date = models.DateField(
|
install_date = models.DateField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Date installed'
|
verbose_name=_('Date installed')
|
||||||
)
|
)
|
||||||
commit_rate = models.PositiveIntegerField(
|
commit_rate = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -258,7 +259,7 @@ class CircuitTermination(CableTermination):
|
|||||||
term_side = models.CharField(
|
term_side = models.CharField(
|
||||||
max_length=1,
|
max_length=1,
|
||||||
choices=CircuitTerminationSideChoices,
|
choices=CircuitTerminationSideChoices,
|
||||||
verbose_name='Termination'
|
verbose_name=_('Termination')
|
||||||
)
|
)
|
||||||
site = models.ForeignKey(
|
site = models.ForeignKey(
|
||||||
to='dcim.Site',
|
to='dcim.Site',
|
||||||
@ -277,23 +278,23 @@ class CircuitTermination(CableTermination):
|
|||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
port_speed = models.PositiveIntegerField(
|
port_speed = models.PositiveIntegerField(
|
||||||
verbose_name='Port speed (Kbps)'
|
verbose_name=_('Port speed (Kbps)')
|
||||||
)
|
)
|
||||||
upstream_speed = models.PositiveIntegerField(
|
upstream_speed = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Upstream speed (Kbps)',
|
verbose_name=_('Upstream speed (Kbps)'),
|
||||||
help_text='Upstream speed, if different from port speed'
|
help_text=_('Upstream speed, if different from port speed')
|
||||||
)
|
)
|
||||||
xconnect_id = models.CharField(
|
xconnect_id = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Cross-connect ID'
|
verbose_name=_('Cross-connect ID')
|
||||||
)
|
)
|
||||||
pp_info = models.CharField(
|
pp_info = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Patch panel/port(s)'
|
verbose_name=_('Patch panel/port(s)')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from tenancy.tables import COL_TENANT
|
from tenancy.tables import COL_TENANT
|
||||||
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
||||||
@ -29,7 +30,7 @@ class ProviderTable(BaseTable):
|
|||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
circuit_count = tables.Column(
|
circuit_count = tables.Column(
|
||||||
accessor=Accessor('count_circuits'),
|
accessor=Accessor('count_circuits'),
|
||||||
verbose_name='Circuits'
|
verbose_name=_('Circuits')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='circuits:provider_list'
|
url_name='circuits:provider_list'
|
||||||
@ -51,7 +52,7 @@ class CircuitTypeTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
circuit_count = tables.Column(
|
circuit_count = tables.Column(
|
||||||
verbose_name='Circuits'
|
verbose_name=_('Circuits')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=CIRCUITTYPE_ACTIONS,
|
template_code=CIRCUITTYPE_ACTIONS,
|
||||||
@ -72,7 +73,7 @@ class CircuitTypeTable(BaseTable):
|
|||||||
class CircuitTable(BaseTable):
|
class CircuitTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
cid = tables.LinkColumn(
|
cid = tables.LinkColumn(
|
||||||
verbose_name='ID'
|
verbose_name=_('ID')
|
||||||
)
|
)
|
||||||
provider = tables.LinkColumn(
|
provider = tables.LinkColumn(
|
||||||
viewname='circuits:provider',
|
viewname='circuits:provider',
|
||||||
@ -85,10 +86,10 @@ class CircuitTable(BaseTable):
|
|||||||
template_code=COL_TENANT
|
template_code=COL_TENANT
|
||||||
)
|
)
|
||||||
a_side = tables.Column(
|
a_side = tables.Column(
|
||||||
verbose_name='A Side'
|
verbose_name=_('A Side')
|
||||||
)
|
)
|
||||||
z_side = tables.Column(
|
z_side = tables.Column(
|
||||||
verbose_name='Z Side'
|
verbose_name=_('Z Side')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='circuits:circuit_list'
|
url_name='circuits:circuit_list'
|
||||||
|
@ -5,6 +5,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
|
|||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import Count, OuterRef, Subquery
|
from django.db.models import Count, OuterRef, Subquery
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
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.views.generic import View
|
||||||
from django_tables2 import RequestConfig
|
from django_tables2 import RequestConfig
|
||||||
|
|
||||||
@ -250,7 +251,7 @@ def circuit_terminations_swap(request, pk):
|
|||||||
else:
|
else:
|
||||||
termination_z.term_side = 'A'
|
termination_z.term_side = 'A'
|
||||||
termination_z.save()
|
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)
|
return redirect('circuits:circuit', pk=circuit.pk)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
class DCIMConfig(AppConfig):
|
class DCIMConfig(AppConfig):
|
||||||
name = "dcim"
|
name = "dcim"
|
||||||
verbose_name = "DCIM"
|
verbose_name = _('DCIM')
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet
|
from extras.filters import CustomFieldFilterSet, LocalConfigContextFilterSet, CreatedUpdatedFilterSet
|
||||||
from tenancy.filters import TenancyFilterSet
|
from tenancy.filters import TenancyFilterSet
|
||||||
@ -63,13 +64,13 @@ __all__ = (
|
|||||||
class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||||
parent_id = django_filters.ModelMultipleChoiceFilter(
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
label='Parent region (ID)',
|
label=_('Parent region (ID)'),
|
||||||
)
|
)
|
||||||
parent = django_filters.ModelMultipleChoiceFilter(
|
parent = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='parent__slug',
|
field_name='parent__slug',
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Parent region (slug)',
|
label=_('Parent region (slug)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -80,7 +81,7 @@ class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=SiteStatusChoices,
|
choices=SiteStatusChoices,
|
||||||
@ -90,14 +91,14 @@ class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
|
|||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='region',
|
field_name='region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='region',
|
field_name='region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -134,34 +135,34 @@ class RackGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
parent_id = django_filters.ModelMultipleChoiceFilter(
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=RackGroup.objects.all(),
|
queryset=RackGroup.objects.all(),
|
||||||
label='Rack group (ID)',
|
label=_('Rack group (ID)'),
|
||||||
)
|
)
|
||||||
parent = django_filters.ModelMultipleChoiceFilter(
|
parent = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='parent__slug',
|
field_name='parent__slug',
|
||||||
queryset=RackGroup.objects.all(),
|
queryset=RackGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Rack group (slug)',
|
label=_('Rack group (slug)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -179,43 +180,43 @@ class RackRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
group_id = TreeNodeMultipleChoiceFilter(
|
group_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=RackGroup.objects.all(),
|
queryset=RackGroup.objects.all(),
|
||||||
field_name='group',
|
field_name='group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Rack group (ID)',
|
label=_('Rack group (ID)'),
|
||||||
)
|
)
|
||||||
group = TreeNodeMultipleChoiceFilter(
|
group = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=RackGroup.objects.all(),
|
queryset=RackGroup.objects.all(),
|
||||||
field_name='group',
|
field_name='group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Rack group (slug)',
|
label=_('Rack group (slug)'),
|
||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=RackStatusChoices,
|
choices=RackStatusChoices,
|
||||||
@ -223,13 +224,13 @@ class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
|
|||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=RackRole.objects.all(),
|
queryset=RackRole.objects.all(),
|
||||||
label='Role (ID)',
|
label=_('Role (ID)'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='role__slug',
|
field_name='role__slug',
|
||||||
queryset=RackRole.objects.all(),
|
queryset=RackRole.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Role (slug)',
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
serial = django_filters.CharFilter(
|
serial = django_filters.CharFilter(
|
||||||
lookup_expr='iexact'
|
lookup_expr='iexact'
|
||||||
@ -258,45 +259,45 @@ class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
|
|||||||
class RackReservationFilterSet(BaseFilterSet, TenancyFilterSet):
|
class RackReservationFilterSet(BaseFilterSet, TenancyFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
rack_id = django_filters.ModelMultipleChoiceFilter(
|
rack_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
label='Rack (ID)',
|
label=_('Rack (ID)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='rack__site',
|
field_name='rack__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='rack__site__slug',
|
field_name='rack__site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
group_id = TreeNodeMultipleChoiceFilter(
|
group_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=RackGroup.objects.all(),
|
queryset=RackGroup.objects.all(),
|
||||||
field_name='rack__group',
|
field_name='rack__group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Rack group (ID)',
|
label=_('Rack group (ID)'),
|
||||||
)
|
)
|
||||||
group = TreeNodeMultipleChoiceFilter(
|
group = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=RackGroup.objects.all(),
|
queryset=RackGroup.objects.all(),
|
||||||
field_name='rack__group',
|
field_name='rack__group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Rack group (slug)',
|
label=_('Rack group (slug)'),
|
||||||
)
|
)
|
||||||
user_id = django_filters.ModelMultipleChoiceFilter(
|
user_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=User.objects.all(),
|
queryset=User.objects.all(),
|
||||||
label='User (ID)',
|
label=_('User (ID)'),
|
||||||
)
|
)
|
||||||
user = django_filters.ModelMultipleChoiceFilter(
|
user = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='user',
|
field_name='user',
|
||||||
queryset=User.objects.all(),
|
queryset=User.objects.all(),
|
||||||
to_field_name='username',
|
to_field_name='username',
|
||||||
label='User (name)',
|
label=_('User (name)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -324,45 +325,45 @@ class ManufacturerFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class DeviceTypeFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class DeviceTypeFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
label='Manufacturer (ID)',
|
label=_('Manufacturer (ID)'),
|
||||||
)
|
)
|
||||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='manufacturer__slug',
|
field_name='manufacturer__slug',
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Manufacturer (slug)',
|
label=_('Manufacturer (slug)'),
|
||||||
)
|
)
|
||||||
console_ports = django_filters.BooleanFilter(
|
console_ports = django_filters.BooleanFilter(
|
||||||
method='_console_ports',
|
method='_console_ports',
|
||||||
label='Has console ports',
|
label=_('Has console ports'),
|
||||||
)
|
)
|
||||||
console_server_ports = django_filters.BooleanFilter(
|
console_server_ports = django_filters.BooleanFilter(
|
||||||
method='_console_server_ports',
|
method='_console_server_ports',
|
||||||
label='Has console server ports',
|
label=_('Has console server ports'),
|
||||||
)
|
)
|
||||||
power_ports = django_filters.BooleanFilter(
|
power_ports = django_filters.BooleanFilter(
|
||||||
method='_power_ports',
|
method='_power_ports',
|
||||||
label='Has power ports',
|
label=_('Has power ports'),
|
||||||
)
|
)
|
||||||
power_outlets = django_filters.BooleanFilter(
|
power_outlets = django_filters.BooleanFilter(
|
||||||
method='_power_outlets',
|
method='_power_outlets',
|
||||||
label='Has power outlets',
|
label=_('Has power outlets'),
|
||||||
)
|
)
|
||||||
interfaces = django_filters.BooleanFilter(
|
interfaces = django_filters.BooleanFilter(
|
||||||
method='_interfaces',
|
method='_interfaces',
|
||||||
label='Has interfaces',
|
label=_('Has interfaces'),
|
||||||
)
|
)
|
||||||
pass_through_ports = django_filters.BooleanFilter(
|
pass_through_ports = django_filters.BooleanFilter(
|
||||||
method='_pass_through_ports',
|
method='_pass_through_ports',
|
||||||
label='Has pass-through ports',
|
label=_('Has pass-through ports'),
|
||||||
)
|
)
|
||||||
device_bays = django_filters.BooleanFilter(
|
device_bays = django_filters.BooleanFilter(
|
||||||
method='_device_bays',
|
method='_device_bays',
|
||||||
label='Has device bays',
|
label=_('Has device bays'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -411,7 +412,7 @@ class DeviceTypeComponentFilterSet(NameSlugSearchFilterSet):
|
|||||||
devicetype_id = django_filters.ModelMultipleChoiceFilter(
|
devicetype_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=DeviceType.objects.all(),
|
queryset=DeviceType.objects.all(),
|
||||||
field_name='device_type_id',
|
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(
|
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='manufacturer',
|
field_name='manufacturer',
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
label='Manufacturer (ID)',
|
label=_('Manufacturer (ID)'),
|
||||||
)
|
)
|
||||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='manufacturer__slug',
|
field_name='manufacturer__slug',
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Manufacturer (slug)',
|
label=_('Manufacturer (slug)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -505,87 +506,87 @@ class DeviceFilterSet(
|
|||||||
):
|
):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device_type__manufacturer',
|
field_name='device_type__manufacturer',
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
label='Manufacturer (ID)',
|
label=_('Manufacturer (ID)'),
|
||||||
)
|
)
|
||||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device_type__manufacturer__slug',
|
field_name='device_type__manufacturer__slug',
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Manufacturer (slug)',
|
label=_('Manufacturer (slug)'),
|
||||||
)
|
)
|
||||||
device_type_id = django_filters.ModelMultipleChoiceFilter(
|
device_type_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=DeviceType.objects.all(),
|
queryset=DeviceType.objects.all(),
|
||||||
label='Device type (ID)',
|
label=_('Device type (ID)'),
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device_role_id',
|
field_name='device_role_id',
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
label='Role (ID)',
|
label=_('Role (ID)'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device_role__slug',
|
field_name='device_role__slug',
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Role (slug)',
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
platform_id = django_filters.ModelMultipleChoiceFilter(
|
platform_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
label='Platform (ID)',
|
label=_('Platform (ID)'),
|
||||||
)
|
)
|
||||||
platform = django_filters.ModelMultipleChoiceFilter(
|
platform = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='platform__slug',
|
field_name='platform__slug',
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Platform (slug)',
|
label=_('Platform (slug)'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site name (slug)',
|
label=_('Site name (slug)'),
|
||||||
)
|
)
|
||||||
rack_group_id = TreeNodeMultipleChoiceFilter(
|
rack_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=RackGroup.objects.all(),
|
queryset=RackGroup.objects.all(),
|
||||||
field_name='rack__group',
|
field_name='rack__group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Rack group (ID)',
|
label=_('Rack group (ID)'),
|
||||||
)
|
)
|
||||||
rack_id = django_filters.ModelMultipleChoiceFilter(
|
rack_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='rack',
|
field_name='rack',
|
||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
label='Rack (ID)',
|
label=_('Rack (ID)'),
|
||||||
)
|
)
|
||||||
cluster_id = django_filters.ModelMultipleChoiceFilter(
|
cluster_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
label='VM cluster (ID)',
|
label=_('VM cluster (ID)'),
|
||||||
)
|
)
|
||||||
model = django_filters.ModelMultipleChoiceFilter(
|
model = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device_type__slug',
|
field_name='device_type__slug',
|
||||||
queryset=DeviceType.objects.all(),
|
queryset=DeviceType.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Device model (slug)',
|
label=_('Device model (slug)'),
|
||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=DeviceStatusChoices,
|
choices=DeviceStatusChoices,
|
||||||
@ -593,55 +594,55 @@ class DeviceFilterSet(
|
|||||||
)
|
)
|
||||||
is_full_depth = django_filters.BooleanFilter(
|
is_full_depth = django_filters.BooleanFilter(
|
||||||
field_name='device_type__is_full_depth',
|
field_name='device_type__is_full_depth',
|
||||||
label='Is full depth',
|
label=_('Is full depth'),
|
||||||
)
|
)
|
||||||
mac_address = MultiValueMACAddressFilter(
|
mac_address = MultiValueMACAddressFilter(
|
||||||
field_name='interfaces__mac_address',
|
field_name='interfaces__mac_address',
|
||||||
label='MAC address',
|
label=_('MAC address'),
|
||||||
)
|
)
|
||||||
serial = django_filters.CharFilter(
|
serial = django_filters.CharFilter(
|
||||||
lookup_expr='iexact'
|
lookup_expr='iexact'
|
||||||
)
|
)
|
||||||
has_primary_ip = django_filters.BooleanFilter(
|
has_primary_ip = django_filters.BooleanFilter(
|
||||||
method='_has_primary_ip',
|
method='_has_primary_ip',
|
||||||
label='Has a primary IP',
|
label=_('Has a primary IP'),
|
||||||
)
|
)
|
||||||
virtual_chassis_id = django_filters.ModelMultipleChoiceFilter(
|
virtual_chassis_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='virtual_chassis',
|
field_name='virtual_chassis',
|
||||||
queryset=VirtualChassis.objects.all(),
|
queryset=VirtualChassis.objects.all(),
|
||||||
label='Virtual chassis (ID)',
|
label=_('Virtual chassis (ID)'),
|
||||||
)
|
)
|
||||||
virtual_chassis_member = django_filters.BooleanFilter(
|
virtual_chassis_member = django_filters.BooleanFilter(
|
||||||
method='_virtual_chassis_member',
|
method='_virtual_chassis_member',
|
||||||
label='Is a virtual chassis member'
|
label=_('Is a virtual chassis member')
|
||||||
)
|
)
|
||||||
console_ports = django_filters.BooleanFilter(
|
console_ports = django_filters.BooleanFilter(
|
||||||
method='_console_ports',
|
method='_console_ports',
|
||||||
label='Has console ports',
|
label=_('Has console ports'),
|
||||||
)
|
)
|
||||||
console_server_ports = django_filters.BooleanFilter(
|
console_server_ports = django_filters.BooleanFilter(
|
||||||
method='_console_server_ports',
|
method='_console_server_ports',
|
||||||
label='Has console server ports',
|
label=_('Has console server ports'),
|
||||||
)
|
)
|
||||||
power_ports = django_filters.BooleanFilter(
|
power_ports = django_filters.BooleanFilter(
|
||||||
method='_power_ports',
|
method='_power_ports',
|
||||||
label='Has power ports',
|
label=_('Has power ports'),
|
||||||
)
|
)
|
||||||
power_outlets = django_filters.BooleanFilter(
|
power_outlets = django_filters.BooleanFilter(
|
||||||
method='_power_outlets',
|
method='_power_outlets',
|
||||||
label='Has power outlets',
|
label=_('Has power outlets'),
|
||||||
)
|
)
|
||||||
interfaces = django_filters.BooleanFilter(
|
interfaces = django_filters.BooleanFilter(
|
||||||
method='_interfaces',
|
method='_interfaces',
|
||||||
label='Has interfaces',
|
label=_('Has interfaces'),
|
||||||
)
|
)
|
||||||
pass_through_ports = django_filters.BooleanFilter(
|
pass_through_ports = django_filters.BooleanFilter(
|
||||||
method='_pass_through_ports',
|
method='_pass_through_ports',
|
||||||
label='Has pass-through ports',
|
label=_('Has pass-through ports'),
|
||||||
)
|
)
|
||||||
device_bays = django_filters.BooleanFilter(
|
device_bays = django_filters.BooleanFilter(
|
||||||
method='_device_bays',
|
method='_device_bays',
|
||||||
label='Has device bays',
|
label=_('Has device bays'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -703,41 +704,41 @@ class DeviceFilterSet(
|
|||||||
class DeviceComponentFilterSet(django_filters.FilterSet):
|
class DeviceComponentFilterSet(django_filters.FilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='device__site__region',
|
field_name='device__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='device__site__region',
|
field_name='device__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device__site',
|
field_name='device__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device__site__slug',
|
field_name='device__site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site name (slug)',
|
label=_('Site name (slug)'),
|
||||||
)
|
)
|
||||||
device_id = django_filters.ModelMultipleChoiceFilter(
|
device_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
label='Device (ID)',
|
label=_('Device (ID)'),
|
||||||
)
|
)
|
||||||
device = django_filters.ModelMultipleChoiceFilter(
|
device = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device__name',
|
field_name='device__name',
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
label='Device (name)',
|
label=_('Device (name)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -817,19 +818,19 @@ class PowerOutletFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
|||||||
class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
# Override device and device_id filters from DeviceComponentFilterSet to match against any peer virtual chassis
|
# Override device and device_id filters from DeviceComponentFilterSet to match against any peer virtual chassis
|
||||||
# members
|
# members
|
||||||
device = MultiValueCharFilter(
|
device = MultiValueCharFilter(
|
||||||
method='filter_device',
|
method='filter_device',
|
||||||
field_name='name',
|
field_name='name',
|
||||||
label='Device',
|
label=_('Device'),
|
||||||
)
|
)
|
||||||
device_id = MultiValueNumberFilter(
|
device_id = MultiValueNumberFilter(
|
||||||
method='filter_device_id',
|
method='filter_device_id',
|
||||||
field_name='pk',
|
field_name='pk',
|
||||||
label='Device (ID)',
|
label=_('Device (ID)'),
|
||||||
)
|
)
|
||||||
cabled = django_filters.BooleanFilter(
|
cabled = django_filters.BooleanFilter(
|
||||||
field_name='cable',
|
field_name='cable',
|
||||||
@ -838,22 +839,22 @@ class InterfaceFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
|||||||
)
|
)
|
||||||
kind = django_filters.CharFilter(
|
kind = django_filters.CharFilter(
|
||||||
method='filter_kind',
|
method='filter_kind',
|
||||||
label='Kind of interface',
|
label=_('Kind of interface'),
|
||||||
)
|
)
|
||||||
lag_id = django_filters.ModelMultipleChoiceFilter(
|
lag_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='lag',
|
field_name='lag',
|
||||||
queryset=Interface.objects.all(),
|
queryset=Interface.objects.all(),
|
||||||
label='LAG interface (ID)',
|
label=_('LAG interface (ID)'),
|
||||||
)
|
)
|
||||||
mac_address = MultiValueMACAddressFilter()
|
mac_address = MultiValueMACAddressFilter()
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
vlan_id = django_filters.CharFilter(
|
vlan_id = django_filters.CharFilter(
|
||||||
method='filter_vlan_id',
|
method='filter_vlan_id',
|
||||||
label='Assigned VLAN'
|
label=_('Assigned VLAN')
|
||||||
)
|
)
|
||||||
vlan = django_filters.CharFilter(
|
vlan = django_filters.CharFilter(
|
||||||
method='filter_vlan',
|
method='filter_vlan',
|
||||||
label='Assigned VID'
|
label=_('Assigned VID')
|
||||||
)
|
)
|
||||||
type = django_filters.MultipleChoiceFilter(
|
type = django_filters.MultipleChoiceFilter(
|
||||||
choices=InterfaceTypeChoices,
|
choices=InterfaceTypeChoices,
|
||||||
@ -946,54 +947,54 @@ class DeviceBayFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
|||||||
class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='device__site__region',
|
field_name='device__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='device__site__region',
|
field_name='device__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device__site',
|
field_name='device__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device__site__slug',
|
field_name='device__site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site name (slug)',
|
label=_('Site name (slug)'),
|
||||||
)
|
)
|
||||||
device_id = django_filters.ModelChoiceFilter(
|
device_id = django_filters.ModelChoiceFilter(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
label='Device (ID)',
|
label=_('Device (ID)'),
|
||||||
)
|
)
|
||||||
device = django_filters.ModelChoiceFilter(
|
device = django_filters.ModelChoiceFilter(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
label='Device (name)',
|
label=_('Device (name)'),
|
||||||
)
|
)
|
||||||
parent_id = django_filters.ModelMultipleChoiceFilter(
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=InventoryItem.objects.all(),
|
queryset=InventoryItem.objects.all(),
|
||||||
label='Parent inventory item (ID)',
|
label=_('Parent inventory item (ID)'),
|
||||||
)
|
)
|
||||||
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
manufacturer_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
label='Manufacturer (ID)',
|
label=_('Manufacturer (ID)'),
|
||||||
)
|
)
|
||||||
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
manufacturer = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='manufacturer__slug',
|
field_name='manufacturer__slug',
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Manufacturer (slug)',
|
label=_('Manufacturer (slug)'),
|
||||||
)
|
)
|
||||||
serial = django_filters.CharFilter(
|
serial = django_filters.CharFilter(
|
||||||
lookup_expr='iexact'
|
lookup_expr='iexact'
|
||||||
@ -1019,42 +1020,42 @@ class InventoryItemFilterSet(BaseFilterSet, DeviceComponentFilterSet):
|
|||||||
class VirtualChassisFilterSet(BaseFilterSet):
|
class VirtualChassisFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='master__site__region',
|
field_name='master__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='master__site__region',
|
field_name='master__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='master__site',
|
field_name='master__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='master__site__slug',
|
field_name='master__site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site name (slug)',
|
label=_('Site name (slug)'),
|
||||||
)
|
)
|
||||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='master__tenant',
|
field_name='master__tenant',
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
label='Tenant (ID)',
|
label=_('Tenant (ID)'),
|
||||||
)
|
)
|
||||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='master__tenant__slug',
|
field_name='master__tenant__slug',
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Tenant (slug)',
|
label=_('Tenant (slug)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -1075,7 +1076,7 @@ class VirtualChassisFilterSet(BaseFilterSet):
|
|||||||
class CableFilterSet(BaseFilterSet):
|
class CableFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
type = django_filters.MultipleChoiceFilter(
|
type = django_filters.MultipleChoiceFilter(
|
||||||
choices=CableTypeChoices
|
choices=CableTypeChoices
|
||||||
@ -1138,7 +1139,7 @@ class CableFilterSet(BaseFilterSet):
|
|||||||
class ConsoleConnectionFilterSet(BaseFilterSet):
|
class ConsoleConnectionFilterSet(BaseFilterSet):
|
||||||
site = django_filters.CharFilter(
|
site = django_filters.CharFilter(
|
||||||
method='filter_site',
|
method='filter_site',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
device_id = MultiValueNumberFilter(
|
device_id = MultiValueNumberFilter(
|
||||||
method='filter_device'
|
method='filter_device'
|
||||||
@ -1169,7 +1170,7 @@ class ConsoleConnectionFilterSet(BaseFilterSet):
|
|||||||
class PowerConnectionFilterSet(BaseFilterSet):
|
class PowerConnectionFilterSet(BaseFilterSet):
|
||||||
site = django_filters.CharFilter(
|
site = django_filters.CharFilter(
|
||||||
method='filter_site',
|
method='filter_site',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
device_id = MultiValueNumberFilter(
|
device_id = MultiValueNumberFilter(
|
||||||
method='filter_device'
|
method='filter_device'
|
||||||
@ -1200,7 +1201,7 @@ class PowerConnectionFilterSet(BaseFilterSet):
|
|||||||
class InterfaceConnectionFilterSet(BaseFilterSet):
|
class InterfaceConnectionFilterSet(BaseFilterSet):
|
||||||
site = django_filters.CharFilter(
|
site = django_filters.CharFilter(
|
||||||
method='filter_site',
|
method='filter_site',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
device_id = MultiValueNumberFilter(
|
device_id = MultiValueNumberFilter(
|
||||||
method='filter_device'
|
method='filter_device'
|
||||||
@ -1234,36 +1235,36 @@ class InterfaceConnectionFilterSet(BaseFilterSet):
|
|||||||
class PowerPanelFilterSet(BaseFilterSet):
|
class PowerPanelFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site name (slug)',
|
label=_('Site name (slug)'),
|
||||||
)
|
)
|
||||||
rack_group_id = TreeNodeMultipleChoiceFilter(
|
rack_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=RackGroup.objects.all(),
|
queryset=RackGroup.objects.all(),
|
||||||
field_name='rack_group',
|
field_name='rack_group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Rack group (ID)',
|
label=_('Rack group (ID)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -1282,40 +1283,40 @@ class PowerPanelFilterSet(BaseFilterSet):
|
|||||||
class PowerFeedFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class PowerFeedFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='power_panel__site__region',
|
field_name='power_panel__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='power_panel__site__region',
|
field_name='power_panel__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='power_panel__site',
|
field_name='power_panel__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='power_panel__site__slug',
|
field_name='power_panel__site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site name (slug)',
|
label=_('Site name (slug)'),
|
||||||
)
|
)
|
||||||
power_panel_id = django_filters.ModelMultipleChoiceFilter(
|
power_panel_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=PowerPanel.objects.all(),
|
queryset=PowerPanel.objects.all(),
|
||||||
label='Power panel (ID)',
|
label=_('Power panel (ID)'),
|
||||||
)
|
)
|
||||||
rack_id = django_filters.ModelMultipleChoiceFilter(
|
rack_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='rack',
|
field_name='rack',
|
||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
label='Rack (ID)',
|
label=_('Rack (ID)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
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.db.models import Count, F, ProtectedError, Sum
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from mptt.models import MPTTModel, TreeForeignKey
|
from mptt.models import MPTTModel, TreeForeignKey
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
from timezone_field import TimeZoneField
|
from timezone_field import TimeZoneField
|
||||||
@ -182,13 +183,13 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
facility = models.CharField(
|
facility = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Local facility ID or description'
|
help_text=_('Local facility ID or description')
|
||||||
)
|
)
|
||||||
asn = ASNField(
|
asn = ASNField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='ASN',
|
verbose_name=_('ASN'),
|
||||||
help_text='32-bit autonomous system number'
|
help_text=_('32-bit autonomous system number')
|
||||||
)
|
)
|
||||||
time_zone = TimeZoneField(
|
time_zone = TimeZoneField(
|
||||||
blank=True
|
blank=True
|
||||||
@ -210,14 +211,14 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
decimal_places=6,
|
decimal_places=6,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text='GPS coordinate (latitude)'
|
help_text=_('GPS coordinate (latitude)')
|
||||||
)
|
)
|
||||||
longitude = models.DecimalField(
|
longitude = models.DecimalField(
|
||||||
max_digits=9,
|
max_digits=9,
|
||||||
decimal_places=6,
|
decimal_places=6,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text='GPS coordinate (longitude)'
|
help_text=_('GPS coordinate (longitude)')
|
||||||
)
|
)
|
||||||
contact_name = models.CharField(
|
contact_name = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
@ -229,7 +230,7 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
)
|
)
|
||||||
contact_email = models.EmailField(
|
contact_email = models.EmailField(
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Contact E-mail'
|
verbose_name=_('Contact E-mail')
|
||||||
)
|
)
|
||||||
comments = models.TextField(
|
comments = models.TextField(
|
||||||
blank=True
|
blank=True
|
||||||
@ -428,8 +429,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Facility ID',
|
verbose_name=_('Facility ID'),
|
||||||
help_text='Locally-assigned identifier'
|
help_text=_('Locally-assigned identifier')
|
||||||
)
|
)
|
||||||
site = models.ForeignKey(
|
site = models.ForeignKey(
|
||||||
to='dcim.Site',
|
to='dcim.Site',
|
||||||
@ -442,7 +443,7 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
related_name='racks',
|
related_name='racks',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text='Assigned group'
|
help_text=_('Assigned group')
|
||||||
)
|
)
|
||||||
tenant = models.ForeignKey(
|
tenant = models.ForeignKey(
|
||||||
to='tenancy.Tenant',
|
to='tenancy.Tenant',
|
||||||
@ -462,53 +463,53 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
related_name='racks',
|
related_name='racks',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text='Functional role'
|
help_text=_('Functional role')
|
||||||
)
|
)
|
||||||
serial = models.CharField(
|
serial = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Serial number'
|
verbose_name=_('Serial number')
|
||||||
)
|
)
|
||||||
asset_tag = models.CharField(
|
asset_tag = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
unique=True,
|
unique=True,
|
||||||
verbose_name='Asset tag',
|
verbose_name=_('Asset tag'),
|
||||||
help_text='A unique tag used to identify this rack'
|
help_text=_('A unique tag used to identify this rack')
|
||||||
)
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
choices=RackTypeChoices,
|
choices=RackTypeChoices,
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Type'
|
verbose_name=_('Type')
|
||||||
)
|
)
|
||||||
width = models.PositiveSmallIntegerField(
|
width = models.PositiveSmallIntegerField(
|
||||||
choices=RackWidthChoices,
|
choices=RackWidthChoices,
|
||||||
default=RackWidthChoices.WIDTH_19IN,
|
default=RackWidthChoices.WIDTH_19IN,
|
||||||
verbose_name='Width',
|
verbose_name=_('Width'),
|
||||||
help_text='Rail-to-rail width'
|
help_text=_('Rail-to-rail width')
|
||||||
)
|
)
|
||||||
u_height = models.PositiveSmallIntegerField(
|
u_height = models.PositiveSmallIntegerField(
|
||||||
default=RACK_U_HEIGHT_DEFAULT,
|
default=RACK_U_HEIGHT_DEFAULT,
|
||||||
verbose_name='Height (U)',
|
verbose_name=_('Height (U)'),
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||||
help_text='Height in rack units'
|
help_text=_('Height in rack units')
|
||||||
)
|
)
|
||||||
desc_units = models.BooleanField(
|
desc_units = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
verbose_name='Descending units',
|
verbose_name=_('Descending units'),
|
||||||
help_text='Units are numbered top-to-bottom'
|
help_text=_('Units are numbered top-to-bottom')
|
||||||
)
|
)
|
||||||
outer_width = models.PositiveSmallIntegerField(
|
outer_width = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text='Outer dimension of rack (width)'
|
help_text=_('Outer dimension of rack (width)')
|
||||||
)
|
)
|
||||||
outer_depth = models.PositiveSmallIntegerField(
|
outer_depth = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text='Outer dimension of rack (depth)'
|
help_text=_('Outer dimension of rack (depth)')
|
||||||
)
|
)
|
||||||
outer_unit = models.CharField(
|
outer_unit = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
@ -949,24 +950,24 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
part_number = models.CharField(
|
part_number = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Discrete part number (optional)'
|
help_text=_('Discrete part number (optional)')
|
||||||
)
|
)
|
||||||
u_height = models.PositiveSmallIntegerField(
|
u_height = models.PositiveSmallIntegerField(
|
||||||
default=1,
|
default=1,
|
||||||
verbose_name='Height (U)'
|
verbose_name=_('Height (U)')
|
||||||
)
|
)
|
||||||
is_full_depth = models.BooleanField(
|
is_full_depth = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
verbose_name='Is full depth',
|
verbose_name=_('Is full depth'),
|
||||||
help_text='Device consumes both front and rear rack faces'
|
help_text=_('Device consumes both front and rear rack faces')
|
||||||
)
|
)
|
||||||
subdevice_role = models.CharField(
|
subdevice_role = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=SubdeviceRoleChoices,
|
choices=SubdeviceRoleChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Parent/child status',
|
verbose_name=_('Parent/child status'),
|
||||||
help_text='Parent devices house child devices in device bays. Leave blank '
|
help_text=_('Parent devices house child devices in device bays. Leave blank '
|
||||||
'if this device type is neither a parent nor a child.'
|
'if this device type is neither a parent nor a child.')
|
||||||
)
|
)
|
||||||
front_image = models.ImageField(
|
front_image = models.ImageField(
|
||||||
upload_to='devicetype-images',
|
upload_to='devicetype-images',
|
||||||
@ -1200,8 +1201,8 @@ class DeviceRole(ChangeLoggedModel):
|
|||||||
)
|
)
|
||||||
vm_role = models.BooleanField(
|
vm_role = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
verbose_name='VM Role',
|
verbose_name=_('VM Role'),
|
||||||
help_text='Virtual machines may be assigned to this role'
|
help_text=_('Virtual machines may be assigned to this role')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
@ -1246,19 +1247,19 @@ class Platform(ChangeLoggedModel):
|
|||||||
related_name='platforms',
|
related_name='platforms',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=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(
|
napalm_driver = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='NAPALM driver',
|
verbose_name=_('NAPALM driver'),
|
||||||
help_text='The name of the NAPALM driver to use when interacting with devices'
|
help_text=_('The name of the NAPALM driver to use when interacting with devices')
|
||||||
)
|
)
|
||||||
napalm_args = JSONField(
|
napalm_args = JSONField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='NAPALM arguments',
|
verbose_name=_('NAPALM arguments'),
|
||||||
help_text='Additional arguments to pass when initiating the NAPALM driver (JSON format)'
|
help_text=_('Additional arguments to pass when initiating the NAPALM driver (JSON format)')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
@ -1338,15 +1339,15 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
serial = models.CharField(
|
serial = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Serial number'
|
verbose_name=_('Serial number')
|
||||||
)
|
)
|
||||||
asset_tag = models.CharField(
|
asset_tag = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
unique=True,
|
unique=True,
|
||||||
verbose_name='Asset tag',
|
verbose_name=_('Asset tag'),
|
||||||
help_text='A unique tag used to identify this device'
|
help_text=_('A unique tag used to identify this device')
|
||||||
)
|
)
|
||||||
site = models.ForeignKey(
|
site = models.ForeignKey(
|
||||||
to='dcim.Site',
|
to='dcim.Site',
|
||||||
@ -1364,14 +1365,14 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
verbose_name='Position (U)',
|
verbose_name=_('Position (U)'),
|
||||||
help_text='The lowest-numbered unit occupied by the device'
|
help_text=_('The lowest-numbered unit occupied by the device')
|
||||||
)
|
)
|
||||||
face = models.CharField(
|
face = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
choices=DeviceFaceChoices,
|
choices=DeviceFaceChoices,
|
||||||
verbose_name='Rack face'
|
verbose_name=_('Rack face')
|
||||||
)
|
)
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
@ -1384,7 +1385,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
related_name='primary_ip4_for',
|
related_name='primary_ip4_for',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Primary IPv4'
|
verbose_name=_('Primary IPv4')
|
||||||
)
|
)
|
||||||
primary_ip6 = models.OneToOneField(
|
primary_ip6 = models.OneToOneField(
|
||||||
to='ipam.IPAddress',
|
to='ipam.IPAddress',
|
||||||
@ -1392,7 +1393,7 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
related_name='primary_ip6_for',
|
related_name='primary_ip6_for',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Primary IPv6'
|
verbose_name=_('Primary IPv6')
|
||||||
)
|
)
|
||||||
cluster = models.ForeignKey(
|
cluster = models.ForeignKey(
|
||||||
to='virtualization.Cluster',
|
to='virtualization.Cluster',
|
||||||
@ -1904,7 +1905,7 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
|
|||||||
max_utilization = models.PositiveSmallIntegerField(
|
max_utilization = models.PositiveSmallIntegerField(
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
validators=[MinValueValidator(1), MaxValueValidator(100)],
|
||||||
default=POWERFEED_MAX_UTILIZATION_DEFAULT,
|
default=POWERFEED_MAX_UTILIZATION_DEFAULT,
|
||||||
help_text="Maximum permissible draw (percentage)"
|
help_text=_('Maximum permissible draw (percentage)')
|
||||||
)
|
)
|
||||||
available_power = models.PositiveIntegerField(
|
available_power = models.PositiveIntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
@ -154,13 +155,13 @@ class PowerPortTemplate(ComponentTemplateModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
help_text="Maximum power draw (watts)"
|
help_text=_('Maximum power draw (watts)')
|
||||||
)
|
)
|
||||||
allocated_draw = models.PositiveSmallIntegerField(
|
allocated_draw = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
help_text="Allocated power draw (watts)"
|
help_text=_('Allocated power draw (watts)')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -213,7 +214,7 @@ class PowerOutletTemplate(ComponentTemplateModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletFeedLegChoices,
|
choices=PowerOutletFeedLegChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Phase (for three-phase feeds)"
|
help_text=_('Phase (for three-phase feeds)')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -269,7 +270,7 @@ class InterfaceTemplate(ComponentTemplateModel):
|
|||||||
)
|
)
|
||||||
mgmt_only = models.BooleanField(
|
mgmt_only = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
verbose_name='Management only'
|
verbose_name=_('Management only')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -6,6 +6,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
@ -252,7 +253,7 @@ class ConsolePort(CableTermination, ComponentModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Physical port type'
|
help_text=_('Physical port type')
|
||||||
)
|
)
|
||||||
connected_endpoint = models.OneToOneField(
|
connected_endpoint = models.OneToOneField(
|
||||||
to='dcim.ConsoleServerPort',
|
to='dcim.ConsoleServerPort',
|
||||||
@ -311,7 +312,7 @@ class ConsoleServerPort(CableTermination, ComponentModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Physical port type'
|
help_text=_('Physical port type')
|
||||||
)
|
)
|
||||||
connection_status = models.NullBooleanField(
|
connection_status = models.NullBooleanField(
|
||||||
choices=CONNECTION_STATUS_CHOICES,
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
@ -363,19 +364,19 @@ class PowerPort(CableTermination, ComponentModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Physical port type'
|
help_text=_('Physical port type')
|
||||||
)
|
)
|
||||||
maximum_draw = models.PositiveSmallIntegerField(
|
maximum_draw = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
help_text="Maximum power draw (watts)"
|
help_text=_('Maximum power draw (watts)')
|
||||||
)
|
)
|
||||||
allocated_draw = models.PositiveSmallIntegerField(
|
allocated_draw = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1)],
|
validators=[MinValueValidator(1)],
|
||||||
help_text="Allocated power draw (watts)"
|
help_text=_('Allocated power draw (watts)')
|
||||||
)
|
)
|
||||||
_connected_poweroutlet = models.OneToOneField(
|
_connected_poweroutlet = models.OneToOneField(
|
||||||
to='dcim.PowerOutlet',
|
to='dcim.PowerOutlet',
|
||||||
@ -523,7 +524,7 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Physical port type'
|
help_text=_('Physical port type')
|
||||||
)
|
)
|
||||||
power_port = models.ForeignKey(
|
power_port = models.ForeignKey(
|
||||||
to='dcim.PowerPort',
|
to='dcim.PowerPort',
|
||||||
@ -536,7 +537,7 @@ class PowerOutlet(CableTermination, ComponentModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PowerOutletFeedLegChoices,
|
choices=PowerOutletFeedLegChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Phase (for three-phase feeds)"
|
help_text=_('Phase (for three-phase feeds)')
|
||||||
)
|
)
|
||||||
connection_status = models.NullBooleanField(
|
connection_status = models.NullBooleanField(
|
||||||
choices=CONNECTION_STATUS_CHOICES,
|
choices=CONNECTION_STATUS_CHOICES,
|
||||||
@ -629,7 +630,7 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
related_name='member_interfaces',
|
related_name='member_interfaces',
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Parent LAG'
|
verbose_name=_('Parent LAG')
|
||||||
)
|
)
|
||||||
type = models.CharField(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
@ -641,18 +642,18 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
mac_address = MACAddressField(
|
mac_address = MACAddressField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='MAC Address'
|
verbose_name=_('MAC Address')
|
||||||
)
|
)
|
||||||
mtu = models.PositiveIntegerField(
|
mtu = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(65536)],
|
validators=[MinValueValidator(1), MaxValueValidator(65536)],
|
||||||
verbose_name='MTU'
|
verbose_name=_('MTU')
|
||||||
)
|
)
|
||||||
mgmt_only = models.BooleanField(
|
mgmt_only = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
verbose_name='OOB Management',
|
verbose_name=_('OOB Management'),
|
||||||
help_text='This interface is used only for out-of-band management'
|
help_text=_('This interface is used only for out-of-band management')
|
||||||
)
|
)
|
||||||
mode = models.CharField(
|
mode = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
@ -665,13 +666,13 @@ class Interface(CableTermination, ComponentModel):
|
|||||||
related_name='interfaces_as_untagged',
|
related_name='interfaces_as_untagged',
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Untagged VLAN'
|
verbose_name=_('Untagged VLAN')
|
||||||
)
|
)
|
||||||
tagged_vlans = models.ManyToManyField(
|
tagged_vlans = models.ManyToManyField(
|
||||||
to='ipam.VLAN',
|
to='ipam.VLAN',
|
||||||
related_name='interfaces_as_tagged',
|
related_name='interfaces_as_tagged',
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Tagged VLANs'
|
verbose_name=_('Tagged VLANs')
|
||||||
)
|
)
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
|
||||||
@ -976,7 +977,7 @@ class DeviceBay(ComponentModel):
|
|||||||
)
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Name'
|
verbose_name=_('Name')
|
||||||
)
|
)
|
||||||
_name = NaturalOrderingField(
|
_name = NaturalOrderingField(
|
||||||
target_field='name',
|
target_field='name',
|
||||||
@ -1056,7 +1057,7 @@ class InventoryItem(ComponentModel):
|
|||||||
)
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Name'
|
verbose_name=_('Name')
|
||||||
)
|
)
|
||||||
_name = NaturalOrderingField(
|
_name = NaturalOrderingField(
|
||||||
target_field='name',
|
target_field='name',
|
||||||
@ -1072,13 +1073,13 @@ class InventoryItem(ComponentModel):
|
|||||||
)
|
)
|
||||||
part_id = models.CharField(
|
part_id = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Part ID',
|
verbose_name=_('Part ID'),
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Manufacturer-assigned part identifier'
|
help_text=_('Manufacturer-assigned part identifier')
|
||||||
)
|
)
|
||||||
serial = models.CharField(
|
serial = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
verbose_name='Serial number',
|
verbose_name=_('Serial number'),
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
asset_tag = models.CharField(
|
asset_tag = models.CharField(
|
||||||
@ -1086,12 +1087,12 @@ class InventoryItem(ComponentModel):
|
|||||||
unique=True,
|
unique=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Asset tag',
|
verbose_name=_('Asset tag'),
|
||||||
help_text='A unique tag used to identify this item'
|
help_text=_('A unique tag used to identify this item')
|
||||||
)
|
)
|
||||||
discovered = models.BooleanField(
|
discovered = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
help_text='This item was automatically discovered'
|
help_text=_('This item was automatically discovered')
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = TaggableManager(through=TaggedItem)
|
tags = TaggableManager(through=TaggedItem)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from tenancy.tables import COL_TENANT
|
from tenancy.tables import COL_TENANT
|
||||||
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ColoredLabelColumn, TagColumn, ToggleColumn
|
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ColoredLabelColumn, TagColumn, ToggleColumn
|
||||||
@ -196,7 +197,7 @@ class RegionTable(BaseTable):
|
|||||||
orderable=False
|
orderable=False
|
||||||
)
|
)
|
||||||
site_count = tables.Column(
|
site_count = tables.Column(
|
||||||
verbose_name='Sites'
|
verbose_name=_('Sites')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=REGION_ACTIONS,
|
template_code=REGION_ACTIONS,
|
||||||
@ -255,10 +256,10 @@ class RackGroupTable(BaseTable):
|
|||||||
site = tables.LinkColumn(
|
site = tables.LinkColumn(
|
||||||
viewname='dcim:site',
|
viewname='dcim:site',
|
||||||
args=[Accessor('site.slug')],
|
args=[Accessor('site.slug')],
|
||||||
verbose_name='Site'
|
verbose_name=_('Site')
|
||||||
)
|
)
|
||||||
rack_count = tables.Column(
|
rack_count = tables.Column(
|
||||||
verbose_name='Racks'
|
verbose_name=_('Racks')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=RACKGROUP_ACTIONS,
|
template_code=RACKGROUP_ACTIONS,
|
||||||
@ -315,7 +316,7 @@ class RackTable(BaseTable):
|
|||||||
role = ColoredLabelColumn()
|
role = ColoredLabelColumn()
|
||||||
u_height = tables.TemplateColumn(
|
u_height = tables.TemplateColumn(
|
||||||
template_code="{{ record.u_height }}U",
|
template_code="{{ record.u_height }}U",
|
||||||
verbose_name='Height'
|
verbose_name=_('Height')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -330,17 +331,17 @@ class RackTable(BaseTable):
|
|||||||
class RackDetailTable(RackTable):
|
class RackDetailTable(RackTable):
|
||||||
device_count = tables.TemplateColumn(
|
device_count = tables.TemplateColumn(
|
||||||
template_code=RACK_DEVICE_COUNT,
|
template_code=RACK_DEVICE_COUNT,
|
||||||
verbose_name='Devices'
|
verbose_name=_('Devices')
|
||||||
)
|
)
|
||||||
get_utilization = tables.TemplateColumn(
|
get_utilization = tables.TemplateColumn(
|
||||||
template_code=UTILIZATION_GRAPH,
|
template_code=UTILIZATION_GRAPH,
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Space'
|
verbose_name=_('Space')
|
||||||
)
|
)
|
||||||
get_power_utilization = tables.TemplateColumn(
|
get_power_utilization = tables.TemplateColumn(
|
||||||
template_code=UTILIZATION_GRAPH,
|
template_code=UTILIZATION_GRAPH,
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Power'
|
verbose_name=_('Power')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='dcim:rack_list'
|
url_name='dcim:rack_list'
|
||||||
@ -382,7 +383,7 @@ class RackReservationTable(BaseTable):
|
|||||||
)
|
)
|
||||||
unit_list = tables.Column(
|
unit_list = tables.Column(
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Units'
|
verbose_name=_('Units')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=RACKRESERVATION_ACTIONS,
|
template_code=RACKRESERVATION_ACTIONS,
|
||||||
@ -408,13 +409,13 @@ class ManufacturerTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
devicetype_count = tables.Column(
|
devicetype_count = tables.Column(
|
||||||
verbose_name='Device Types'
|
verbose_name=_('Device Types')
|
||||||
)
|
)
|
||||||
inventoryitem_count = tables.Column(
|
inventoryitem_count = tables.Column(
|
||||||
verbose_name='Inventory Items'
|
verbose_name=_('Inventory Items')
|
||||||
)
|
)
|
||||||
platform_count = tables.Column(
|
platform_count = tables.Column(
|
||||||
verbose_name='Platforms'
|
verbose_name=_('Platforms')
|
||||||
)
|
)
|
||||||
slug = tables.Column()
|
slug = tables.Column()
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
@ -439,14 +440,14 @@ class DeviceTypeTable(BaseTable):
|
|||||||
model = tables.LinkColumn(
|
model = tables.LinkColumn(
|
||||||
viewname='dcim:devicetype',
|
viewname='dcim:devicetype',
|
||||||
args=[Accessor('pk')],
|
args=[Accessor('pk')],
|
||||||
verbose_name='Device Type'
|
verbose_name=_('Device Type')
|
||||||
)
|
)
|
||||||
is_full_depth = BooleanColumn(
|
is_full_depth = BooleanColumn(
|
||||||
verbose_name='Full Depth'
|
verbose_name=_('Full Depth')
|
||||||
)
|
)
|
||||||
instance_count = tables.TemplateColumn(
|
instance_count = tables.TemplateColumn(
|
||||||
template_code=DEVICETYPE_INSTANCES_TEMPLATE,
|
template_code=DEVICETYPE_INSTANCES_TEMPLATE,
|
||||||
verbose_name='Instances'
|
verbose_name=_('Instances')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='dcim:devicetype_list'
|
url_name='dcim:devicetype_list'
|
||||||
@ -608,7 +609,7 @@ class InterfaceImportTable(BaseTable):
|
|||||||
virtual_machine = tables.LinkColumn(
|
virtual_machine = tables.LinkColumn(
|
||||||
viewname='virtualization:virtualmachine',
|
viewname='virtualization:virtualmachine',
|
||||||
args=[Accessor('virtual_machine.pk')],
|
args=[Accessor('virtual_machine.pk')],
|
||||||
verbose_name='Virtual Machine'
|
verbose_name=_('Virtual Machine')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -626,7 +627,7 @@ class FrontPortTemplateTable(BaseTable):
|
|||||||
order_by=('_name',)
|
order_by=('_name',)
|
||||||
)
|
)
|
||||||
rear_port_position = tables.Column(
|
rear_port_position = tables.Column(
|
||||||
verbose_name='Position'
|
verbose_name=_('Position')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=get_component_template_actions('frontporttemplate'),
|
template_code=get_component_template_actions('frontporttemplate'),
|
||||||
@ -706,15 +707,15 @@ class DeviceRoleTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
device_count = tables.TemplateColumn(
|
device_count = tables.TemplateColumn(
|
||||||
template_code=DEVICEROLE_DEVICE_COUNT,
|
template_code=DEVICEROLE_DEVICE_COUNT,
|
||||||
verbose_name='Devices'
|
verbose_name=_('Devices')
|
||||||
)
|
)
|
||||||
vm_count = tables.TemplateColumn(
|
vm_count = tables.TemplateColumn(
|
||||||
template_code=DEVICEROLE_VM_COUNT,
|
template_code=DEVICEROLE_VM_COUNT,
|
||||||
verbose_name='VMs'
|
verbose_name=_('VMs')
|
||||||
)
|
)
|
||||||
color = tables.TemplateColumn(
|
color = tables.TemplateColumn(
|
||||||
template_code=COLOR_LABEL,
|
template_code=COLOR_LABEL,
|
||||||
verbose_name='Label'
|
verbose_name=_('Label')
|
||||||
)
|
)
|
||||||
vm_role = BooleanColumn()
|
vm_role = BooleanColumn()
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
@ -737,11 +738,11 @@ class PlatformTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
device_count = tables.TemplateColumn(
|
device_count = tables.TemplateColumn(
|
||||||
template_code=PLATFORM_DEVICE_COUNT,
|
template_code=PLATFORM_DEVICE_COUNT,
|
||||||
verbose_name='Devices'
|
verbose_name=_('Devices')
|
||||||
)
|
)
|
||||||
vm_count = tables.TemplateColumn(
|
vm_count = tables.TemplateColumn(
|
||||||
template_code=PLATFORM_VM_COUNT,
|
template_code=PLATFORM_VM_COUNT,
|
||||||
verbose_name='VMs'
|
verbose_name=_('VMs')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=PLATFORM_ACTIONS,
|
template_code=PLATFORM_ACTIONS,
|
||||||
@ -785,28 +786,28 @@ class DeviceTable(BaseTable):
|
|||||||
args=[Accessor('rack.pk')]
|
args=[Accessor('rack.pk')]
|
||||||
)
|
)
|
||||||
device_role = ColoredLabelColumn(
|
device_role = ColoredLabelColumn(
|
||||||
verbose_name='Role'
|
verbose_name=_('Role')
|
||||||
)
|
)
|
||||||
device_type = tables.LinkColumn(
|
device_type = tables.LinkColumn(
|
||||||
viewname='dcim:devicetype',
|
viewname='dcim:devicetype',
|
||||||
args=[Accessor('device_type.pk')],
|
args=[Accessor('device_type.pk')],
|
||||||
verbose_name='Type',
|
verbose_name=_('Type'),
|
||||||
text=lambda record: record.device_type.display_name
|
text=lambda record: record.device_type.display_name
|
||||||
)
|
)
|
||||||
primary_ip = tables.TemplateColumn(
|
primary_ip = tables.TemplateColumn(
|
||||||
template_code=DEVICE_PRIMARY_IP,
|
template_code=DEVICE_PRIMARY_IP,
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='IP Address'
|
verbose_name=_('IP Address')
|
||||||
)
|
)
|
||||||
primary_ip4 = tables.LinkColumn(
|
primary_ip4 = tables.LinkColumn(
|
||||||
viewname='ipam:ipaddress',
|
viewname='ipam:ipaddress',
|
||||||
args=[Accessor('primary_ip4.pk')],
|
args=[Accessor('primary_ip4.pk')],
|
||||||
verbose_name='IPv4 Address'
|
verbose_name=_('IPv4 Address')
|
||||||
)
|
)
|
||||||
primary_ip6 = tables.LinkColumn(
|
primary_ip6 = tables.LinkColumn(
|
||||||
viewname='ipam:ipaddress',
|
viewname='ipam:ipaddress',
|
||||||
args=[Accessor('primary_ip6.pk')],
|
args=[Accessor('primary_ip6.pk')],
|
||||||
verbose_name='IPv6 Address'
|
verbose_name=_('IPv6 Address')
|
||||||
)
|
)
|
||||||
cluster = tables.LinkColumn(
|
cluster = tables.LinkColumn(
|
||||||
viewname='virtualization:cluster',
|
viewname='virtualization:cluster',
|
||||||
@ -817,10 +818,10 @@ class DeviceTable(BaseTable):
|
|||||||
args=[Accessor('virtual_chassis.pk')]
|
args=[Accessor('virtual_chassis.pk')]
|
||||||
)
|
)
|
||||||
vc_position = tables.Column(
|
vc_position = tables.Column(
|
||||||
verbose_name='VC Position'
|
verbose_name=_('VC Position')
|
||||||
)
|
)
|
||||||
vc_priority = tables.Column(
|
vc_priority = tables.Column(
|
||||||
verbose_name='VC Priority'
|
verbose_name=_('VC Priority')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='dcim:device_list'
|
url_name='dcim:device_list'
|
||||||
@ -857,10 +858,10 @@ class DeviceImportTable(BaseTable):
|
|||||||
args=[Accessor('rack.pk')]
|
args=[Accessor('rack.pk')]
|
||||||
)
|
)
|
||||||
device_role = tables.Column(
|
device_role = tables.Column(
|
||||||
verbose_name='Role'
|
verbose_name=_('Role')
|
||||||
)
|
)
|
||||||
device_type = tables.Column(
|
device_type = tables.Column(
|
||||||
verbose_name='Type'
|
verbose_name=_('Type')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -1031,29 +1032,29 @@ class CableTable(BaseTable):
|
|||||||
id = tables.LinkColumn(
|
id = tables.LinkColumn(
|
||||||
viewname='dcim:cable',
|
viewname='dcim:cable',
|
||||||
args=[Accessor('pk')],
|
args=[Accessor('pk')],
|
||||||
verbose_name='ID'
|
verbose_name=_('ID')
|
||||||
)
|
)
|
||||||
termination_a_parent = tables.TemplateColumn(
|
termination_a_parent = tables.TemplateColumn(
|
||||||
template_code=CABLE_TERMINATION_PARENT,
|
template_code=CABLE_TERMINATION_PARENT,
|
||||||
accessor=Accessor('termination_a'),
|
accessor=Accessor('termination_a'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Side A'
|
verbose_name=_('Side A')
|
||||||
)
|
)
|
||||||
termination_a = tables.LinkColumn(
|
termination_a = tables.LinkColumn(
|
||||||
accessor=Accessor('termination_a'),
|
accessor=Accessor('termination_a'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Termination A'
|
verbose_name=_('Termination A')
|
||||||
)
|
)
|
||||||
termination_b_parent = tables.TemplateColumn(
|
termination_b_parent = tables.TemplateColumn(
|
||||||
template_code=CABLE_TERMINATION_PARENT,
|
template_code=CABLE_TERMINATION_PARENT,
|
||||||
accessor=Accessor('termination_b'),
|
accessor=Accessor('termination_b'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Side B'
|
verbose_name=_('Side B')
|
||||||
)
|
)
|
||||||
termination_b = tables.LinkColumn(
|
termination_b = tables.LinkColumn(
|
||||||
accessor=Accessor('termination_b'),
|
accessor=Accessor('termination_b'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Termination B'
|
verbose_name=_('Termination B')
|
||||||
)
|
)
|
||||||
status = tables.TemplateColumn(
|
status = tables.TemplateColumn(
|
||||||
template_code=STATUS_LABEL
|
template_code=STATUS_LABEL
|
||||||
@ -1085,17 +1086,17 @@ class ConsoleConnectionTable(BaseTable):
|
|||||||
viewname='dcim:device',
|
viewname='dcim:device',
|
||||||
accessor=Accessor('connected_endpoint.device'),
|
accessor=Accessor('connected_endpoint.device'),
|
||||||
args=[Accessor('connected_endpoint.device.pk')],
|
args=[Accessor('connected_endpoint.device.pk')],
|
||||||
verbose_name='Console Server'
|
verbose_name=_('Console Server')
|
||||||
)
|
)
|
||||||
connected_endpoint = tables.Column(
|
connected_endpoint = tables.Column(
|
||||||
verbose_name='Port'
|
verbose_name=_('Port')
|
||||||
)
|
)
|
||||||
device = tables.LinkColumn(
|
device = tables.LinkColumn(
|
||||||
viewname='dcim:device',
|
viewname='dcim:device',
|
||||||
args=[Accessor('device.pk')]
|
args=[Accessor('device.pk')]
|
||||||
)
|
)
|
||||||
name = tables.Column(
|
name = tables.Column(
|
||||||
verbose_name='Console Port'
|
verbose_name=_('Console Port')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -1109,18 +1110,18 @@ class PowerConnectionTable(BaseTable):
|
|||||||
accessor=Accessor('connected_endpoint.device'),
|
accessor=Accessor('connected_endpoint.device'),
|
||||||
args=[Accessor('connected_endpoint.device.pk')],
|
args=[Accessor('connected_endpoint.device.pk')],
|
||||||
order_by='_connected_poweroutlet__device',
|
order_by='_connected_poweroutlet__device',
|
||||||
verbose_name='PDU'
|
verbose_name=_('PDU')
|
||||||
)
|
)
|
||||||
outlet = tables.Column(
|
outlet = tables.Column(
|
||||||
accessor=Accessor('_connected_poweroutlet'),
|
accessor=Accessor('_connected_poweroutlet'),
|
||||||
verbose_name='Outlet'
|
verbose_name=_('Outlet')
|
||||||
)
|
)
|
||||||
device = tables.LinkColumn(
|
device = tables.LinkColumn(
|
||||||
viewname='dcim:device',
|
viewname='dcim:device',
|
||||||
args=[Accessor('device.pk')]
|
args=[Accessor('device.pk')]
|
||||||
)
|
)
|
||||||
name = tables.Column(
|
name = tables.Column(
|
||||||
verbose_name='Power Port'
|
verbose_name=_('Power Port')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -1133,25 +1134,25 @@ class InterfaceConnectionTable(BaseTable):
|
|||||||
viewname='dcim:device',
|
viewname='dcim:device',
|
||||||
accessor=Accessor('device'),
|
accessor=Accessor('device'),
|
||||||
args=[Accessor('device.pk')],
|
args=[Accessor('device.pk')],
|
||||||
verbose_name='Device A'
|
verbose_name=_('Device A')
|
||||||
)
|
)
|
||||||
interface_a = tables.LinkColumn(
|
interface_a = tables.LinkColumn(
|
||||||
viewname='dcim:interface',
|
viewname='dcim:interface',
|
||||||
accessor=Accessor('name'),
|
accessor=Accessor('name'),
|
||||||
args=[Accessor('pk')],
|
args=[Accessor('pk')],
|
||||||
verbose_name='Interface A'
|
verbose_name=_('Interface A')
|
||||||
)
|
)
|
||||||
device_b = tables.LinkColumn(
|
device_b = tables.LinkColumn(
|
||||||
viewname='dcim:device',
|
viewname='dcim:device',
|
||||||
accessor=Accessor('_connected_interface.device'),
|
accessor=Accessor('_connected_interface.device'),
|
||||||
args=[Accessor('_connected_interface.device.pk')],
|
args=[Accessor('_connected_interface.device.pk')],
|
||||||
verbose_name='Device B'
|
verbose_name=_('Device B')
|
||||||
)
|
)
|
||||||
interface_b = tables.LinkColumn(
|
interface_b = tables.LinkColumn(
|
||||||
viewname='dcim:interface',
|
viewname='dcim:interface',
|
||||||
accessor=Accessor('_connected_interface'),
|
accessor=Accessor('_connected_interface'),
|
||||||
args=[Accessor('_connected_interface.pk')],
|
args=[Accessor('_connected_interface.pk')],
|
||||||
verbose_name='Interface B'
|
verbose_name=_('Interface B')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -1195,7 +1196,7 @@ class VirtualChassisTable(BaseTable):
|
|||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
member_count = tables.Column(
|
member_count = tables.Column(
|
||||||
verbose_name='Members'
|
verbose_name=_('Members')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='dcim:virtualchassis_list'
|
url_name='dcim:virtualchassis_list'
|
||||||
@ -1220,7 +1221,7 @@ class PowerPanelTable(BaseTable):
|
|||||||
)
|
)
|
||||||
powerfeed_count = tables.TemplateColumn(
|
powerfeed_count = tables.TemplateColumn(
|
||||||
template_code=POWERPANEL_POWERFEED_COUNT,
|
template_code=POWERPANEL_POWERFEED_COUNT,
|
||||||
verbose_name='Feeds'
|
verbose_name=_('Feeds')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -1254,7 +1255,7 @@ class PowerFeedTable(BaseTable):
|
|||||||
template_code="{{ value }}%"
|
template_code="{{ value }}%"
|
||||||
)
|
)
|
||||||
available_power = tables.Column(
|
available_power = tables.Column(
|
||||||
verbose_name='Available power (VA)'
|
verbose_name=_('Available power (VA)')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='dcim:powerfeed_list'
|
url_name='dcim:powerfeed_list'
|
||||||
|
@ -14,6 +14,7 @@ from django.urls import reverse
|
|||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.http import is_safe_url
|
from django.utils.http import is_safe_url
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
from circuits.models import Circuit
|
from circuits.models import Circuit
|
||||||
@ -74,7 +75,7 @@ class BulkRenameView(GetReturnURLMixin, View):
|
|||||||
for obj in selected_objects:
|
for obj in selected_objects:
|
||||||
obj.name = obj.new_name
|
obj.name = obj.new_name
|
||||||
obj.save()
|
obj.save()
|
||||||
messages.success(request, "Renamed {} {}".format(
|
messages.success(request, _('Renamed {} {}').format(
|
||||||
len(selected_objects),
|
len(selected_objects),
|
||||||
model._meta.verbose_name_plural
|
model._meta.verbose_name_plural
|
||||||
))
|
))
|
||||||
@ -119,7 +120,7 @@ class BulkDisconnectView(GetReturnURLMixin, View):
|
|||||||
obj.cable.delete()
|
obj.cable.delete()
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
messages.success(request, "Disconnected {} {}".format(
|
messages.success(request, _('Disconnected {} {}').format(
|
||||||
count, self.model._meta.verbose_name_plural
|
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.installed_device = form.cleaned_data['installed_device']
|
||||||
device_bay.save()
|
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)
|
return redirect('dcim:device', pk=device_bay.device.pk)
|
||||||
|
|
||||||
@ -1888,7 +1889,7 @@ class DeviceBayDepopulateView(PermissionRequiredMixin, View):
|
|||||||
removed_device = device_bay.installed_device
|
removed_device = device_bay.installed_device
|
||||||
device_bay.installed_device = None
|
device_bay.installed_device = None
|
||||||
device_bay.save()
|
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)
|
return redirect('dcim:device', pk=device_bay.device.pk)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utilities.forms import LaxURLField
|
from utilities.forms import LaxURLField
|
||||||
from .models import CustomField, CustomFieldChoice, CustomLink, Graph, ExportTemplate, ReportResult, Webhook
|
from .models import CustomField, CustomFieldChoice, CustomLink, Graph, ExportTemplate, ReportResult, Webhook
|
||||||
@ -20,7 +21,7 @@ def order_content_types(field):
|
|||||||
|
|
||||||
class WebhookForm(forms.ModelForm):
|
class WebhookForm(forms.ModelForm):
|
||||||
payload_url = LaxURLField(
|
payload_url = LaxURLField(
|
||||||
label='URL'
|
label=_('URL')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -116,11 +117,11 @@ class CustomLinkForm(forms.ModelForm):
|
|||||||
'url': forms.Textarea,
|
'url': forms.Textarea,
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'weight': 'A numeric weight to influence the ordering of this link among its peers. Lower weights appear '
|
'weight': _('A numeric weight to influence the ordering of this link among its peers. Lower weights appear '
|
||||||
'first in a list.',
|
'first in a list.'),
|
||||||
'text': 'Jinja2 template code for the link text. Reference the object as <code>{{ obj }}</code>. Links '
|
'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.',
|
'which render as empty text will not be displayed.'),
|
||||||
'url': 'Jinja2 template code for the link URL. Reference the object as <code>{{ obj }}</code>.',
|
'url': _('Jinja2 template code for the link URL. Reference the object as <code>{{ obj }}</code>.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
@ -107,7 +108,7 @@ class ExportTemplateFilterSet(BaseFilterSet):
|
|||||||
class TagFilterSet(BaseFilterSet):
|
class TagFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -126,95 +127,95 @@ class TagFilterSet(BaseFilterSet):
|
|||||||
class ConfigContextFilterSet(BaseFilterSet):
|
class ConfigContextFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = django_filters.ModelMultipleChoiceFilter(
|
region_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='regions',
|
field_name='regions',
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
label='Region',
|
label=_('Region'),
|
||||||
)
|
)
|
||||||
region = django_filters.ModelMultipleChoiceFilter(
|
region = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='regions__slug',
|
field_name='regions__slug',
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='sites',
|
field_name='sites',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site',
|
label=_('Site'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='sites__slug',
|
field_name='sites__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='roles',
|
field_name='roles',
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
label='Role',
|
label=_('Role'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='roles__slug',
|
field_name='roles__slug',
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Role (slug)',
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
platform_id = django_filters.ModelMultipleChoiceFilter(
|
platform_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='platforms',
|
field_name='platforms',
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
label='Platform',
|
label=_('Platform'),
|
||||||
)
|
)
|
||||||
platform = django_filters.ModelMultipleChoiceFilter(
|
platform = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='platforms__slug',
|
field_name='platforms__slug',
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Platform (slug)',
|
label=_('Platform (slug)'),
|
||||||
)
|
)
|
||||||
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
|
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster_groups',
|
field_name='cluster_groups',
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
label='Cluster group',
|
label=_('Cluster group'),
|
||||||
)
|
)
|
||||||
cluster_group = django_filters.ModelMultipleChoiceFilter(
|
cluster_group = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster_groups__slug',
|
field_name='cluster_groups__slug',
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Cluster group (slug)',
|
label=_('Cluster group (slug)'),
|
||||||
)
|
)
|
||||||
cluster_id = django_filters.ModelMultipleChoiceFilter(
|
cluster_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='clusters',
|
field_name='clusters',
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
label='Cluster',
|
label=_('Cluster'),
|
||||||
)
|
)
|
||||||
tenant_group_id = django_filters.ModelMultipleChoiceFilter(
|
tenant_group_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='tenant_groups',
|
field_name='tenant_groups',
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
label='Tenant group',
|
label=_('Tenant group'),
|
||||||
)
|
)
|
||||||
tenant_group = django_filters.ModelMultipleChoiceFilter(
|
tenant_group = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='tenant_groups__slug',
|
field_name='tenant_groups__slug',
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Tenant group (slug)',
|
label=_('Tenant group (slug)'),
|
||||||
)
|
)
|
||||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='tenants',
|
field_name='tenants',
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
label='Tenant',
|
label=_('Tenant'),
|
||||||
)
|
)
|
||||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='tenants__slug',
|
field_name='tenants__slug',
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Tenant (slug)',
|
label=_('Tenant (slug)'),
|
||||||
)
|
)
|
||||||
tag = django_filters.ModelMultipleChoiceFilter(
|
tag = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='tags__slug',
|
field_name='tags__slug',
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Tag (slug)',
|
label=_('Tag (slug)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -238,7 +239,7 @@ class ConfigContextFilterSet(BaseFilterSet):
|
|||||||
class LocalConfigContextFilterSet(django_filters.FilterSet):
|
class LocalConfigContextFilterSet(django_filters.FilterSet):
|
||||||
local_context_data = django_filters.BooleanFilter(
|
local_context_data = django_filters.BooleanFilter(
|
||||||
method='_local_context_data',
|
method='_local_context_data',
|
||||||
label='Has local config context data',
|
label=_('Has local config context data'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _local_context_data(self, queryset, name, value):
|
def _local_context_data(self, queryset, name, value):
|
||||||
@ -248,7 +249,7 @@ class LocalConfigContextFilterSet(django_filters.FilterSet):
|
|||||||
class ObjectChangeFilterSet(BaseFilterSet):
|
class ObjectChangeFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
time = django_filters.DateTimeFromToRangeFilter()
|
time = django_filters.DateTimeFromToRangeFilter()
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from mptt.forms import TreeNodeMultipleChoiceField
|
from mptt.forms import TreeNodeMultipleChoiceField
|
||||||
from taggit.forms import TagField as TagField_
|
from taggit.forms import TagField as TagField_
|
||||||
|
|
||||||
@ -181,7 +182,7 @@ class TagFilterForm(BootstrapMixin, forms.Form):
|
|||||||
model = Tag
|
model = Tag
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -285,7 +286,7 @@ class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
region = DynamicModelMultipleChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -330,7 +331,7 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
|||||||
cluster_id = DynamicModelMultipleChoiceField(
|
cluster_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Cluster'
|
label=_('Cluster')
|
||||||
)
|
)
|
||||||
tenant_group = DynamicModelMultipleChoiceField(
|
tenant_group = DynamicModelMultipleChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
@ -365,7 +366,7 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
|||||||
class LocalConfigContextFilterForm(forms.Form):
|
class LocalConfigContextFilterForm(forms.Form):
|
||||||
local_context_data = forms.NullBooleanField(
|
local_context_data = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Has local config context data',
|
label=_('Has local config context data'),
|
||||||
widget=StaticSelect2(
|
widget=StaticSelect2(
|
||||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
)
|
)
|
||||||
@ -393,15 +394,15 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
|
|||||||
model = ObjectChange
|
model = ObjectChange
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
time_after = forms.DateTimeField(
|
time_after = forms.DateTimeField(
|
||||||
label='After',
|
label=_('After'),
|
||||||
required=False,
|
required=False,
|
||||||
widget=DateTimePicker()
|
widget=DateTimePicker()
|
||||||
)
|
)
|
||||||
time_before = forms.DateTimeField(
|
time_before = forms.DateTimeField(
|
||||||
label='Before',
|
label=_('Before'),
|
||||||
required=False,
|
required=False,
|
||||||
widget=DateTimePicker()
|
widget=DateTimePicker()
|
||||||
)
|
)
|
||||||
@ -420,7 +421,7 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
|
|||||||
queryset=ContentType.objects.order_by('model'),
|
queryset=ContentType.objects.order_by('model'),
|
||||||
required=False,
|
required=False,
|
||||||
widget=ContentTypeSelect(),
|
widget=ContentTypeSelect(),
|
||||||
label='Object Type'
|
label=_('Object Type')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -432,8 +433,8 @@ class ScriptForm(BootstrapMixin, forms.Form):
|
|||||||
_commit = forms.BooleanField(
|
_commit = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
initial=True,
|
initial=True,
|
||||||
label="Commit changes",
|
label=_('Commit changes'),
|
||||||
help_text="Commit changes to the database (uncheck for a dry-run)"
|
help_text=_('Commit changes to the database (uncheck for a dry-run)')
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
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.contrib.contenttypes.models import ContentType
|
||||||
from django.core.validators import ValidationError
|
from django.core.validators import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utilities.forms import CSVChoiceField, DatePicker, LaxURLField, StaticSelect2, add_blank_choice
|
from utilities.forms import CSVChoiceField, DatePicker, LaxURLField, StaticSelect2, add_blank_choice
|
||||||
from extras.choices import *
|
from extras.choices import *
|
||||||
@ -70,9 +71,9 @@ class CustomField(models.Model):
|
|||||||
obj_type = models.ManyToManyField(
|
obj_type = models.ManyToManyField(
|
||||||
to=ContentType,
|
to=ContentType,
|
||||||
related_name='custom_fields',
|
related_name='custom_fields',
|
||||||
verbose_name='Object(s)',
|
verbose_name=_('Object(s)'),
|
||||||
limit_choices_to=FeatureQuery('custom_fields'),
|
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(
|
type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
@ -86,8 +87,8 @@ class CustomField(models.Model):
|
|||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Name of the field as displayed to users (if not provided, '
|
help_text=_('Name of the field as displayed to users (if not provided, '
|
||||||
'the field\'s name will be used)'
|
'the field\'s name will be used)')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
@ -95,24 +96,24 @@ class CustomField(models.Model):
|
|||||||
)
|
)
|
||||||
required = models.BooleanField(
|
required = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
help_text='If true, this field is required when creating new objects '
|
help_text=_('If true, this field is required when creating new objects '
|
||||||
'or editing an existing object.'
|
'or editing an existing object.')
|
||||||
)
|
)
|
||||||
filter_logic = models.CharField(
|
filter_logic = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=CustomFieldFilterLogicChoices,
|
choices=CustomFieldFilterLogicChoices,
|
||||||
default=CustomFieldFilterLogicChoices.FILTER_LOOSE,
|
default=CustomFieldFilterLogicChoices.FILTER_LOOSE,
|
||||||
help_text='Loose matches any instance of a given string; exact '
|
help_text=_('Loose matches any instance of a given string; exact '
|
||||||
'matches the entire field.'
|
'matches the entire field.')
|
||||||
)
|
)
|
||||||
default = models.CharField(
|
default = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
blank=True,
|
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(
|
weight = models.PositiveSmallIntegerField(
|
||||||
default=100,
|
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()
|
objects = CustomFieldManager()
|
||||||
@ -284,7 +285,7 @@ class CustomFieldChoice(models.Model):
|
|||||||
)
|
)
|
||||||
weight = models.PositiveSmallIntegerField(
|
weight = models.PositiveSmallIntegerField(
|
||||||
default=100,
|
default=100,
|
||||||
help_text='Higher weights appear lower in the list'
|
help_text=_('Higher weights appear lower in the list')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -10,6 +10,7 @@ from django.db import models
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.template import Template, Context
|
from django.template import Template, Context
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from rest_framework.utils.encoders import JSONEncoder
|
from rest_framework.utils.encoders import JSONEncoder
|
||||||
|
|
||||||
from utilities.utils import deepmerge, render_jinja2
|
from utilities.utils import deepmerge, render_jinja2
|
||||||
@ -32,9 +33,9 @@ class Webhook(models.Model):
|
|||||||
obj_type = models.ManyToManyField(
|
obj_type = models.ManyToManyField(
|
||||||
to=ContentType,
|
to=ContentType,
|
||||||
related_name='webhooks',
|
related_name='webhooks',
|
||||||
verbose_name='Object types',
|
verbose_name=_('Object types'),
|
||||||
limit_choices_to=FeatureQuery('webhooks'),
|
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(
|
name = models.CharField(
|
||||||
max_length=150,
|
max_length=150,
|
||||||
@ -42,20 +43,20 @@ class Webhook(models.Model):
|
|||||||
)
|
)
|
||||||
type_create = models.BooleanField(
|
type_create = models.BooleanField(
|
||||||
default=False,
|
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(
|
type_update = models.BooleanField(
|
||||||
default=False,
|
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(
|
type_delete = models.BooleanField(
|
||||||
default=False,
|
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(
|
payload_url = models.CharField(
|
||||||
max_length=500,
|
max_length=500,
|
||||||
verbose_name='URL',
|
verbose_name=_('URL'),
|
||||||
help_text="A POST will be sent to this URL when the webhook is called."
|
help_text=_('A POST will be sent to this URL when the webhook is called.')
|
||||||
)
|
)
|
||||||
enabled = models.BooleanField(
|
enabled = models.BooleanField(
|
||||||
default=True
|
default=True
|
||||||
@ -64,47 +65,47 @@ class Webhook(models.Model):
|
|||||||
max_length=30,
|
max_length=30,
|
||||||
choices=WebhookHttpMethodChoices,
|
choices=WebhookHttpMethodChoices,
|
||||||
default=WebhookHttpMethodChoices.METHOD_POST,
|
default=WebhookHttpMethodChoices.METHOD_POST,
|
||||||
verbose_name='HTTP method'
|
verbose_name=_('HTTP method')
|
||||||
)
|
)
|
||||||
http_content_type = models.CharField(
|
http_content_type = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
default=HTTP_CONTENT_TYPE_JSON,
|
default=HTTP_CONTENT_TYPE_JSON,
|
||||||
verbose_name='HTTP content type',
|
verbose_name=_('HTTP content type'),
|
||||||
help_text='The complete list of official content types is available '
|
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>.'
|
'<a href="https://www.iana.org/assignments/media-types/media-types.xhtml">here</a>.')
|
||||||
)
|
)
|
||||||
additional_headers = models.TextField(
|
additional_headers = models.TextField(
|
||||||
blank=True,
|
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 "
|
"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(
|
body_template = models.TextField(
|
||||||
blank=True,
|
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>, '
|
'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(
|
secret = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
blank=True,
|
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 "
|
"header containing a HMAC hex digest of the payload body using "
|
||||||
"the secret as the key. The secret is not transmitted in "
|
"the secret as the key. The secret is not transmitted in "
|
||||||
"the request."
|
"the request.")
|
||||||
)
|
)
|
||||||
ssl_verification = models.BooleanField(
|
ssl_verification = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
verbose_name='SSL verification',
|
verbose_name=_('SSL verification'),
|
||||||
help_text="Enable SSL certificate verification. Disable with caution!"
|
help_text=_('Enable SSL certificate verification. Disable with caution!')
|
||||||
)
|
)
|
||||||
ca_file_path = models.CharField(
|
ca_file_path = models.CharField(
|
||||||
max_length=4096,
|
max_length=4096,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='CA File Path',
|
verbose_name=_('CA File Path'),
|
||||||
help_text='The specific CA certificate file to use for SSL verification. '
|
help_text=_('The specific CA certificate file to use for SSL verification. '
|
||||||
'Leave blank to use the system defaults.'
|
'Leave blank to use the system defaults.')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -168,12 +169,12 @@ class CustomLink(models.Model):
|
|||||||
)
|
)
|
||||||
text = models.CharField(
|
text = models.CharField(
|
||||||
max_length=500,
|
max_length=500,
|
||||||
help_text="Jinja2 template code for link text"
|
help_text=_('Jinja2 template code for link text')
|
||||||
)
|
)
|
||||||
url = models.CharField(
|
url = models.CharField(
|
||||||
max_length=500,
|
max_length=500,
|
||||||
verbose_name='URL',
|
verbose_name=_('URL'),
|
||||||
help_text="Jinja2 template code for link URL"
|
help_text=_('Jinja2 template code for link URL')
|
||||||
)
|
)
|
||||||
weight = models.PositiveSmallIntegerField(
|
weight = models.PositiveSmallIntegerField(
|
||||||
default=100
|
default=100
|
||||||
@ -181,16 +182,16 @@ class CustomLink(models.Model):
|
|||||||
group_name = models.CharField(
|
group_name = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
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(
|
button_class = models.CharField(
|
||||||
max_length=30,
|
max_length=30,
|
||||||
choices=CustomLinkButtonClassChoices,
|
choices=CustomLinkButtonClassChoices,
|
||||||
default=CustomLinkButtonClassChoices.CLASS_DEFAULT,
|
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(
|
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:
|
class Meta:
|
||||||
@ -215,7 +216,7 @@ class Graph(models.Model):
|
|||||||
)
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
verbose_name='Name'
|
verbose_name=_('Name')
|
||||||
)
|
)
|
||||||
template_language = models.CharField(
|
template_language = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
@ -224,11 +225,11 @@ class Graph(models.Model):
|
|||||||
)
|
)
|
||||||
source = models.CharField(
|
source = models.CharField(
|
||||||
max_length=500,
|
max_length=500,
|
||||||
verbose_name='Source URL'
|
verbose_name=_('Source URL')
|
||||||
)
|
)
|
||||||
link = models.URLField(
|
link = models.URLField(
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='Link URL'
|
verbose_name=_('Link URL')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -284,18 +285,18 @@ class ExportTemplate(models.Model):
|
|||||||
default=TemplateLanguageChoices.LANGUAGE_JINJA2
|
default=TemplateLanguageChoices.LANGUAGE_JINJA2
|
||||||
)
|
)
|
||||||
template_code = models.TextField(
|
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(
|
mime_type = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='MIME type',
|
verbose_name=_('MIME type'),
|
||||||
help_text='Defaults to <code>text/plain</code>'
|
help_text=_('Defaults to <code>text/plain</code>')
|
||||||
)
|
)
|
||||||
file_extension = models.CharField(
|
file_extension = models.CharField(
|
||||||
max_length=15,
|
max_length=15,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='Extension to append to the rendered filename'
|
help_text=_('Extension to append to the rendered filename')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
|
from utilities.tables import BaseTable, BooleanColumn, ColorColumn, ToggleColumn
|
||||||
from .models import ConfigContext, ObjectChange, Tag, TaggedItem
|
from .models import ConfigContext, ObjectChange, Tag, TaggedItem
|
||||||
@ -84,10 +85,10 @@ class TaggedItemTable(BaseTable):
|
|||||||
content_object = tables.TemplateColumn(
|
content_object = tables.TemplateColumn(
|
||||||
template_code=TAGGED_ITEM,
|
template_code=TAGGED_ITEM,
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Object'
|
verbose_name=_('Object')
|
||||||
)
|
)
|
||||||
content_type = tables.Column(
|
content_type = tables.Column(
|
||||||
verbose_name='Type'
|
verbose_name=_('Type')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -99,7 +100,7 @@ class ConfigContextTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
is_active = BooleanColumn(
|
is_active = BooleanColumn(
|
||||||
verbose_name='Active'
|
verbose_name=_('Active')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -119,15 +120,15 @@ class ObjectChangeTable(BaseTable):
|
|||||||
template_code=OBJECTCHANGE_ACTION
|
template_code=OBJECTCHANGE_ACTION
|
||||||
)
|
)
|
||||||
changed_object_type = tables.Column(
|
changed_object_type = tables.Column(
|
||||||
verbose_name='Type'
|
verbose_name=_('Type')
|
||||||
)
|
)
|
||||||
object_repr = tables.TemplateColumn(
|
object_repr = tables.TemplateColumn(
|
||||||
template_code=OBJECTCHANGE_OBJECT,
|
template_code=OBJECTCHANGE_OBJECT,
|
||||||
verbose_name='Object'
|
verbose_name=_('Object')
|
||||||
)
|
)
|
||||||
request_id = tables.TemplateColumn(
|
request_id = tables.TemplateColumn(
|
||||||
template_code=OBJECTCHANGE_REQUEST_ID,
|
template_code=OBJECTCHANGE_REQUEST_ID,
|
||||||
verbose_name='Request ID'
|
verbose_name=_('Request ID')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class IPAMConfig(AppConfig):
|
class IPAMConfig(AppConfig):
|
||||||
name = "ipam"
|
name = "ipam"
|
||||||
verbose_name = "IPAM"
|
verbose_name = _('IPAM')
|
||||||
|
@ -2,6 +2,7 @@ import django_filters
|
|||||||
import netaddr
|
import netaddr
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from netaddr.core import AddrFormatError
|
from netaddr.core import AddrFormatError
|
||||||
|
|
||||||
from dcim.models import Device, Interface, Region, Site
|
from dcim.models import Device, Interface, Region, Site
|
||||||
@ -32,7 +33,7 @@ __all__ = (
|
|||||||
class VRFFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class VRFFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ class RIRFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
family = django_filters.NumberFilter(
|
family = django_filters.NumberFilter(
|
||||||
field_name='prefix',
|
field_name='prefix',
|
||||||
@ -68,17 +69,17 @@ class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
|
|||||||
)
|
)
|
||||||
prefix = django_filters.CharFilter(
|
prefix = django_filters.CharFilter(
|
||||||
method='filter_prefix',
|
method='filter_prefix',
|
||||||
label='Prefix',
|
label=_('Prefix'),
|
||||||
)
|
)
|
||||||
rir_id = django_filters.ModelMultipleChoiceFilter(
|
rir_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
label='RIR (ID)',
|
label=_('RIR (ID)'),
|
||||||
)
|
)
|
||||||
rir = django_filters.ModelMultipleChoiceFilter(
|
rir = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='rir__slug',
|
field_name='rir__slug',
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='RIR (slug)',
|
label=_('RIR (slug)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ class AggregateFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt
|
|||||||
class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -121,7 +122,7 @@ class RoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
family = django_filters.NumberFilter(
|
family = django_filters.NumberFilter(
|
||||||
field_name='prefix',
|
field_name='prefix',
|
||||||
@ -129,74 +130,74 @@ class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Cre
|
|||||||
)
|
)
|
||||||
prefix = django_filters.CharFilter(
|
prefix = django_filters.CharFilter(
|
||||||
method='filter_prefix',
|
method='filter_prefix',
|
||||||
label='Prefix',
|
label=_('Prefix'),
|
||||||
)
|
)
|
||||||
within = django_filters.CharFilter(
|
within = django_filters.CharFilter(
|
||||||
method='search_within',
|
method='search_within',
|
||||||
label='Within prefix',
|
label=_('Within prefix'),
|
||||||
)
|
)
|
||||||
within_include = django_filters.CharFilter(
|
within_include = django_filters.CharFilter(
|
||||||
method='search_within_include',
|
method='search_within_include',
|
||||||
label='Within and including prefix',
|
label=_('Within and including prefix'),
|
||||||
)
|
)
|
||||||
contains = django_filters.CharFilter(
|
contains = django_filters.CharFilter(
|
||||||
method='search_contains',
|
method='search_contains',
|
||||||
label='Prefixes which contain this prefix or IP',
|
label=_('Prefixes which contain this prefix or IP'),
|
||||||
)
|
)
|
||||||
mask_length = django_filters.NumberFilter(
|
mask_length = django_filters.NumberFilter(
|
||||||
method='filter_mask_length',
|
method='filter_mask_length',
|
||||||
label='Mask length',
|
label=_('Mask length'),
|
||||||
)
|
)
|
||||||
vrf_id = django_filters.ModelMultipleChoiceFilter(
|
vrf_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
label='VRF',
|
label=_('VRF'),
|
||||||
)
|
)
|
||||||
vrf = django_filters.ModelMultipleChoiceFilter(
|
vrf = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='vrf__rd',
|
field_name='vrf__rd',
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
to_field_name='rd',
|
to_field_name='rd',
|
||||||
label='VRF (RD)',
|
label=_('VRF (RD)'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
vlan_id = django_filters.ModelMultipleChoiceFilter(
|
vlan_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
label='VLAN (ID)',
|
label=_('VLAN (ID)'),
|
||||||
)
|
)
|
||||||
vlan_vid = django_filters.NumberFilter(
|
vlan_vid = django_filters.NumberFilter(
|
||||||
field_name='vlan__vid',
|
field_name='vlan__vid',
|
||||||
label='VLAN number (1-4095)',
|
label=_('VLAN number (1-4095)'),
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
label='Role (ID)',
|
label=_('Role (ID)'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='role__slug',
|
field_name='role__slug',
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Role (slug)',
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=PrefixStatusChoices,
|
choices=PrefixStatusChoices,
|
||||||
@ -271,7 +272,7 @@ class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Cre
|
|||||||
class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
family = django_filters.NumberFilter(
|
family = django_filters.NumberFilter(
|
||||||
field_name='address',
|
field_name='address',
|
||||||
@ -279,60 +280,60 @@ class IPAddressFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet,
|
|||||||
)
|
)
|
||||||
parent = django_filters.CharFilter(
|
parent = django_filters.CharFilter(
|
||||||
method='search_by_parent',
|
method='search_by_parent',
|
||||||
label='Parent prefix',
|
label=_('Parent prefix'),
|
||||||
)
|
)
|
||||||
address = MultiValueCharFilter(
|
address = MultiValueCharFilter(
|
||||||
method='filter_address',
|
method='filter_address',
|
||||||
label='Address',
|
label=_('Address'),
|
||||||
)
|
)
|
||||||
mask_length = django_filters.NumberFilter(
|
mask_length = django_filters.NumberFilter(
|
||||||
method='filter_mask_length',
|
method='filter_mask_length',
|
||||||
label='Mask length',
|
label=_('Mask length'),
|
||||||
)
|
)
|
||||||
vrf_id = django_filters.ModelMultipleChoiceFilter(
|
vrf_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
label='VRF',
|
label=_('VRF'),
|
||||||
)
|
)
|
||||||
vrf = django_filters.ModelMultipleChoiceFilter(
|
vrf = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='vrf__rd',
|
field_name='vrf__rd',
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
to_field_name='rd',
|
to_field_name='rd',
|
||||||
label='VRF (RD)',
|
label=_('VRF (RD)'),
|
||||||
)
|
)
|
||||||
device = MultiValueCharFilter(
|
device = MultiValueCharFilter(
|
||||||
method='filter_device',
|
method='filter_device',
|
||||||
field_name='name',
|
field_name='name',
|
||||||
label='Device (name)',
|
label=_('Device (name)'),
|
||||||
)
|
)
|
||||||
device_id = MultiValueNumberFilter(
|
device_id = MultiValueNumberFilter(
|
||||||
method='filter_device',
|
method='filter_device',
|
||||||
field_name='pk',
|
field_name='pk',
|
||||||
label='Device (ID)',
|
label=_('Device (ID)'),
|
||||||
)
|
)
|
||||||
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
|
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='interface__virtual_machine',
|
field_name='interface__virtual_machine',
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
label='Virtual machine (ID)',
|
label=_('Virtual machine (ID)'),
|
||||||
)
|
)
|
||||||
virtual_machine = django_filters.ModelMultipleChoiceFilter(
|
virtual_machine = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='interface__virtual_machine__name',
|
field_name='interface__virtual_machine__name',
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
label='Virtual machine (name)',
|
label=_('Virtual machine (name)'),
|
||||||
)
|
)
|
||||||
interface = django_filters.ModelMultipleChoiceFilter(
|
interface = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='interface__name',
|
field_name='interface__name',
|
||||||
queryset=Interface.objects.all(),
|
queryset=Interface.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
label='Interface (ID)',
|
label=_('Interface (ID)'),
|
||||||
)
|
)
|
||||||
interface_id = django_filters.ModelMultipleChoiceFilter(
|
interface_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Interface.objects.all(),
|
queryset=Interface.objects.all(),
|
||||||
label='Interface (ID)',
|
label=_('Interface (ID)'),
|
||||||
)
|
)
|
||||||
assigned_to_interface = django_filters.BooleanFilter(
|
assigned_to_interface = django_filters.BooleanFilter(
|
||||||
method='_assigned_to_interface',
|
method='_assigned_to_interface',
|
||||||
label='Is assigned to an interface',
|
label=_('Is assigned to an interface'),
|
||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=IPAddressStatusChoices,
|
choices=IPAddressStatusChoices,
|
||||||
@ -397,24 +398,24 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -425,50 +426,50 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
group_id = django_filters.ModelMultipleChoiceFilter(
|
group_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
label='Group (ID)',
|
label=_('Group (ID)'),
|
||||||
)
|
)
|
||||||
group = django_filters.ModelMultipleChoiceFilter(
|
group = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='group__slug',
|
field_name='group__slug',
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Group',
|
label=_('Group'),
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
label='Role (ID)',
|
label=_('Role (ID)'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='role__slug',
|
field_name='role__slug',
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Role (slug)',
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=VLANStatusChoices,
|
choices=VLANStatusChoices,
|
||||||
@ -494,27 +495,27 @@ class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, Creat
|
|||||||
class ServiceFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
|
class ServiceFilterSet(BaseFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
device_id = django_filters.ModelMultipleChoiceFilter(
|
device_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
label='Device (ID)',
|
label=_('Device (ID)'),
|
||||||
)
|
)
|
||||||
device = django_filters.ModelMultipleChoiceFilter(
|
device = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device__name',
|
field_name='device__name',
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
label='Device (name)',
|
label=_('Device (name)'),
|
||||||
)
|
)
|
||||||
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
|
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
label='Virtual machine (ID)',
|
label=_('Virtual machine (ID)'),
|
||||||
)
|
)
|
||||||
virtual_machine = django_filters.ModelMultipleChoiceFilter(
|
virtual_machine = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='virtual_machine__name',
|
field_name='virtual_machine__name',
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
label='Virtual machine (name)',
|
label=_('Virtual machine (name)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Device, Interface, Rack, Region, Site
|
from dcim.models import Device, Interface, Rack, Region, Site
|
||||||
from extras.forms import (
|
from extras.forms import (
|
||||||
@ -55,7 +56,7 @@ class VRFCSVForm(CustomFieldModelCSVForm):
|
|||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned tenant'
|
help_text=_('Assigned tenant')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -75,7 +76,7 @@ class VRFBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm
|
|||||||
enforce_unique = forms.NullBooleanField(
|
enforce_unique = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
widget=BulkEditNullBooleanSelect(),
|
widget=BulkEditNullBooleanSelect(),
|
||||||
label='Enforce unique space'
|
label=_('Enforce unique space')
|
||||||
)
|
)
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
@ -93,7 +94,7 @@ class VRFFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
|||||||
field_order = ['q', 'tenant_group', 'tenant']
|
field_order = ['q', 'tenant_group', 'tenant']
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
@ -119,14 +120,14 @@ class RIRCSVForm(CSVModelForm):
|
|||||||
model = RIR
|
model = RIR
|
||||||
fields = RIR.csv_headers
|
fields = RIR.csv_headers
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'name': 'RIR name',
|
'name': _('RIR name'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RIRFilterForm(BootstrapMixin, forms.Form):
|
class RIRFilterForm(BootstrapMixin, forms.Form):
|
||||||
is_private = forms.NullBooleanField(
|
is_private = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Private',
|
label=_('Private'),
|
||||||
widget=StaticSelect2(
|
widget=StaticSelect2(
|
||||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
)
|
)
|
||||||
@ -163,7 +164,7 @@ class AggregateCSVForm(CustomFieldModelCSVForm):
|
|||||||
rir = CSVModelChoiceField(
|
rir = CSVModelChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned RIR'
|
help_text=_('Assigned RIR')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -179,7 +180,7 @@ class AggregateBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
|||||||
rir = DynamicModelChoiceField(
|
rir = DynamicModelChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='RIR'
|
label=_('RIR')
|
||||||
)
|
)
|
||||||
date_added = forms.DateField(
|
date_added = forms.DateField(
|
||||||
required=False
|
required=False
|
||||||
@ -202,19 +203,19 @@ class AggregateFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
model = Aggregate
|
model = Aggregate
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
family = forms.ChoiceField(
|
family = forms.ChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
choices=add_blank_choice(IPAddressFamilyChoices),
|
choices=add_blank_choice(IPAddressFamilyChoices),
|
||||||
label='Address family',
|
label=_('Address family'),
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
rir = DynamicModelMultipleChoiceField(
|
rir = DynamicModelMultipleChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
required=False,
|
required=False,
|
||||||
label='RIR',
|
label=_('RIR'),
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
value_field="slug",
|
value_field="slug",
|
||||||
)
|
)
|
||||||
@ -252,7 +253,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
vrf = DynamicModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF'
|
label=_('VRF')
|
||||||
)
|
)
|
||||||
site = DynamicModelChoiceField(
|
site = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
@ -270,7 +271,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
vlan_group = DynamicModelChoiceField(
|
vlan_group = DynamicModelChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VLAN group',
|
label=_('VLAN group'),
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
filter_for={
|
filter_for={
|
||||||
'vlan': 'group_id'
|
'vlan': 'group_id'
|
||||||
@ -283,7 +284,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
vlan = DynamicModelChoiceField(
|
vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VLAN',
|
label=_('VLAN'),
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
display_field='display_name'
|
display_field='display_name'
|
||||||
)
|
)
|
||||||
@ -323,41 +324,41 @@ class PrefixCSVForm(CustomFieldModelCSVForm):
|
|||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Assigned VRF'
|
help_text=_('Assigned VRF')
|
||||||
)
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned tenant'
|
help_text=_('Assigned tenant')
|
||||||
)
|
)
|
||||||
site = CSVModelChoiceField(
|
site = CSVModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned site'
|
help_text=_('Assigned site')
|
||||||
)
|
)
|
||||||
vlan_group = CSVModelChoiceField(
|
vlan_group = CSVModelChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text="VLAN's group (if any)"
|
help_text=_("VLAN's group (if any)")
|
||||||
)
|
)
|
||||||
vlan = CSVModelChoiceField(
|
vlan = CSVModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='vid',
|
to_field_name='vid',
|
||||||
help_text="Assigned VLAN"
|
help_text=_('Assigned VLAN')
|
||||||
)
|
)
|
||||||
status = CSVChoiceField(
|
status = CSVChoiceField(
|
||||||
choices=PrefixStatusChoices,
|
choices=PrefixStatusChoices,
|
||||||
help_text='Operational status'
|
help_text=_('Operational status')
|
||||||
)
|
)
|
||||||
role = CSVModelChoiceField(
|
role = CSVModelChoiceField(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Functional role'
|
help_text=_('Functional role')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -389,7 +390,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
|||||||
vrf = DynamicModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF'
|
label=_('VRF')
|
||||||
)
|
)
|
||||||
prefix_length = forms.IntegerField(
|
prefix_length = forms.IntegerField(
|
||||||
min_value=PREFIX_LENGTH_MIN,
|
min_value=PREFIX_LENGTH_MIN,
|
||||||
@ -412,7 +413,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
|||||||
is_pool = forms.NullBooleanField(
|
is_pool = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
widget=BulkEditNullBooleanSelect(),
|
widget=BulkEditNullBooleanSelect(),
|
||||||
label='Is a pool'
|
label=_('Is a pool')
|
||||||
)
|
)
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
@ -433,7 +434,7 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
|
|||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
within_include = forms.CharField(
|
within_include = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -442,24 +443,24 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
|
|||||||
'placeholder': 'Prefix',
|
'placeholder': 'Prefix',
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
label='Search within'
|
label=_('Search within')
|
||||||
)
|
)
|
||||||
family = forms.ChoiceField(
|
family = forms.ChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
choices=add_blank_choice(IPAddressFamilyChoices),
|
choices=add_blank_choice(IPAddressFamilyChoices),
|
||||||
label='Address family',
|
label=_('Address family'),
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
mask_length = forms.ChoiceField(
|
mask_length = forms.ChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
choices=PREFIX_MASK_LENGTH_CHOICES,
|
choices=PREFIX_MASK_LENGTH_CHOICES,
|
||||||
label='Mask length',
|
label=_('Mask length'),
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
vrf_id = DynamicModelMultipleChoiceField(
|
vrf_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF',
|
label=_('VRF'),
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
null_option=True,
|
null_option=True,
|
||||||
)
|
)
|
||||||
@ -500,14 +501,14 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
|
|||||||
)
|
)
|
||||||
is_pool = forms.NullBooleanField(
|
is_pool = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Is a pool',
|
label=_('Is a pool'),
|
||||||
widget=StaticSelect2(
|
widget=StaticSelect2(
|
||||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
expand = forms.BooleanField(
|
expand = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Expand prefix hierarchy'
|
label=_('Expand prefix hierarchy')
|
||||||
)
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
@ -524,12 +525,12 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
vrf = DynamicModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF'
|
label=_('VRF')
|
||||||
)
|
)
|
||||||
nat_site = DynamicModelChoiceField(
|
nat_site = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Site',
|
label=_('Site'),
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
filter_for={
|
filter_for={
|
||||||
'nat_rack': 'site_id',
|
'nat_rack': 'site_id',
|
||||||
@ -540,7 +541,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
nat_rack = DynamicModelChoiceField(
|
nat_rack = DynamicModelChoiceField(
|
||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Rack',
|
label=_('Rack'),
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
filter_for={
|
filter_for={
|
||||||
@ -554,7 +555,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
nat_device = DynamicModelChoiceField(
|
nat_device = DynamicModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Device',
|
label=_('Device'),
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
display_field='display_name',
|
display_field='display_name',
|
||||||
filter_for={
|
filter_for={
|
||||||
@ -565,7 +566,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
nat_vrf = DynamicModelChoiceField(
|
nat_vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF',
|
label=_('VRF'),
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
filter_for={
|
filter_for={
|
||||||
'nat_inside': 'vrf_id'
|
'nat_inside': 'vrf_id'
|
||||||
@ -575,14 +576,14 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
nat_inside = DynamicModelChoiceField(
|
nat_inside = DynamicModelChoiceField(
|
||||||
queryset=IPAddress.objects.all(),
|
queryset=IPAddress.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='IP Address',
|
label=_('IP Address'),
|
||||||
widget=APISelect(
|
widget=APISelect(
|
||||||
display_field='address'
|
display_field='address'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
primary_for_parent = forms.BooleanField(
|
primary_for_parent = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Make this the primary IP for the device/VM'
|
label=_('Make this the primary IP for the device/VM')
|
||||||
)
|
)
|
||||||
tags = TagField(
|
tags = TagField(
|
||||||
required=False
|
required=False
|
||||||
@ -671,7 +672,7 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel
|
|||||||
|
|
||||||
class IPAddressBulkCreateForm(BootstrapMixin, forms.Form):
|
class IPAddressBulkCreateForm(BootstrapMixin, forms.Form):
|
||||||
pattern = ExpandableIPAddressField(
|
pattern = ExpandableIPAddressField(
|
||||||
label='Address pattern'
|
label=_('Address pattern')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -679,7 +680,7 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
vrf = DynamicModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF'
|
label=_('VRF')
|
||||||
)
|
)
|
||||||
tags = TagField(
|
tags = TagField(
|
||||||
required=False
|
required=False
|
||||||
@ -705,43 +706,43 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
|
|||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Assigned VRF'
|
help_text=_('Assigned VRF')
|
||||||
)
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Assigned tenant'
|
help_text=_('Assigned tenant')
|
||||||
)
|
)
|
||||||
status = CSVChoiceField(
|
status = CSVChoiceField(
|
||||||
choices=IPAddressStatusChoices,
|
choices=IPAddressStatusChoices,
|
||||||
help_text='Operational status'
|
help_text=_('Operational status')
|
||||||
)
|
)
|
||||||
role = CSVChoiceField(
|
role = CSVChoiceField(
|
||||||
choices=IPAddressRoleChoices,
|
choices=IPAddressRoleChoices,
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Functional role'
|
help_text=_('Functional role')
|
||||||
)
|
)
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
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(
|
virtual_machine = CSVModelChoiceField(
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Parent VM of assigned interface (if any)'
|
help_text=_('Parent VM of assigned interface (if any)')
|
||||||
)
|
)
|
||||||
interface = CSVModelChoiceField(
|
interface = CSVModelChoiceField(
|
||||||
queryset=Interface.objects.all(),
|
queryset=Interface.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned interface'
|
help_text=_('Assigned interface')
|
||||||
)
|
)
|
||||||
is_primary = forms.BooleanField(
|
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
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -779,7 +780,7 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
|
|||||||
|
|
||||||
# Validate is_primary
|
# Validate is_primary
|
||||||
if is_primary and not device and not virtual_machine:
|
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):
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
@ -805,7 +806,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd
|
|||||||
vrf = DynamicModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF'
|
label=_('VRF')
|
||||||
)
|
)
|
||||||
mask_length = forms.IntegerField(
|
mask_length = forms.IntegerField(
|
||||||
min_value=IPADDRESS_MASK_LENGTH_MIN,
|
min_value=IPADDRESS_MASK_LENGTH_MIN,
|
||||||
@ -845,12 +846,12 @@ class IPAddressAssignForm(BootstrapMixin, forms.Form):
|
|||||||
vrf_id = DynamicModelChoiceField(
|
vrf_id = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF',
|
label=_('VRF'),
|
||||||
empty_label='Global'
|
empty_label='Global'
|
||||||
)
|
)
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -862,7 +863,7 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
|
|||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
parent = forms.CharField(
|
parent = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -871,24 +872,24 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
|
|||||||
'placeholder': 'Prefix',
|
'placeholder': 'Prefix',
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
label='Parent Prefix'
|
label=_('Parent Prefix')
|
||||||
)
|
)
|
||||||
family = forms.ChoiceField(
|
family = forms.ChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
choices=add_blank_choice(IPAddressFamilyChoices),
|
choices=add_blank_choice(IPAddressFamilyChoices),
|
||||||
label='Address family',
|
label=_('Address family'),
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
mask_length = forms.ChoiceField(
|
mask_length = forms.ChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
choices=IPADDRESS_MASK_LENGTH_CHOICES,
|
choices=IPADDRESS_MASK_LENGTH_CHOICES,
|
||||||
label='Mask length',
|
label=_('Mask length'),
|
||||||
widget=StaticSelect2()
|
widget=StaticSelect2()
|
||||||
)
|
)
|
||||||
vrf_id = DynamicModelMultipleChoiceField(
|
vrf_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VRF',
|
label=_('VRF'),
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
null_option=True,
|
null_option=True,
|
||||||
)
|
)
|
||||||
@ -905,7 +906,7 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo
|
|||||||
)
|
)
|
||||||
assigned_to_interface = forms.NullBooleanField(
|
assigned_to_interface = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Assigned to an interface',
|
label=_('Assigned to an interface'),
|
||||||
widget=StaticSelect2(
|
widget=StaticSelect2(
|
||||||
choices=BOOLEAN_WITH_BLANK_CHOICES
|
choices=BOOLEAN_WITH_BLANK_CHOICES
|
||||||
)
|
)
|
||||||
@ -936,7 +937,7 @@ class VLANGroupCSVForm(CSVModelForm):
|
|||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned site'
|
help_text=_('Assigned site')
|
||||||
)
|
)
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
@ -1018,37 +1019,37 @@ class VLANCSVForm(CustomFieldModelCSVForm):
|
|||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned site'
|
help_text=_('Assigned site')
|
||||||
)
|
)
|
||||||
group = CSVModelChoiceField(
|
group = CSVModelChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned VLAN group'
|
help_text=_('Assigned VLAN group')
|
||||||
)
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Assigned tenant'
|
help_text=_('Assigned tenant')
|
||||||
)
|
)
|
||||||
status = CSVChoiceField(
|
status = CSVChoiceField(
|
||||||
choices=VLANStatusChoices,
|
choices=VLANStatusChoices,
|
||||||
help_text='Operational status'
|
help_text=_('Operational status')
|
||||||
)
|
)
|
||||||
role = CSVModelChoiceField(
|
role = CSVModelChoiceField(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Functional role'
|
help_text=_('Functional role')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VLAN
|
model = VLAN
|
||||||
fields = VLAN.csv_headers
|
fields = VLAN.csv_headers
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'vid': 'Numeric VLAN ID (1-4095)',
|
'vid': _('Numeric VLAN ID (1-4095)'),
|
||||||
'name': 'VLAN name',
|
'name': _('VLAN name'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, data=None, *args, **kwargs):
|
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']
|
field_order = ['q', 'region', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant']
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
region = DynamicModelMultipleChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -1134,7 +1135,7 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
|||||||
group_id = DynamicModelMultipleChoiceField(
|
group_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='VLAN group',
|
label=_('VLAN group'),
|
||||||
widget=APISelectMultiple(
|
widget=APISelectMultiple(
|
||||||
null_option=True,
|
null_option=True,
|
||||||
)
|
)
|
||||||
@ -1204,7 +1205,7 @@ class ServiceFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
model = Service
|
model = Service
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
protocol = forms.ChoiceField(
|
protocol = forms.ChoiceField(
|
||||||
choices=add_blank_choice(ServiceProtocolChoices),
|
choices=add_blank_choice(ServiceProtocolChoices),
|
||||||
@ -1222,17 +1223,17 @@ class ServiceCSVForm(CustomFieldModelCSVForm):
|
|||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
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(
|
virtual_machine = CSVModelChoiceField(
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Required if not assigned to a device'
|
help_text=_('Required if not assigned to a device')
|
||||||
)
|
)
|
||||||
protocol = CSVChoiceField(
|
protocol = CSVChoiceField(
|
||||||
choices=ServiceProtocolChoices,
|
choices=ServiceProtocolChoices,
|
||||||
help_text='IP protocol'
|
help_text=_('IP protocol')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -6,6 +6,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F, Q
|
from django.db.models import F, Q
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from dcim.models import Device, Interface
|
from dcim.models import Device, Interface
|
||||||
@ -50,8 +51,8 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
|
|||||||
unique=True,
|
unique=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Route distinguisher',
|
verbose_name=_('Route distinguisher'),
|
||||||
help_text='Unique route distinguisher (as defined in RFC 4364)'
|
help_text=_('Unique route distinguisher (as defined in RFC 4364)')
|
||||||
)
|
)
|
||||||
tenant = models.ForeignKey(
|
tenant = models.ForeignKey(
|
||||||
to='tenancy.Tenant',
|
to='tenancy.Tenant',
|
||||||
@ -62,8 +63,8 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
|
|||||||
)
|
)
|
||||||
enforce_unique = models.BooleanField(
|
enforce_unique = models.BooleanField(
|
||||||
default=True,
|
default=True,
|
||||||
verbose_name='Enforce unique space',
|
verbose_name=_('Enforce unique space'),
|
||||||
help_text='Prevent duplicate prefixes/IP addresses within this VRF'
|
help_text=_('Prevent duplicate prefixes/IP addresses within this VRF')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
@ -84,7 +85,7 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('name', 'rd', 'pk') # (name, rd) may be non-unique
|
ordering = ('name', 'rd', 'pk') # (name, rd) may be non-unique
|
||||||
verbose_name = 'VRF'
|
verbose_name = _('VRF')
|
||||||
verbose_name_plural = 'VRFs'
|
verbose_name_plural = 'VRFs'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -123,8 +124,8 @@ class RIR(ChangeLoggedModel):
|
|||||||
)
|
)
|
||||||
is_private = models.BooleanField(
|
is_private = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
verbose_name='Private',
|
verbose_name=_('Private'),
|
||||||
help_text='IP space managed by this RIR is considered private'
|
help_text=_('IP space managed by this RIR is considered private')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
@ -135,7 +136,7 @@ class RIR(ChangeLoggedModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
verbose_name = 'RIR'
|
verbose_name = _('RIR')
|
||||||
verbose_name_plural = 'RIRs'
|
verbose_name_plural = 'RIRs'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -164,7 +165,7 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
|
|||||||
to='ipam.RIR',
|
to='ipam.RIR',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='aggregates',
|
related_name='aggregates',
|
||||||
verbose_name='RIR'
|
verbose_name=_('RIR')
|
||||||
)
|
)
|
||||||
date_added = models.DateField(
|
date_added = models.DateField(
|
||||||
blank=True,
|
blank=True,
|
||||||
@ -299,7 +300,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
|
|||||||
assigned to a VLAN where appropriate.
|
assigned to a VLAN where appropriate.
|
||||||
"""
|
"""
|
||||||
prefix = IPNetworkField(
|
prefix = IPNetworkField(
|
||||||
help_text='IPv4 or IPv6 network with mask'
|
help_text=_('IPv4 or IPv6 network with mask')
|
||||||
)
|
)
|
||||||
site = models.ForeignKey(
|
site = models.ForeignKey(
|
||||||
to='dcim.Site',
|
to='dcim.Site',
|
||||||
@ -314,7 +315,7 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
|
|||||||
related_name='prefixes',
|
related_name='prefixes',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='VRF'
|
verbose_name=_('VRF')
|
||||||
)
|
)
|
||||||
tenant = models.ForeignKey(
|
tenant = models.ForeignKey(
|
||||||
to='tenancy.Tenant',
|
to='tenancy.Tenant',
|
||||||
@ -329,14 +330,14 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
|
|||||||
related_name='prefixes',
|
related_name='prefixes',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='VLAN'
|
verbose_name=_('VLAN')
|
||||||
)
|
)
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=PrefixStatusChoices,
|
choices=PrefixStatusChoices,
|
||||||
default=PrefixStatusChoices.STATUS_ACTIVE,
|
default=PrefixStatusChoices.STATUS_ACTIVE,
|
||||||
verbose_name='Status',
|
verbose_name=_('Status'),
|
||||||
help_text='Operational status of this prefix'
|
help_text=_('Operational status of this prefix')
|
||||||
)
|
)
|
||||||
role = models.ForeignKey(
|
role = models.ForeignKey(
|
||||||
to='ipam.Role',
|
to='ipam.Role',
|
||||||
@ -344,12 +345,12 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
|
|||||||
related_name='prefixes',
|
related_name='prefixes',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
help_text='The primary function of this prefix'
|
help_text=_('The primary function of this prefix')
|
||||||
)
|
)
|
||||||
is_pool = models.BooleanField(
|
is_pool = models.BooleanField(
|
||||||
verbose_name='Is a pool',
|
verbose_name=_('Is a pool'),
|
||||||
default=False,
|
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(
|
description = models.CharField(
|
||||||
max_length=200,
|
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.
|
which has a NAT outside IP, that Interface's Device can use either the inside or outside IP as its primary IP.
|
||||||
"""
|
"""
|
||||||
address = IPAddressField(
|
address = IPAddressField(
|
||||||
help_text='IPv4 or IPv6 address (with mask)'
|
help_text=_('IPv4 or IPv6 address (with mask)')
|
||||||
)
|
)
|
||||||
vrf = models.ForeignKey(
|
vrf = models.ForeignKey(
|
||||||
to='ipam.VRF',
|
to='ipam.VRF',
|
||||||
@ -578,7 +579,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
|||||||
related_name='ip_addresses',
|
related_name='ip_addresses',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='VRF'
|
verbose_name=_('VRF')
|
||||||
)
|
)
|
||||||
tenant = models.ForeignKey(
|
tenant = models.ForeignKey(
|
||||||
to='tenancy.Tenant',
|
to='tenancy.Tenant',
|
||||||
@ -591,13 +592,13 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
choices=IPAddressStatusChoices,
|
choices=IPAddressStatusChoices,
|
||||||
default=IPAddressStatusChoices.STATUS_ACTIVE,
|
default=IPAddressStatusChoices.STATUS_ACTIVE,
|
||||||
help_text='The operational status of this IP'
|
help_text=_('The operational status of this IP')
|
||||||
)
|
)
|
||||||
role = models.CharField(
|
role = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
choices=IPAddressRoleChoices,
|
choices=IPAddressRoleChoices,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text='The functional role of this IP'
|
help_text=_('The functional role of this IP')
|
||||||
)
|
)
|
||||||
interface = models.ForeignKey(
|
interface = models.ForeignKey(
|
||||||
to='dcim.Interface',
|
to='dcim.Interface',
|
||||||
@ -612,15 +613,15 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
|||||||
related_name='nat_outside',
|
related_name='nat_outside',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='NAT (Inside)',
|
verbose_name=_('NAT (Inside)'),
|
||||||
help_text='The IP for which this address is the "outside" IP'
|
help_text=_('The IP for which this address is the "outside" IP')
|
||||||
)
|
)
|
||||||
dns_name = models.CharField(
|
dns_name = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
blank=True,
|
blank=True,
|
||||||
validators=[DNSValidator],
|
validators=[DNSValidator],
|
||||||
verbose_name='DNS Name',
|
verbose_name=_('DNS Name'),
|
||||||
help_text='Hostname or FQDN (not case-sensitive)'
|
help_text=_('Hostname or FQDN (not case-sensitive)')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
@ -663,7 +664,7 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('address', 'pk') # address may be non-unique
|
ordering = ('address', 'pk') # address may be non-unique
|
||||||
verbose_name = 'IP address'
|
verbose_name = _('IP address')
|
||||||
verbose_name_plural = 'IP addresses'
|
verbose_name_plural = 'IP addresses'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -836,7 +837,7 @@ class VLANGroup(ChangeLoggedModel):
|
|||||||
['site', 'name'],
|
['site', 'name'],
|
||||||
['site', 'slug'],
|
['site', 'slug'],
|
||||||
]
|
]
|
||||||
verbose_name = 'VLAN group'
|
verbose_name = _('VLAN group')
|
||||||
verbose_name_plural = 'VLAN groups'
|
verbose_name_plural = 'VLAN groups'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -889,7 +890,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
|
|||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
vid = models.PositiveSmallIntegerField(
|
vid = models.PositiveSmallIntegerField(
|
||||||
verbose_name='ID',
|
verbose_name=_('ID'),
|
||||||
validators=[MinValueValidator(1), MaxValueValidator(4094)]
|
validators=[MinValueValidator(1), MaxValueValidator(4094)]
|
||||||
)
|
)
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
@ -943,7 +944,7 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
|
|||||||
['group', 'vid'],
|
['group', 'vid'],
|
||||||
['group', 'name'],
|
['group', 'name'],
|
||||||
]
|
]
|
||||||
verbose_name = 'VLAN'
|
verbose_name = _('VLAN')
|
||||||
verbose_name_plural = 'VLANs'
|
verbose_name_plural = 'VLANs'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -999,7 +1000,7 @@ class Service(ChangeLoggedModel, CustomFieldModel):
|
|||||||
to='dcim.Device',
|
to='dcim.Device',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='services',
|
related_name='services',
|
||||||
verbose_name='device',
|
verbose_name=_('device'),
|
||||||
null=True,
|
null=True,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
@ -1022,13 +1023,13 @@ class Service(ChangeLoggedModel, CustomFieldModel):
|
|||||||
MinValueValidator(SERVICE_PORT_MIN),
|
MinValueValidator(SERVICE_PORT_MIN),
|
||||||
MaxValueValidator(SERVICE_PORT_MAX)
|
MaxValueValidator(SERVICE_PORT_MAX)
|
||||||
],
|
],
|
||||||
verbose_name='Port number'
|
verbose_name=_('Port number')
|
||||||
)
|
)
|
||||||
ipaddresses = models.ManyToManyField(
|
ipaddresses = models.ManyToManyField(
|
||||||
to='ipam.IPAddress',
|
to='ipam.IPAddress',
|
||||||
related_name='services',
|
related_name='services',
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name='IP addresses'
|
verbose_name=_('IP addresses')
|
||||||
)
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from tenancy.tables import COL_TENANT
|
from tenancy.tables import COL_TENANT
|
||||||
@ -191,13 +192,13 @@ class VRFTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
rd = tables.Column(
|
rd = tables.Column(
|
||||||
verbose_name='RD'
|
verbose_name=_('RD')
|
||||||
)
|
)
|
||||||
tenant = tables.TemplateColumn(
|
tenant = tables.TemplateColumn(
|
||||||
template_code=COL_TENANT
|
template_code=COL_TENANT
|
||||||
)
|
)
|
||||||
enforce_unique = BooleanColumn(
|
enforce_unique = BooleanColumn(
|
||||||
verbose_name='Unique'
|
verbose_name=_('Unique')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='ipam:vrf_list'
|
url_name='ipam:vrf_list'
|
||||||
@ -217,10 +218,10 @@ class RIRTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
is_private = BooleanColumn(
|
is_private = BooleanColumn(
|
||||||
verbose_name='Private'
|
verbose_name=_('Private')
|
||||||
)
|
)
|
||||||
aggregate_count = tables.Column(
|
aggregate_count = tables.Column(
|
||||||
verbose_name='Aggregates'
|
verbose_name=_('Aggregates')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=RIR_ACTIONS,
|
template_code=RIR_ACTIONS,
|
||||||
@ -237,32 +238,32 @@ class RIRTable(BaseTable):
|
|||||||
class RIRDetailTable(RIRTable):
|
class RIRDetailTable(RIRTable):
|
||||||
stats_total = tables.Column(
|
stats_total = tables.Column(
|
||||||
accessor='stats.total',
|
accessor='stats.total',
|
||||||
verbose_name='Total',
|
verbose_name=_('Total'),
|
||||||
footer=lambda table: sum(r.stats['total'] for r in table.data)
|
footer=lambda table: sum(r.stats['total'] for r in table.data)
|
||||||
)
|
)
|
||||||
stats_active = tables.Column(
|
stats_active = tables.Column(
|
||||||
accessor='stats.active',
|
accessor='stats.active',
|
||||||
verbose_name='Active',
|
verbose_name=_('Active'),
|
||||||
footer=lambda table: sum(r.stats['active'] for r in table.data)
|
footer=lambda table: sum(r.stats['active'] for r in table.data)
|
||||||
)
|
)
|
||||||
stats_reserved = tables.Column(
|
stats_reserved = tables.Column(
|
||||||
accessor='stats.reserved',
|
accessor='stats.reserved',
|
||||||
verbose_name='Reserved',
|
verbose_name=_('Reserved'),
|
||||||
footer=lambda table: sum(r.stats['reserved'] for r in table.data)
|
footer=lambda table: sum(r.stats['reserved'] for r in table.data)
|
||||||
)
|
)
|
||||||
stats_deprecated = tables.Column(
|
stats_deprecated = tables.Column(
|
||||||
accessor='stats.deprecated',
|
accessor='stats.deprecated',
|
||||||
verbose_name='Deprecated',
|
verbose_name=_('Deprecated'),
|
||||||
footer=lambda table: sum(r.stats['deprecated'] for r in table.data)
|
footer=lambda table: sum(r.stats['deprecated'] for r in table.data)
|
||||||
)
|
)
|
||||||
stats_available = tables.Column(
|
stats_available = tables.Column(
|
||||||
accessor='stats.available',
|
accessor='stats.available',
|
||||||
verbose_name='Available',
|
verbose_name=_('Available'),
|
||||||
footer=lambda table: sum(r.stats['available'] for r in table.data)
|
footer=lambda table: sum(r.stats['available'] for r in table.data)
|
||||||
)
|
)
|
||||||
utilization = tables.TemplateColumn(
|
utilization = tables.TemplateColumn(
|
||||||
template_code=RIR_UTILIZATION,
|
template_code=RIR_UTILIZATION,
|
||||||
verbose_name='Utilization'
|
verbose_name=_('Utilization')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(RIRTable.Meta):
|
class Meta(RIRTable.Meta):
|
||||||
@ -283,11 +284,11 @@ class RIRDetailTable(RIRTable):
|
|||||||
class AggregateTable(BaseTable):
|
class AggregateTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
prefix = tables.LinkColumn(
|
prefix = tables.LinkColumn(
|
||||||
verbose_name='Aggregate'
|
verbose_name=_('Aggregate')
|
||||||
)
|
)
|
||||||
date_added = tables.DateColumn(
|
date_added = tables.DateColumn(
|
||||||
format="Y-m-d",
|
format="Y-m-d",
|
||||||
verbose_name='Added'
|
verbose_name=_('Added')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
@ -297,7 +298,7 @@ class AggregateTable(BaseTable):
|
|||||||
|
|
||||||
class AggregateDetailTable(AggregateTable):
|
class AggregateDetailTable(AggregateTable):
|
||||||
child_count = tables.Column(
|
child_count = tables.Column(
|
||||||
verbose_name='Prefixes'
|
verbose_name=_('Prefixes')
|
||||||
)
|
)
|
||||||
utilization = tables.TemplateColumn(
|
utilization = tables.TemplateColumn(
|
||||||
template_code=UTILIZATION_GRAPH,
|
template_code=UTILIZATION_GRAPH,
|
||||||
@ -320,11 +321,11 @@ class RoleTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
prefix_count = tables.TemplateColumn(
|
prefix_count = tables.TemplateColumn(
|
||||||
template_code=ROLE_PREFIX_COUNT,
|
template_code=ROLE_PREFIX_COUNT,
|
||||||
verbose_name='Prefixes'
|
verbose_name=_('Prefixes')
|
||||||
)
|
)
|
||||||
vlan_count = tables.TemplateColumn(
|
vlan_count = tables.TemplateColumn(
|
||||||
template_code=ROLE_VLAN_COUNT,
|
template_code=ROLE_VLAN_COUNT,
|
||||||
verbose_name='VLANs'
|
verbose_name=_('VLANs')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=ROLE_ACTIONS,
|
template_code=ROLE_ACTIONS,
|
||||||
@ -353,7 +354,7 @@ class PrefixTable(BaseTable):
|
|||||||
)
|
)
|
||||||
vrf = tables.TemplateColumn(
|
vrf = tables.TemplateColumn(
|
||||||
template_code=VRF_LINK,
|
template_code=VRF_LINK,
|
||||||
verbose_name='VRF'
|
verbose_name=_('VRF')
|
||||||
)
|
)
|
||||||
tenant = tables.TemplateColumn(
|
tenant = tables.TemplateColumn(
|
||||||
template_code=TENANT_LINK
|
template_code=TENANT_LINK
|
||||||
@ -365,13 +366,13 @@ class PrefixTable(BaseTable):
|
|||||||
vlan = tables.LinkColumn(
|
vlan = tables.LinkColumn(
|
||||||
viewname='ipam:vlan',
|
viewname='ipam:vlan',
|
||||||
args=[Accessor('vlan.pk')],
|
args=[Accessor('vlan.pk')],
|
||||||
verbose_name='VLAN'
|
verbose_name=_('VLAN')
|
||||||
)
|
)
|
||||||
role = tables.TemplateColumn(
|
role = tables.TemplateColumn(
|
||||||
template_code=PREFIX_ROLE_LINK
|
template_code=PREFIX_ROLE_LINK
|
||||||
)
|
)
|
||||||
is_pool = BooleanColumn(
|
is_pool = BooleanColumn(
|
||||||
verbose_name='Pool'
|
verbose_name=_('Pool')
|
||||||
)
|
)
|
||||||
|
|
||||||
add_prefetch = False
|
add_prefetch = False
|
||||||
@ -415,11 +416,11 @@ class IPAddressTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
address = tables.TemplateColumn(
|
address = tables.TemplateColumn(
|
||||||
template_code=IPADDRESS_LINK,
|
template_code=IPADDRESS_LINK,
|
||||||
verbose_name='IP Address'
|
verbose_name=_('IP Address')
|
||||||
)
|
)
|
||||||
vrf = tables.TemplateColumn(
|
vrf = tables.TemplateColumn(
|
||||||
template_code=VRF_LINK,
|
template_code=VRF_LINK,
|
||||||
verbose_name='VRF'
|
verbose_name=_('VRF')
|
||||||
)
|
)
|
||||||
status = tables.TemplateColumn(
|
status = tables.TemplateColumn(
|
||||||
template_code=STATUS_LABEL
|
template_code=STATUS_LABEL
|
||||||
@ -450,7 +451,7 @@ class IPAddressDetailTable(IPAddressTable):
|
|||||||
viewname='ipam:ipaddress',
|
viewname='ipam:ipaddress',
|
||||||
args=[Accessor('nat_inside.pk')],
|
args=[Accessor('nat_inside.pk')],
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='NAT (Inside)'
|
verbose_name=_('NAT (Inside)')
|
||||||
)
|
)
|
||||||
tenant = tables.TemplateColumn(
|
tenant = tables.TemplateColumn(
|
||||||
template_code=COL_TENANT
|
template_code=COL_TENANT
|
||||||
@ -472,7 +473,7 @@ class IPAddressDetailTable(IPAddressTable):
|
|||||||
class IPAddressAssignTable(BaseTable):
|
class IPAddressAssignTable(BaseTable):
|
||||||
address = tables.TemplateColumn(
|
address = tables.TemplateColumn(
|
||||||
template_code=IPADDRESS_ASSIGN_LINK,
|
template_code=IPADDRESS_ASSIGN_LINK,
|
||||||
verbose_name='IP Address'
|
verbose_name=_('IP Address')
|
||||||
)
|
)
|
||||||
status = tables.TemplateColumn(
|
status = tables.TemplateColumn(
|
||||||
template_code=STATUS_LABEL
|
template_code=STATUS_LABEL
|
||||||
@ -496,11 +497,11 @@ class InterfaceIPAddressTable(BaseTable):
|
|||||||
List IP addresses assigned to a specific Interface.
|
List IP addresses assigned to a specific Interface.
|
||||||
"""
|
"""
|
||||||
address = tables.LinkColumn(
|
address = tables.LinkColumn(
|
||||||
verbose_name='IP Address'
|
verbose_name=_('IP Address')
|
||||||
)
|
)
|
||||||
vrf = tables.TemplateColumn(
|
vrf = tables.TemplateColumn(
|
||||||
template_code=VRF_LINK,
|
template_code=VRF_LINK,
|
||||||
verbose_name='VRF'
|
verbose_name=_('VRF')
|
||||||
)
|
)
|
||||||
status = tables.TemplateColumn(
|
status = tables.TemplateColumn(
|
||||||
template_code=STATUS_LABEL
|
template_code=STATUS_LABEL
|
||||||
@ -526,7 +527,7 @@ class VLANGroupTable(BaseTable):
|
|||||||
args=[Accessor('site.slug')]
|
args=[Accessor('site.slug')]
|
||||||
)
|
)
|
||||||
vlan_count = tables.Column(
|
vlan_count = tables.Column(
|
||||||
verbose_name='VLANs'
|
verbose_name=_('VLANs')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=VLANGROUP_ACTIONS,
|
template_code=VLANGROUP_ACTIONS,
|
||||||
@ -548,7 +549,7 @@ class VLANTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
vid = tables.TemplateColumn(
|
vid = tables.TemplateColumn(
|
||||||
template_code=VLAN_LINK,
|
template_code=VLAN_LINK,
|
||||||
verbose_name='ID'
|
verbose_name=_('ID')
|
||||||
)
|
)
|
||||||
site = tables.LinkColumn(
|
site = tables.LinkColumn(
|
||||||
viewname='dcim:site',
|
viewname='dcim:site',
|
||||||
@ -580,7 +581,7 @@ class VLANDetailTable(VLANTable):
|
|||||||
prefixes = tables.TemplateColumn(
|
prefixes = tables.TemplateColumn(
|
||||||
template_code=VLAN_PREFIXES,
|
template_code=VLAN_PREFIXES,
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='Prefixes'
|
verbose_name=_('Prefixes')
|
||||||
)
|
)
|
||||||
tenant = tables.TemplateColumn(
|
tenant = tables.TemplateColumn(
|
||||||
template_code=COL_TENANT
|
template_code=COL_TENANT
|
||||||
@ -599,7 +600,7 @@ class VLANMemberTable(BaseTable):
|
|||||||
order_by=['device', 'virtual_machine']
|
order_by=['device', 'virtual_machine']
|
||||||
)
|
)
|
||||||
name = tables.LinkColumn(
|
name = tables.LinkColumn(
|
||||||
verbose_name='Interface'
|
verbose_name=_('Interface')
|
||||||
)
|
)
|
||||||
untagged = tables.TemplateColumn(
|
untagged = tables.TemplateColumn(
|
||||||
template_code=VLAN_MEMBER_UNTAGGED,
|
template_code=VLAN_MEMBER_UNTAGGED,
|
||||||
@ -623,7 +624,7 @@ class InterfaceVLANTable(BaseTable):
|
|||||||
vid = tables.LinkColumn(
|
vid = tables.LinkColumn(
|
||||||
viewname='ipam:vlan',
|
viewname='ipam:vlan',
|
||||||
args=[Accessor('pk')],
|
args=[Accessor('pk')],
|
||||||
verbose_name='ID'
|
verbose_name=_('ID')
|
||||||
)
|
)
|
||||||
tagged = BooleanColumn()
|
tagged = BooleanColumn()
|
||||||
site = tables.LinkColumn(
|
site = tables.LinkColumn(
|
||||||
@ -632,7 +633,7 @@ class InterfaceVLANTable(BaseTable):
|
|||||||
)
|
)
|
||||||
group = tables.Column(
|
group = tables.Column(
|
||||||
accessor=Accessor('group.name'),
|
accessor=Accessor('group.name'),
|
||||||
verbose_name='Group'
|
verbose_name=_('Group')
|
||||||
)
|
)
|
||||||
tenant = tables.TemplateColumn(
|
tenant = tables.TemplateColumn(
|
||||||
template_code=COL_TENANT
|
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 import forms
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utilities.forms import BootstrapMixin
|
from utilities.forms import BootstrapMixin
|
||||||
|
|
||||||
OBJ_TYPE_CHOICES = (
|
OBJ_TYPE_CHOICES = (
|
||||||
('', 'All Objects'),
|
('', _('All Objects')),
|
||||||
('Circuits', (
|
(_('Circuits'), (
|
||||||
('provider', 'Providers'),
|
('provider', _('Providers')),
|
||||||
('circuit', 'Circuits'),
|
('circuit', _('Circuits')),
|
||||||
)),
|
)),
|
||||||
('DCIM', (
|
(_('DCIM'), (
|
||||||
('site', 'Sites'),
|
('site', _('Sites')),
|
||||||
('rack', 'Racks'),
|
('rack', _('Racks')),
|
||||||
('rackgroup', 'Rack Groups'),
|
('rackgroup', _('Rack Groups')),
|
||||||
('devicetype', 'Device types'),
|
('devicetype', _('Device types')),
|
||||||
('device', 'Devices'),
|
('device', _('Devices')),
|
||||||
('virtualchassis', 'Virtual Chassis'),
|
('virtualchassis', _('Virtual Chassis')),
|
||||||
('cable', 'Cables'),
|
('cable', _('Cables')),
|
||||||
('powerfeed', 'Power Feeds'),
|
('powerfeed', _('Power Feeds')),
|
||||||
)),
|
)),
|
||||||
('IPAM', (
|
(_('IPAM'), (
|
||||||
('vrf', 'VRFs'),
|
('vrf', _('VRFs')),
|
||||||
('aggregate', 'Aggregates'),
|
('aggregate', _('Aggregates')),
|
||||||
('prefix', 'Prefixes'),
|
('prefix', _('Prefixes')),
|
||||||
('ipaddress', 'IP addresses'),
|
('ipaddress', _('IP addresses')),
|
||||||
('vlan', 'VLANs'),
|
('vlan', _('VLANs')),
|
||||||
)),
|
)),
|
||||||
('Secrets', (
|
(_('Secrets'), (
|
||||||
('secret', 'Secrets'),
|
('secret', _('Secrets')),
|
||||||
)),
|
)),
|
||||||
('Tenancy', (
|
(_('Tenancy'), (
|
||||||
('tenant', 'Tenants'),
|
('tenant', _('Tenants')),
|
||||||
)),
|
)),
|
||||||
('Virtualization', (
|
(_('Virtualization'), (
|
||||||
('cluster', 'Clusters'),
|
('cluster', _('Clusters')),
|
||||||
('virtualmachine', 'Virtual machines'),
|
('virtualmachine', _('Virtual machines')),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SearchForm(BootstrapMixin, forms.Form):
|
class SearchForm(BootstrapMixin, forms.Form):
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
obj_type = forms.ChoiceField(
|
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({
|
CACHEOPS.update({
|
||||||
"{}.{}".format(plugin_name, key): value for key, value in plugin_config.caching_config.items()
|
"{}.{}".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
|
import django_filters
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||||
@ -20,30 +21,31 @@ class SecretRoleFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
fields = ['id', 'name', 'slug']
|
fields = ['id', 'name', 'slug']
|
||||||
|
|
||||||
|
|
||||||
class SecretFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class SecretFilterSet(
|
||||||
|
BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=SecretRole.objects.all(),
|
queryset=SecretRole.objects.all(),
|
||||||
label='Role (ID)',
|
label=_('Role (ID)'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='role__slug',
|
field_name='role__slug',
|
||||||
queryset=SecretRole.objects.all(),
|
queryset=SecretRole.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Role (slug)',
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
device_id = django_filters.ModelMultipleChoiceFilter(
|
device_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
label='Device (ID)',
|
label=_('Device (ID)'),
|
||||||
)
|
)
|
||||||
device = django_filters.ModelMultipleChoiceFilter(
|
device = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device__name',
|
field_name='device__name',
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
label='Device (name)',
|
label=_('Device (name)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from Crypto.Cipher import PKCS1_OAEP
|
from Crypto.Cipher import PKCS1_OAEP
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
from extras.forms import (
|
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.
|
Validate the format and type of an RSA key.
|
||||||
"""
|
"""
|
||||||
if key.startswith('ssh-rsa '):
|
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:
|
try:
|
||||||
key = RSA.importKey(key)
|
key = RSA.importKey(key)
|
||||||
except ValueError:
|
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:
|
except Exception as e:
|
||||||
raise forms.ValidationError("Invalid key detected: {}".format(e))
|
raise forms.ValidationError("Invalid key detected: {}".format(e))
|
||||||
if is_secret and not key.has_private():
|
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():
|
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:
|
try:
|
||||||
PKCS1_OAEP.new(key)
|
PKCS1_OAEP.new(key)
|
||||||
except Exception:
|
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(
|
plaintext = forms.CharField(
|
||||||
max_length=SECRET_PLAINTEXT_MAX_LENGTH,
|
max_length=SECRET_PLAINTEXT_MAX_LENGTH,
|
||||||
required=False,
|
required=False,
|
||||||
label='Plaintext',
|
label=_('Plaintext'),
|
||||||
widget=forms.PasswordInput(
|
widget=forms.PasswordInput(
|
||||||
attrs={
|
attrs={
|
||||||
'class': 'requires-session-key',
|
'class': 'requires-session-key',
|
||||||
@ -84,7 +85,7 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
plaintext2 = forms.CharField(
|
plaintext2 = forms.CharField(
|
||||||
max_length=SECRET_PLAINTEXT_MAX_LENGTH,
|
max_length=SECRET_PLAINTEXT_MAX_LENGTH,
|
||||||
required=False,
|
required=False,
|
||||||
label='Plaintext (verify)',
|
label=_('Plaintext (verify)'),
|
||||||
widget=forms.PasswordInput()
|
widget=forms.PasswordInput()
|
||||||
)
|
)
|
||||||
role = DynamicModelChoiceField(
|
role = DynamicModelChoiceField(
|
||||||
@ -130,22 +131,22 @@ class SecretCSVForm(CustomFieldModelCSVForm):
|
|||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned device'
|
help_text=_('Assigned device')
|
||||||
)
|
)
|
||||||
role = CSVModelChoiceField(
|
role = CSVModelChoiceField(
|
||||||
queryset=SecretRole.objects.all(),
|
queryset=SecretRole.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned role'
|
help_text=_('Assigned role')
|
||||||
)
|
)
|
||||||
plaintext = forms.CharField(
|
plaintext = forms.CharField(
|
||||||
help_text='Plaintext secret data'
|
help_text=_('Plaintext secret data')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Secret
|
model = Secret
|
||||||
fields = Secret.csv_headers
|
fields = Secret.csv_headers
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'name': 'Name or username',
|
'name': _('Name or username'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
@ -154,7 +155,8 @@ class SecretCSVForm(CustomFieldModelCSVForm):
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
|
class SecretBulkEditForm(
|
||||||
|
BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Secret.objects.all(),
|
queryset=Secret.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -178,7 +180,7 @@ class SecretFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
model = Secret
|
model = Secret
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
role = DynamicModelMultipleChoiceField(
|
role = DynamicModelMultipleChoiceField(
|
||||||
queryset=SecretRole.objects.all(),
|
queryset=SecretRole.objects.all(),
|
||||||
@ -220,7 +222,7 @@ class UserKeyForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class ActivateUserKeyForm(forms.Form):
|
class ActivateUserKeyForm(forms.Form):
|
||||||
_selected_action = forms.ModelMultipleChoiceField(
|
_selected_action = forms.ModelMultipleChoiceField(
|
||||||
queryset=UserKey.objects.all(),
|
queryset=UserKey.objects.all(),
|
||||||
label='User Keys'
|
label=_('User Keys')
|
||||||
)
|
)
|
||||||
secret_key = forms.CharField(
|
secret_key = forms.CharField(
|
||||||
widget=forms.Textarea(
|
widget=forms.Textarea(
|
||||||
@ -228,5 +230,5 @@ class ActivateUserKeyForm(forms.Form):
|
|||||||
'class': 'vLargeTextField',
|
'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.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
@ -51,7 +52,7 @@ class UserKey(models.Model):
|
|||||||
editable=False
|
editable=False
|
||||||
)
|
)
|
||||||
public_key = models.TextField(
|
public_key = models.TextField(
|
||||||
verbose_name='RSA public key'
|
verbose_name=_('RSA public key')
|
||||||
)
|
)
|
||||||
master_key_cipher = models.BinaryField(
|
master_key_cipher = models.BinaryField(
|
||||||
max_length=512,
|
max_length=512,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
||||||
from .models import SecretRole, Secret
|
from .models import SecretRole, Secret
|
||||||
@ -21,7 +22,7 @@ class SecretRoleTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
secret_count = tables.Column(
|
secret_count = tables.Column(
|
||||||
verbose_name='Secrets'
|
verbose_name=_('Secrets')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=SECRETROLE_ACTIONS,
|
template_code=SECRETROLE_ACTIONS,
|
||||||
|
@ -6,6 +6,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
|
|||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
from utilities.views import (
|
from utilities.views import (
|
||||||
@ -117,7 +118,7 @@ def secret_add(request):
|
|||||||
secret.save()
|
secret.save()
|
||||||
form.save_m2m()
|
form.save_m2m()
|
||||||
|
|
||||||
messages.success(request, "Added new secret: {}.".format(secret))
|
messages.success(request, _("Added new secret: {}.").format(secret))
|
||||||
if '_addanother' in request.POST:
|
if '_addanother' in request.POST:
|
||||||
return redirect('secrets:secret_add')
|
return redirect('secrets:secret_add')
|
||||||
else:
|
else:
|
||||||
@ -164,7 +165,7 @@ def secret_edit(request, pk):
|
|||||||
secret.plaintext = form.cleaned_data['plaintext']
|
secret.plaintext = form.cleaned_data['plaintext']
|
||||||
secret.encrypt(master_key)
|
secret.encrypt(master_key)
|
||||||
secret.save()
|
secret.save()
|
||||||
messages.success(request, "Modified secret {}.".format(secret))
|
messages.success(request, _("Modified secret {}.").format(secret))
|
||||||
return redirect('secrets:secret', pk=secret.pk)
|
return redirect('secrets:secret', pk=secret.pk)
|
||||||
else:
|
else:
|
||||||
form.add_error(None, "Invalid session key. Unable to encrypt secret data.")
|
form.add_error(None, "Invalid session key. Unable to encrypt secret data.")
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet
|
||||||
from utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter
|
from utilities.filters import BaseFilterSet, NameSlugSearchFilterSet, TagFilter, TreeNodeMultipleChoiceFilter
|
||||||
@ -16,13 +17,13 @@ __all__ = (
|
|||||||
class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||||
parent_id = django_filters.ModelMultipleChoiceFilter(
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
label='Tenant group (ID)',
|
label=_('Tenant group (ID)'),
|
||||||
)
|
)
|
||||||
parent = django_filters.ModelMultipleChoiceFilter(
|
parent = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='parent__slug',
|
field_name='parent__slug',
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Tenant group group (slug)',
|
label=_('Tenant group group (slug)'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -33,20 +34,20 @@ class TenantGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class TenantFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class TenantFilterSet(BaseFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
group_id = TreeNodeMultipleChoiceFilter(
|
group_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
field_name='group',
|
field_name='group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Tenant group (ID)',
|
label=_('Tenant group (ID)'),
|
||||||
)
|
)
|
||||||
group = TreeNodeMultipleChoiceFilter(
|
group = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
field_name='group',
|
field_name='group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Tenant group (slug)',
|
label=_('Tenant group (slug)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -73,22 +74,22 @@ class TenancyFilterSet(django_filters.FilterSet):
|
|||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
field_name='tenant__group',
|
field_name='tenant__group',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Tenant Group (ID)',
|
label=_('Tenant Group (ID)'),
|
||||||
)
|
)
|
||||||
tenant_group = TreeNodeMultipleChoiceFilter(
|
tenant_group = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
field_name='tenant__group',
|
field_name='tenant__group',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Tenant Group (slug)',
|
label=_('Tenant Group (slug)'),
|
||||||
)
|
)
|
||||||
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
tenant_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
label='Tenant (ID)',
|
label=_('Tenant (ID)'),
|
||||||
)
|
)
|
||||||
tenant = django_filters.ModelMultipleChoiceFilter(
|
tenant = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
field_name='tenant__slug',
|
field_name='tenant__slug',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Tenant (slug)',
|
label=_('Tenant (slug)'),
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from extras.forms import (
|
from extras.forms import (
|
||||||
AddRemoveTagsForm, CustomFieldModelForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldModelCSVForm,
|
AddRemoveTagsForm, CustomFieldModelForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldModelCSVForm,
|
||||||
@ -37,7 +38,7 @@ class TenantGroupCSVForm(CSVModelForm):
|
|||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Parent group'
|
help_text=_('Parent group')
|
||||||
)
|
)
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ class TenantCSVForm(CustomFieldModelCSVForm):
|
|||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned group'
|
help_text=_('Assigned group')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -102,7 +103,7 @@ class TenantFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
model = Tenant
|
model = Tenant
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
group = DynamicModelMultipleChoiceField(
|
group = DynamicModelMultipleChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
from utilities.tables import BaseTable, TagColumn, ToggleColumn
|
||||||
from .models import Tenant, TenantGroup
|
from .models import Tenant, TenantGroup
|
||||||
@ -42,7 +43,7 @@ class TenantGroupTable(BaseTable):
|
|||||||
orderable=False
|
orderable=False
|
||||||
)
|
)
|
||||||
tenant_count = tables.Column(
|
tenant_count = tables.Column(
|
||||||
verbose_name='Tenants'
|
verbose_name=_('Tenants')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=TENANTGROUP_ACTIONS,
|
template_code=TENANTGROUP_ACTIONS,
|
||||||
|
@ -2,6 +2,7 @@ from django import forms
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin as UserAdmin_
|
from django.contrib.auth.admin import UserAdmin as UserAdmin_
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from .models import Token, UserConfig
|
from .models import Token, UserConfig
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ class UserConfigInline(admin.TabularInline):
|
|||||||
model = UserConfig
|
model = UserConfig
|
||||||
readonly_fields = ('data',)
|
readonly_fields = ('data',)
|
||||||
can_delete = False
|
can_delete = False
|
||||||
verbose_name = 'Preferences'
|
verbose_name = _('Preferences')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(User)
|
@admin.register(User)
|
||||||
@ -27,7 +28,7 @@ class UserAdmin(UserAdmin_):
|
|||||||
class TokenAdminForm(forms.ModelForm):
|
class TokenAdminForm(forms.ModelForm):
|
||||||
key = forms.CharField(
|
key = forms.CharField(
|
||||||
required=False,
|
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:
|
class Meta:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
|
from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm as DjangoPasswordChangeForm
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utilities.forms import BootstrapMixin, DateTimePicker
|
from utilities.forms import BootstrapMixin, DateTimePicker
|
||||||
from .models import Token
|
from .models import Token
|
||||||
@ -21,7 +22,7 @@ class PasswordChangeForm(BootstrapMixin, DjangoPasswordChangeForm):
|
|||||||
class TokenForm(BootstrapMixin, forms.ModelForm):
|
class TokenForm(BootstrapMixin, forms.ModelForm):
|
||||||
key = forms.CharField(
|
key = forms.CharField(
|
||||||
required=False,
|
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:
|
class Meta:
|
||||||
|
@ -8,6 +8,7 @@ from django.db import models
|
|||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utilities.utils import flatten_dict
|
from utilities.utils import flatten_dict
|
||||||
|
|
||||||
@ -162,7 +163,7 @@ class Token(models.Model):
|
|||||||
)
|
)
|
||||||
write_enabled = models.BooleanField(
|
write_enabled = models.BooleanField(
|
||||||
default=True,
|
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(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
|
@ -11,6 +11,7 @@ from django.shortcuts import get_object_or_404, redirect, render
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.http import is_safe_url
|
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.decorators.debug import sensitive_post_parameters
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ class LoginView(View):
|
|||||||
# Authenticate user
|
# Authenticate user
|
||||||
auth_login(request, form.get_user())
|
auth_login(request, form.get_user())
|
||||||
logger.info(f"User {request.user} successfully authenticated")
|
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}")
|
logger.debug(f"Redirecting user to {redirect_to}")
|
||||||
return HttpResponseRedirect(redirect_to)
|
return HttpResponseRedirect(redirect_to)
|
||||||
@ -88,7 +89,7 @@ class LogoutView(View):
|
|||||||
username = request.user
|
username = request.user
|
||||||
auth_logout(request)
|
auth_logout(request)
|
||||||
logger.info(f"User {username} has logged out")
|
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
|
# Delete session key cookie (if set) upon logout
|
||||||
response = HttpResponseRedirect(reverse('home'))
|
response = HttpResponseRedirect(reverse('home'))
|
||||||
@ -130,7 +131,7 @@ class UserConfigView(LoginRequiredMixin, View):
|
|||||||
if key in data:
|
if key in data:
|
||||||
userconfig.clear(key)
|
userconfig.clear(key)
|
||||||
userconfig.save()
|
userconfig.save()
|
||||||
messages.success(request, "Your preferences have been updated.")
|
messages.success(request, _('Your preferences have been updated.'))
|
||||||
|
|
||||||
return redirect('user:preferences')
|
return redirect('user:preferences')
|
||||||
|
|
||||||
@ -156,7 +157,7 @@ class ChangePasswordView(LoginRequiredMixin, View):
|
|||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
update_session_auth_hash(request, form.user)
|
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 redirect('user:profile')
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
@ -206,7 +207,7 @@ class UserKeyEditView(LoginRequiredMixin, View):
|
|||||||
uk = form.save(commit=False)
|
uk = form.save(commit=False)
|
||||||
uk.user = request.user
|
uk.user = request.user
|
||||||
uk.save()
|
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 redirect('user:userkey')
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
return render(request, self.template_name, {
|
||||||
@ -237,7 +238,7 @@ class SessionKeyDeleteView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
# Delete session key
|
# Delete session key
|
||||||
sessionkey.delete()
|
sessionkey.delete()
|
||||||
messages.success(request, "Session key deleted")
|
messages.success(request, _('Session key deleted'))
|
||||||
|
|
||||||
# Delete cookie
|
# Delete cookie
|
||||||
response = redirect('user:userkey')
|
response = redirect('user:userkey')
|
||||||
@ -304,7 +305,7 @@ class TokenEditView(LoginRequiredMixin, View):
|
|||||||
token.user = request.user
|
token.user = request.user
|
||||||
token.save()
|
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)
|
messages.success(request, msg)
|
||||||
|
|
||||||
if '_addanother' in request.POST:
|
if '_addanother' in request.POST:
|
||||||
@ -344,7 +345,7 @@ class TokenDeleteView(PermissionRequiredMixin, View):
|
|||||||
form = ConfirmationForm(request.POST)
|
form = ConfirmationForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
token.delete()
|
token.delete()
|
||||||
messages.success(request, "Token deleted")
|
messages.success(request, _('Token deleted'))
|
||||||
return redirect('user:token_list')
|
return redirect('user:token_list')
|
||||||
|
|
||||||
return render(request, 'utilities/obj_delete.html', {
|
return render(request, 'utilities/obj_delete.html', {
|
||||||
|
@ -5,6 +5,7 @@ from django import forms
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django_filters.utils import get_model_field, resolve_field
|
from django_filters.utils import get_model_field, resolve_field
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from utilities.constants import (
|
from utilities.constants import (
|
||||||
@ -274,7 +275,7 @@ class NameSlugSearchFilterSet(django_filters.FilterSet):
|
|||||||
"""
|
"""
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def search(self, queryset, name, value):
|
def search(self, queryset, name, value):
|
||||||
|
@ -14,6 +14,7 @@ from django.db.models import Count
|
|||||||
from django.forms import BoundField
|
from django.forms import BoundField
|
||||||
from django.forms.models import fields_for_model
|
from django.forms.models import fields_for_model
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from .choices import ColorChoices, unpack_grouped_choices
|
from .choices import ColorChoices, unpack_grouped_choices
|
||||||
from .validators import EnhancedURLValidator
|
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]
|
return [(str(tag.slug), '{} ({})'.format(tag.name, tag.count)) for tag in tags]
|
||||||
|
|
||||||
# Choices are fetched each time the form is initialized
|
# 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:
|
class DynamicModelChoiceMixin:
|
||||||
@ -740,7 +741,7 @@ class ImportForm(BootstrapMixin, forms.Form):
|
|||||||
"""
|
"""
|
||||||
data = forms.CharField(
|
data = forms.CharField(
|
||||||
widget=forms.Textarea,
|
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(
|
format = forms.ChoiceField(
|
||||||
choices=(
|
choices=(
|
||||||
@ -791,7 +792,7 @@ class TableConfigForm(BootstrapMixin, forms.Form):
|
|||||||
widget=forms.SelectMultiple(
|
widget=forms.SelectMultiple(
|
||||||
attrs={'size': 10}
|
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):
|
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.html import escape
|
||||||
from django.utils.http import is_safe_url
|
from django.utils.http import is_safe_url
|
||||||
from django.utils.safestring import mark_safe
|
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.decorators.csrf import requires_csrf_token
|
||||||
from django.views.defaults import ERROR_500_TEMPLATE_NAME
|
from django.views.defaults import ERROR_500_TEMPLATE_NAME
|
||||||
from django.views.generic import View
|
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)
|
request.user.config.set(preference_name, form.cleaned_data['columns'], commit=True)
|
||||||
elif 'clear' in request.POST:
|
elif 'clear' in request.POST:
|
||||||
request.user.config.clear(preference_name, commit=True)
|
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())
|
return redirect(request.get_full_path())
|
||||||
|
|
||||||
@ -532,7 +533,7 @@ class ObjectImportView(GetReturnURLMixin, View):
|
|||||||
|
|
||||||
if not model_form.errors:
|
if not model_form.errors:
|
||||||
logger.info(f"Import object {obj} (PK: {obj.pk})")
|
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
|
obj.get_absolute_url(), obj
|
||||||
)))
|
)))
|
||||||
|
|
||||||
@ -946,7 +947,7 @@ class ComponentCreateView(GetReturnURLMixin, View):
|
|||||||
for component_form in new_components:
|
for component_form in new_components:
|
||||||
component_form.save()
|
component_form.save()
|
||||||
|
|
||||||
messages.success(request, "Added {} {}".format(
|
messages.success(request, _('Added {} {}').format(
|
||||||
len(new_components), self.model._meta.verbose_name_plural
|
len(new_components), self.model._meta.verbose_name_plural
|
||||||
))
|
))
|
||||||
if '_addanother' in request.POST:
|
if '_addanother' in request.POST:
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Interface, Platform, Region, Site
|
from dcim.models import DeviceRole, Interface, Platform, Region, Site
|
||||||
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet, LocalConfigContextFilterSet
|
from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet, LocalConfigContextFilterSet
|
||||||
@ -37,50 +38,50 @@ class ClusterGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
class ClusterFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
class ClusterFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='site__region',
|
field_name='site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='site__slug',
|
field_name='site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
group_id = django_filters.ModelMultipleChoiceFilter(
|
group_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
label='Parent group (ID)',
|
label=_('Parent group (ID)'),
|
||||||
)
|
)
|
||||||
group = django_filters.ModelMultipleChoiceFilter(
|
group = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='group__slug',
|
field_name='group__slug',
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Parent group (slug)',
|
label=_('Parent group (slug)'),
|
||||||
)
|
)
|
||||||
type_id = django_filters.ModelMultipleChoiceFilter(
|
type_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
label='Cluster type (ID)',
|
label=_('Cluster type (ID)'),
|
||||||
)
|
)
|
||||||
type = django_filters.ModelMultipleChoiceFilter(
|
type = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='type__slug',
|
field_name='type__slug',
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Cluster type (slug)',
|
label=_('Cluster type (slug)'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ class VirtualMachineFilterSet(
|
|||||||
):
|
):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
choices=VirtualMachineStatusChoices,
|
choices=VirtualMachineStatusChoices,
|
||||||
@ -115,76 +116,76 @@ class VirtualMachineFilterSet(
|
|||||||
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
|
cluster_group_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster__group',
|
field_name='cluster__group',
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
label='Cluster group (ID)',
|
label=_('Cluster group (ID)'),
|
||||||
)
|
)
|
||||||
cluster_group = django_filters.ModelMultipleChoiceFilter(
|
cluster_group = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster__group__slug',
|
field_name='cluster__group__slug',
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Cluster group (slug)',
|
label=_('Cluster group (slug)'),
|
||||||
)
|
)
|
||||||
cluster_type_id = django_filters.ModelMultipleChoiceFilter(
|
cluster_type_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster__type',
|
field_name='cluster__type',
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
label='Cluster type (ID)',
|
label=_('Cluster type (ID)'),
|
||||||
)
|
)
|
||||||
cluster_type = django_filters.ModelMultipleChoiceFilter(
|
cluster_type = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster__type__slug',
|
field_name='cluster__type__slug',
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Cluster type (slug)',
|
label=_('Cluster type (slug)'),
|
||||||
)
|
)
|
||||||
cluster_id = django_filters.ModelMultipleChoiceFilter(
|
cluster_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
label='Cluster (ID)',
|
label=_('Cluster (ID)'),
|
||||||
)
|
)
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='cluster__site__region',
|
field_name='cluster__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
label='Region (ID)',
|
label=_('Region (ID)'),
|
||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='cluster__site__region',
|
field_name='cluster__site__region',
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label=_('Region (slug)'),
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster__site',
|
field_name='cluster__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label=_('Site (ID)'),
|
||||||
)
|
)
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster__site__slug',
|
field_name='cluster__site__slug',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Site (slug)',
|
label=_('Site (slug)'),
|
||||||
)
|
)
|
||||||
role_id = django_filters.ModelMultipleChoiceFilter(
|
role_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
label='Role (ID)',
|
label=_('Role (ID)'),
|
||||||
)
|
)
|
||||||
role = django_filters.ModelMultipleChoiceFilter(
|
role = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='role__slug',
|
field_name='role__slug',
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Role (slug)',
|
label=_('Role (slug)'),
|
||||||
)
|
)
|
||||||
platform_id = django_filters.ModelMultipleChoiceFilter(
|
platform_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
label='Platform (ID)',
|
label=_('Platform (ID)'),
|
||||||
)
|
)
|
||||||
platform = django_filters.ModelMultipleChoiceFilter(
|
platform = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='platform__slug',
|
field_name='platform__slug',
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Platform (slug)',
|
label=_('Platform (slug)'),
|
||||||
)
|
)
|
||||||
mac_address = MultiValueMACAddressFilter(
|
mac_address = MultiValueMACAddressFilter(
|
||||||
field_name='interfaces__mac_address',
|
field_name='interfaces__mac_address',
|
||||||
label='MAC address',
|
label=_('MAC address'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
@ -204,21 +205,21 @@ class VirtualMachineFilterSet(
|
|||||||
class InterfaceFilterSet(BaseFilterSet):
|
class InterfaceFilterSet(BaseFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
label='Search',
|
label=_('Search'),
|
||||||
)
|
)
|
||||||
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
|
virtual_machine_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='virtual_machine',
|
field_name='virtual_machine',
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
label='Virtual machine (ID)',
|
label=_('Virtual machine (ID)'),
|
||||||
)
|
)
|
||||||
virtual_machine = django_filters.ModelMultipleChoiceFilter(
|
virtual_machine = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='virtual_machine__name',
|
field_name='virtual_machine__name',
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
label='Virtual machine',
|
label=_('Virtual machine'),
|
||||||
)
|
)
|
||||||
mac_address = MultiValueMACAddressFilter(
|
mac_address = MultiValueMACAddressFilter(
|
||||||
label='MAC address',
|
label=_('MAC address'),
|
||||||
)
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.choices import InterfaceModeChoices
|
from dcim.choices import InterfaceModeChoices
|
||||||
from dcim.constants import INTERFACE_MTU_MAX, INTERFACE_MTU_MIN
|
from dcim.constants import INTERFACE_MTU_MAX, INTERFACE_MTU_MIN
|
||||||
@ -98,25 +99,25 @@ class ClusterCSVForm(CustomFieldModelCSVForm):
|
|||||||
type = CSVModelChoiceField(
|
type = CSVModelChoiceField(
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Type of cluster'
|
help_text=_('Type of cluster')
|
||||||
)
|
)
|
||||||
group = CSVModelChoiceField(
|
group = CSVModelChoiceField(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Assigned cluster group'
|
help_text=_('Assigned cluster group')
|
||||||
)
|
)
|
||||||
site = CSVModelChoiceField(
|
site = CSVModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Assigned site'
|
help_text=_('Assigned site')
|
||||||
)
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Assigned tenant'
|
help_text=_('Assigned tenant')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -147,7 +148,7 @@ class ClusterBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit
|
|||||||
)
|
)
|
||||||
comments = CommentField(
|
comments = CommentField(
|
||||||
widget=SmallTextarea,
|
widget=SmallTextarea,
|
||||||
label='Comments'
|
label=_('Comments')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -387,12 +388,12 @@ class VirtualMachineCSVForm(CustomFieldModelCSVForm):
|
|||||||
status = CSVChoiceField(
|
status = CSVChoiceField(
|
||||||
choices=VirtualMachineStatusChoices,
|
choices=VirtualMachineStatusChoices,
|
||||||
required=False,
|
required=False,
|
||||||
help_text='Operational status of device'
|
help_text=_('Operational status of device')
|
||||||
)
|
)
|
||||||
cluster = CSVModelChoiceField(
|
cluster = CSVModelChoiceField(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned cluster'
|
help_text=_('Assigned cluster')
|
||||||
)
|
)
|
||||||
role = CSVModelChoiceField(
|
role = CSVModelChoiceField(
|
||||||
queryset=DeviceRole.objects.filter(
|
queryset=DeviceRole.objects.filter(
|
||||||
@ -400,19 +401,19 @@ class VirtualMachineCSVForm(CustomFieldModelCSVForm):
|
|||||||
),
|
),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Functional role'
|
help_text=_('Functional role')
|
||||||
)
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned tenant'
|
help_text=_('Assigned tenant')
|
||||||
)
|
)
|
||||||
platform = CSVModelChoiceField(
|
platform = CSVModelChoiceField(
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned platform'
|
help_text=_('Assigned platform')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -456,19 +457,19 @@ class VirtualMachineBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldB
|
|||||||
)
|
)
|
||||||
vcpus = forms.IntegerField(
|
vcpus = forms.IntegerField(
|
||||||
required=False,
|
required=False,
|
||||||
label='vCPUs'
|
label=_('vCPUs')
|
||||||
)
|
)
|
||||||
memory = forms.IntegerField(
|
memory = forms.IntegerField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Memory (MB)'
|
label=_('Memory (MB)')
|
||||||
)
|
)
|
||||||
disk = forms.IntegerField(
|
disk = forms.IntegerField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Disk (GB)'
|
label=_('Disk (GB)')
|
||||||
)
|
)
|
||||||
comments = CommentField(
|
comments = CommentField(
|
||||||
widget=SmallTextarea,
|
widget=SmallTextarea,
|
||||||
label='Comments'
|
label=_('Comments')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -485,7 +486,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
|
|||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label=_('Search')
|
||||||
)
|
)
|
||||||
cluster_group = DynamicModelMultipleChoiceField(
|
cluster_group = DynamicModelMultipleChoiceField(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
@ -508,7 +509,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
|
|||||||
cluster_id = DynamicModelMultipleChoiceField(
|
cluster_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
label='Cluster'
|
label=_('Cluster')
|
||||||
)
|
)
|
||||||
region = DynamicModelMultipleChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -558,7 +559,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
|
|||||||
)
|
)
|
||||||
mac_address = forms.CharField(
|
mac_address = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='MAC address'
|
label=_('MAC address')
|
||||||
)
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
@ -606,7 +607,7 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm):
|
|||||||
'mode': StaticSelect2()
|
'mode': StaticSelect2()
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
'mode': '802.1Q Mode',
|
'mode': _('802.1Q Mode'),
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'mode': INTERFACE_MODE_HELP_TEXT,
|
'mode': INTERFACE_MODE_HELP_TEXT,
|
||||||
@ -645,7 +646,7 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
|
|||||||
widget=forms.HiddenInput()
|
widget=forms.HiddenInput()
|
||||||
)
|
)
|
||||||
name_pattern = ExpandableNameField(
|
name_pattern = ExpandableNameField(
|
||||||
label='Name'
|
label=_('Name')
|
||||||
)
|
)
|
||||||
type = forms.ChoiceField(
|
type = forms.ChoiceField(
|
||||||
choices=VMInterfaceTypeChoices,
|
choices=VMInterfaceTypeChoices,
|
||||||
@ -660,11 +661,11 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form):
|
|||||||
required=False,
|
required=False,
|
||||||
min_value=INTERFACE_MTU_MIN,
|
min_value=INTERFACE_MTU_MIN,
|
||||||
max_value=INTERFACE_MTU_MAX,
|
max_value=INTERFACE_MTU_MAX,
|
||||||
label='MTU'
|
label=_('MTU')
|
||||||
)
|
)
|
||||||
mac_address = forms.CharField(
|
mac_address = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='MAC Address'
|
label=_('MAC Address')
|
||||||
)
|
)
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
@ -732,7 +733,7 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
required=False,
|
required=False,
|
||||||
min_value=INTERFACE_MTU_MIN,
|
min_value=INTERFACE_MTU_MIN,
|
||||||
max_value=INTERFACE_MTU_MAX,
|
max_value=INTERFACE_MTU_MAX,
|
||||||
label='MTU'
|
label=_('MTU')
|
||||||
)
|
)
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
@ -795,7 +796,7 @@ class VirtualMachineBulkAddComponentForm(BootstrapMixin, forms.Form):
|
|||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
name_pattern = ExpandableNameField(
|
name_pattern = ExpandableNameField(
|
||||||
label='Name'
|
label=_('Name')
|
||||||
)
|
)
|
||||||
|
|
||||||
def clean_tags(self):
|
def clean_tags(self):
|
||||||
|
@ -3,6 +3,7 @@ from django.contrib.contenttypes.fields import GenericRelation
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
from dcim.models import Device
|
from dcim.models import Device
|
||||||
@ -220,7 +221,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
max_length=50,
|
max_length=50,
|
||||||
choices=VirtualMachineStatusChoices,
|
choices=VirtualMachineStatusChoices,
|
||||||
default=VirtualMachineStatusChoices.STATUS_ACTIVE,
|
default=VirtualMachineStatusChoices.STATUS_ACTIVE,
|
||||||
verbose_name='Status'
|
verbose_name=_('Status')
|
||||||
)
|
)
|
||||||
role = models.ForeignKey(
|
role = models.ForeignKey(
|
||||||
to='dcim.DeviceRole',
|
to='dcim.DeviceRole',
|
||||||
@ -236,7 +237,7 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
related_name='+',
|
related_name='+',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Primary IPv4'
|
verbose_name=_('Primary IPv4')
|
||||||
)
|
)
|
||||||
primary_ip6 = models.OneToOneField(
|
primary_ip6 = models.OneToOneField(
|
||||||
to='ipam.IPAddress',
|
to='ipam.IPAddress',
|
||||||
@ -244,22 +245,22 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
related_name='+',
|
related_name='+',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Primary IPv6'
|
verbose_name=_('Primary IPv6')
|
||||||
)
|
)
|
||||||
vcpus = models.PositiveSmallIntegerField(
|
vcpus = models.PositiveSmallIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='vCPUs'
|
verbose_name=_('vCPUs')
|
||||||
)
|
)
|
||||||
memory = models.PositiveIntegerField(
|
memory = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Memory (MB)'
|
verbose_name=_('Memory (MB)')
|
||||||
)
|
)
|
||||||
disk = models.PositiveIntegerField(
|
disk = models.PositiveIntegerField(
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
verbose_name='Disk (GB)'
|
verbose_name=_('Disk (GB)')
|
||||||
)
|
)
|
||||||
comments = models.TextField(
|
comments = models.TextField(
|
||||||
blank=True
|
blank=True
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2.utils import Accessor
|
from django_tables2.utils import Accessor
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from tenancy.tables import COL_TENANT
|
from tenancy.tables import COL_TENANT
|
||||||
@ -51,7 +52,7 @@ class ClusterTypeTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
cluster_count = tables.Column(
|
cluster_count = tables.Column(
|
||||||
verbose_name='Clusters'
|
verbose_name=_('Clusters')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=CLUSTERTYPE_ACTIONS,
|
template_code=CLUSTERTYPE_ACTIONS,
|
||||||
@ -73,7 +74,7 @@ class ClusterGroupTable(BaseTable):
|
|||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
name = tables.LinkColumn()
|
name = tables.LinkColumn()
|
||||||
cluster_count = tables.Column(
|
cluster_count = tables.Column(
|
||||||
verbose_name='Clusters'
|
verbose_name=_('Clusters')
|
||||||
)
|
)
|
||||||
actions = tables.TemplateColumn(
|
actions = tables.TemplateColumn(
|
||||||
template_code=CLUSTERGROUP_ACTIONS,
|
template_code=CLUSTERGROUP_ACTIONS,
|
||||||
@ -104,11 +105,11 @@ class ClusterTable(BaseTable):
|
|||||||
)
|
)
|
||||||
device_count = tables.TemplateColumn(
|
device_count = tables.TemplateColumn(
|
||||||
template_code=CLUSTER_DEVICE_COUNT,
|
template_code=CLUSTER_DEVICE_COUNT,
|
||||||
verbose_name='Devices'
|
verbose_name=_('Devices')
|
||||||
)
|
)
|
||||||
vm_count = tables.TemplateColumn(
|
vm_count = tables.TemplateColumn(
|
||||||
template_code=CLUSTER_VM_COUNT,
|
template_code=CLUSTER_VM_COUNT,
|
||||||
verbose_name='VMs'
|
verbose_name=_('VMs')
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='virtualization:cluster_list'
|
url_name='virtualization:cluster_list'
|
||||||
@ -148,16 +149,16 @@ class VirtualMachineDetailTable(VirtualMachineTable):
|
|||||||
primary_ip4 = tables.LinkColumn(
|
primary_ip4 = tables.LinkColumn(
|
||||||
viewname='ipam:ipaddress',
|
viewname='ipam:ipaddress',
|
||||||
args=[Accessor('primary_ip4.pk')],
|
args=[Accessor('primary_ip4.pk')],
|
||||||
verbose_name='IPv4 Address'
|
verbose_name=_('IPv4 Address')
|
||||||
)
|
)
|
||||||
primary_ip6 = tables.LinkColumn(
|
primary_ip6 = tables.LinkColumn(
|
||||||
viewname='ipam:ipaddress',
|
viewname='ipam:ipaddress',
|
||||||
args=[Accessor('primary_ip6.pk')],
|
args=[Accessor('primary_ip6.pk')],
|
||||||
verbose_name='IPv6 Address'
|
verbose_name=_('IPv6 Address')
|
||||||
)
|
)
|
||||||
primary_ip = tables.TemplateColumn(
|
primary_ip = tables.TemplateColumn(
|
||||||
orderable=False,
|
orderable=False,
|
||||||
verbose_name='IP Address',
|
verbose_name=_('IP Address'),
|
||||||
template_code=VIRTUALMACHINE_PRIMARY_IP
|
template_code=VIRTUALMACHINE_PRIMARY_IP
|
||||||
)
|
)
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
|
@ -4,6 +4,7 @@ from django.db import transaction
|
|||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
from dcim.models import Device, Interface
|
from dcim.models import Device, Interface
|
||||||
@ -195,7 +196,7 @@ class ClusterAddDevicesView(PermissionRequiredMixin, View):
|
|||||||
device.cluster = cluster
|
device.cluster = cluster
|
||||||
device.save()
|
device.save()
|
||||||
|
|
||||||
messages.success(request, "Added {} devices to cluster {}".format(
|
messages.success(request, _('Added {} devices to cluster {}').format(
|
||||||
len(device_pks), cluster
|
len(device_pks), cluster
|
||||||
))
|
))
|
||||||
return redirect(cluster.get_absolute_url())
|
return redirect(cluster.get_absolute_url())
|
||||||
@ -228,7 +229,7 @@ class ClusterRemoveDevicesView(PermissionRequiredMixin, View):
|
|||||||
device.cluster = None
|
device.cluster = None
|
||||||
device.save()
|
device.save()
|
||||||
|
|
||||||
messages.success(request, "Removed {} devices from cluster {}".format(
|
messages.success(request, _('Removed {} devices from cluster {}').format(
|
||||||
len(device_pks), cluster
|
len(device_pks), cluster
|
||||||
))
|
))
|
||||||
return redirect(cluster.get_absolute_url())
|
return redirect(cluster.get_absolute_url())
|
||||||
|
Loading…
Reference in New Issue
Block a user