Coverage for dcim/filtersets.py: 73%
1063 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1import django_filters
2import netaddr
3from django.contrib.contenttypes.models import ContentType
4from django.db.models import Func, IntegerField, Q
5from django.utils.translation import gettext as _
6from drf_spectacular.types import OpenApiTypes
7from drf_spectacular.utils import extend_schema_field
8from netaddr.core import AddrFormatError
10from circuits.models import CircuitTermination, VirtualCircuit, VirtualCircuitTermination
11from extras.filtersets import LocalConfigContextFilterSet
12from extras.models import ConfigTemplate
13from ipam.filtersets import PrimaryIPFilterSet
14from ipam.models import ASN, VRF, IPAddress, VLANTranslationPolicy
15from netbox.choices import ColorChoices, DiameterUnitChoices, FlowRateUnitChoices
16from netbox.filtersets import (
17 AttributeFiltersMixin,
18 BaseFilterSet,
19 ChangeLoggedModelFilterSet,
20 NestedGroupModelFilterSet,
21 NetBoxModelFilterSet,
22 OrganizationalModelFilterSet,
23 PrimaryModelFilterSet,
24)
25from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
26from tenancy.models import *
27from users.filterset_mixins import OwnerFilterMixin
28from users.models import User
29from utilities.filters import (
30 MultiValueBigNumberFilter,
31 MultiValueCharFilter,
32 MultiValueContentTypeFilter,
33 MultiValueMACAddressFilter,
34 MultiValueNumberFilter,
35 MultiValueWWNFilter,
36 NumericArrayFilter,
37 TreeNodeMultipleChoiceFilter,
38)
39from utilities.filtersets import register_filterset
40from virtualization.models import Cluster, ClusterGroup, VirtualMachine, VMInterface
41from vpn.models import L2VPN
42from wireless.choices import WirelessChannelChoices, WirelessRoleChoices
43from wireless.models import WirelessLAN, WirelessLink
45from .choices import *
46from .constants import *
47from .models import *
49__all__ = (
50 'CableBundleFilterSet',
51 'CableFilterSet',
52 'CableTerminationFilterSet',
53 'CabledObjectFilterSet',
54 'CommonInterfaceFilterSet',
55 'ConsoleConnectionFilterSet',
56 'ConsolePortFilterSet',
57 'ConsolePortTemplateFilterSet',
58 'ConsoleServerPortFilterSet',
59 'ConsoleServerPortTemplateFilterSet',
60 'CoolingFeedFilterSet',
61 'CoolingIntakeFilterSet',
62 'CoolingIntakeTemplateFilterSet',
63 'CoolingOutflowFilterSet',
64 'CoolingOutflowTemplateFilterSet',
65 'CoolingSourceFilterSet',
66 'DeviceBayFilterSet',
67 'DeviceBayTemplateFilterSet',
68 'DeviceFilterSet',
69 'DeviceRoleFilterSet',
70 'DeviceTypeFilterSet',
71 'FrontPortFilterSet',
72 'FrontPortTemplateFilterSet',
73 'InterfaceConnectionFilterSet',
74 'InterfaceFilterSet',
75 'InterfaceTemplateFilterSet',
76 'InventoryItemFilterSet',
77 'InventoryItemRoleFilterSet',
78 'InventoryItemTemplateFilterSet',
79 'LocationFilterSet',
80 'MACAddressFilterSet',
81 'ManufacturerFilterSet',
82 'ModuleBayFilterSet',
83 'ModuleBayTemplateFilterSet',
84 'ModuleBayTypeFilterSet',
85 'ModuleFilterSet',
86 'ModuleTypeFilterSet',
87 'ModuleTypeProfileFilterSet',
88 'PathEndpointFilterSet',
89 'PlatformFilterSet',
90 'PowerConnectionFilterSet',
91 'PowerFeedFilterSet',
92 'PowerOutletFilterSet',
93 'PowerOutletTemplateFilterSet',
94 'PowerPanelFilterSet',
95 'PowerPortFilterSet',
96 'PowerPortTemplateFilterSet',
97 'RackFilterSet',
98 'RackGroupFilterSet',
99 'RackReservationFilterSet',
100 'RackRoleFilterSet',
101 'RackTypeFilterSet',
102 'RearPortFilterSet',
103 'RearPortTemplateFilterSet',
104 'RegionFilterSet',
105 'SiteFilterSet',
106 'SiteGroupFilterSet',
107 'VirtualChassisFilterSet',
108 'VirtualDeviceContextFilterSet',
109)
112@register_filterset
113class RegionFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet):
114 parent_id = django_filters.ModelMultipleChoiceFilter(
115 queryset=Region.objects.all(),
116 distinct=False,
117 label=_('Parent region (ID)'),
118 )
119 parent = django_filters.ModelMultipleChoiceFilter(
120 field_name='parent__slug',
121 queryset=Region.objects.all(),
122 distinct=False,
123 to_field_name='slug',
124 label=_('Parent region (slug)'),
125 )
126 ancestor_id = TreeNodeMultipleChoiceFilter(
127 queryset=Region.objects.all(),
128 field_name='parent',
129 lookup_expr='in',
130 label=_('Region (ID)'),
131 )
132 ancestor = TreeNodeMultipleChoiceFilter(
133 queryset=Region.objects.all(),
134 field_name='parent',
135 lookup_expr='in',
136 to_field_name='slug',
137 label=_('Region (slug)'),
138 )
140 class Meta:
141 model = Region
142 fields = ('id', 'name', 'slug', 'description')
145@register_filterset
146class SiteGroupFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet):
147 parent_id = django_filters.ModelMultipleChoiceFilter(
148 queryset=SiteGroup.objects.all(),
149 distinct=False,
150 label=_('Parent site group (ID)'),
151 )
152 parent = django_filters.ModelMultipleChoiceFilter(
153 field_name='parent__slug',
154 queryset=SiteGroup.objects.all(),
155 distinct=False,
156 to_field_name='slug',
157 label=_('Parent site group (slug)'),
158 )
159 ancestor_id = TreeNodeMultipleChoiceFilter(
160 queryset=SiteGroup.objects.all(),
161 field_name='parent',
162 lookup_expr='in',
163 label=_('Site group (ID)'),
164 )
165 ancestor = TreeNodeMultipleChoiceFilter(
166 queryset=SiteGroup.objects.all(),
167 field_name='parent',
168 lookup_expr='in',
169 to_field_name='slug',
170 label=_('Site group (slug)'),
171 )
173 class Meta:
174 model = SiteGroup
175 fields = ('id', 'name', 'slug', 'description')
178@register_filterset
179class SiteFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
180 status = django_filters.MultipleChoiceFilter(
181 choices=SiteStatusChoices,
182 distinct=False,
183 null_value=None
184 )
185 region_id = TreeNodeMultipleChoiceFilter(
186 queryset=Region.objects.all(),
187 field_name='region',
188 lookup_expr='in',
189 label=_('Region (ID)'),
190 )
191 region = TreeNodeMultipleChoiceFilter(
192 queryset=Region.objects.all(),
193 lookup_expr='in',
194 to_field_name='slug',
195 label=_('Region (slug)'),
196 )
197 group_id = TreeNodeMultipleChoiceFilter(
198 queryset=SiteGroup.objects.all(),
199 field_name='group',
200 lookup_expr='in',
201 label=_('Group (ID)'),
202 )
203 group = TreeNodeMultipleChoiceFilter(
204 queryset=SiteGroup.objects.all(),
205 lookup_expr='in',
206 to_field_name='slug',
207 label=_('Group (slug)'),
208 )
209 asn = django_filters.ModelMultipleChoiceFilter(
210 field_name='asns__asn',
211 queryset=ASN.objects.all(),
212 to_field_name='asn',
213 label=_('AS (ID)'),
214 )
215 asn_id = django_filters.ModelMultipleChoiceFilter(
216 field_name='asns',
217 queryset=ASN.objects.all(),
218 label=_('AS (ID)'),
219 )
220 time_zone = MultiValueCharFilter()
222 class Meta:
223 model = Site
224 fields = ('id', 'name', 'slug', 'facility', 'latitude', 'longitude', 'description')
226 def search(self, queryset, name, value):
227 if not value.strip():
228 return queryset
229 qs_filter = (
230 Q(name__icontains=value) |
231 Q(facility__icontains=value) |
232 Q(description__icontains=value) |
233 Q(physical_address__icontains=value) |
234 Q(shipping_address__icontains=value) |
235 Q(comments__icontains=value)
236 )
237 try:
238 qs_filter |= Q(asns__asn=int(value.strip()))
239 except ValueError:
240 pass
241 return queryset.filter(qs_filter).distinct()
244@register_filterset
245class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, NestedGroupModelFilterSet):
246 region_id = TreeNodeMultipleChoiceFilter(
247 queryset=Region.objects.all(),
248 field_name='site__region',
249 lookup_expr='in',
250 label=_('Region (ID)'),
251 )
252 region = TreeNodeMultipleChoiceFilter(
253 queryset=Region.objects.all(),
254 field_name='site__region',
255 lookup_expr='in',
256 to_field_name='slug',
257 label=_('Region (slug)'),
258 )
259 site_group_id = TreeNodeMultipleChoiceFilter(
260 queryset=SiteGroup.objects.all(),
261 field_name='site__group',
262 lookup_expr='in',
263 label=_('Site group (ID)'),
264 )
265 site_group = TreeNodeMultipleChoiceFilter(
266 queryset=SiteGroup.objects.all(),
267 field_name='site__group',
268 lookup_expr='in',
269 to_field_name='slug',
270 label=_('Site group (slug)'),
271 )
272 site_id = django_filters.ModelMultipleChoiceFilter(
273 queryset=Site.objects.all(),
274 distinct=False,
275 label=_('Site (ID)'),
276 )
277 site = django_filters.ModelMultipleChoiceFilter(
278 field_name='site__slug',
279 queryset=Site.objects.all(),
280 distinct=False,
281 to_field_name='slug',
282 label=_('Site (slug)'),
283 )
284 parent_id = django_filters.ModelMultipleChoiceFilter(
285 queryset=Location.objects.all(),
286 distinct=False,
287 label=_('Parent location (ID)'),
288 )
289 parent = django_filters.ModelMultipleChoiceFilter(
290 field_name='parent__slug',
291 queryset=Location.objects.all(),
292 distinct=False,
293 to_field_name='slug',
294 label=_('Parent location (slug)'),
295 )
296 ancestor_id = TreeNodeMultipleChoiceFilter(
297 queryset=Location.objects.all(),
298 field_name='parent',
299 lookup_expr='in',
300 label=_('Location (ID)'),
301 )
302 ancestor = TreeNodeMultipleChoiceFilter(
303 queryset=Location.objects.all(),
304 field_name='parent',
305 lookup_expr='in',
306 to_field_name='slug',
307 label=_('Location (slug)'),
308 )
309 status = django_filters.MultipleChoiceFilter(
310 choices=LocationStatusChoices,
311 distinct=False,
312 null_value=None
313 )
315 class Meta:
316 model = Location
317 fields = ('id', 'name', 'slug', 'facility', 'description')
319 def search(self, queryset, name, value):
320 # Extend `search()` to include querying on Location.facility
321 if value.strip():
322 return super().search(queryset, name, value) | queryset.filter(facility__icontains=value)
323 return queryset
326@register_filterset
327class RackGroupFilterSet(OrganizationalModelFilterSet):
329 class Meta:
330 model = RackGroup
331 fields = ('id', 'name', 'slug', 'description')
334@register_filterset
335class RackRoleFilterSet(OrganizationalModelFilterSet):
337 class Meta:
338 model = RackRole
339 fields = ('id', 'name', 'slug', 'color', 'description')
342@register_filterset
343class RackTypeFilterSet(PrimaryModelFilterSet):
344 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
345 queryset=Manufacturer.objects.all(),
346 distinct=False,
347 label=_('Manufacturer (ID)'),
348 )
349 manufacturer = django_filters.ModelMultipleChoiceFilter(
350 field_name='manufacturer__slug',
351 queryset=Manufacturer.objects.all(),
352 distinct=False,
353 to_field_name='slug',
354 label=_('Manufacturer (slug)'),
355 )
356 form_factor = django_filters.MultipleChoiceFilter(
357 choices=RackFormFactorChoices,
358 distinct=False,
359 )
360 width = django_filters.MultipleChoiceFilter(
361 choices=RackWidthChoices,
362 distinct=False,
363 )
364 cooling_capability = django_filters.MultipleChoiceFilter(
365 choices=RackCoolingCapabilityChoices,
366 distinct=False,
367 )
369 class Meta:
370 model = RackType
371 fields = (
372 'id', 'model', 'slug', 'u_height', 'starting_unit', 'desc_units', 'outer_width', 'outer_height',
373 'outer_depth', 'outer_unit', 'mounting_depth', 'weight', 'max_weight', 'weight_unit', 'cooling_capacity',
374 'description',
376 # Counters
377 'rack_count',
378 )
380 def search(self, queryset, name, value):
381 if not value.strip():
382 return queryset
383 return queryset.filter(
384 Q(model__icontains=value) |
385 Q(description__icontains=value) |
386 Q(comments__icontains=value)
387 )
390@register_filterset
391class RackFilterSet(PrimaryModelFilterSet, TenancyFilterSet, ContactModelFilterSet):
392 region_id = TreeNodeMultipleChoiceFilter(
393 queryset=Region.objects.all(),
394 field_name='site__region',
395 lookup_expr='in',
396 label=_('Region (ID)'),
397 )
398 region = TreeNodeMultipleChoiceFilter(
399 queryset=Region.objects.all(),
400 field_name='site__region',
401 lookup_expr='in',
402 to_field_name='slug',
403 label=_('Region (slug)'),
404 )
405 site_group_id = TreeNodeMultipleChoiceFilter(
406 queryset=SiteGroup.objects.all(),
407 field_name='site__group',
408 lookup_expr='in',
409 label=_('Site group (ID)'),
410 )
411 site_group = TreeNodeMultipleChoiceFilter(
412 queryset=SiteGroup.objects.all(),
413 field_name='site__group',
414 lookup_expr='in',
415 to_field_name='slug',
416 label=_('Site group (slug)'),
417 )
418 site_id = django_filters.ModelMultipleChoiceFilter(
419 queryset=Site.objects.all(),
420 distinct=False,
421 label=_('Site (ID)'),
422 )
423 site = django_filters.ModelMultipleChoiceFilter(
424 field_name='site__slug',
425 queryset=Site.objects.all(),
426 distinct=False,
427 to_field_name='slug',
428 label=_('Site (slug)'),
429 )
430 location_id = TreeNodeMultipleChoiceFilter(
431 queryset=Location.objects.all(),
432 field_name='location',
433 lookup_expr='in',
434 label=_('Location (ID)'),
435 )
436 location = TreeNodeMultipleChoiceFilter(
437 queryset=Location.objects.all(),
438 field_name='location',
439 lookup_expr='in',
440 to_field_name='slug',
441 label=_('Location (slug)'),
442 )
443 group_id = django_filters.ModelMultipleChoiceFilter(
444 queryset=RackGroup.objects.all(),
445 distinct=False,
446 label=_('Group (ID)'),
447 )
448 group = django_filters.ModelMultipleChoiceFilter(
449 field_name='group__slug',
450 queryset=RackGroup.objects.all(),
451 distinct=False,
452 to_field_name='slug',
453 label=_('Group (slug)'),
454 )
455 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
456 field_name='rack_type__manufacturer',
457 queryset=Manufacturer.objects.all(),
458 distinct=False,
459 label=_('Manufacturer (ID)'),
460 )
461 manufacturer = django_filters.ModelMultipleChoiceFilter(
462 field_name='rack_type__manufacturer__slug',
463 queryset=Manufacturer.objects.all(),
464 distinct=False,
465 to_field_name='slug',
466 label=_('Manufacturer (slug)'),
467 )
468 rack_type = django_filters.ModelMultipleChoiceFilter(
469 field_name='rack_type__slug',
470 queryset=RackType.objects.all(),
471 distinct=False,
472 to_field_name='slug',
473 label=_('Rack type (slug)'),
474 )
475 rack_type_id = django_filters.ModelMultipleChoiceFilter(
476 queryset=RackType.objects.all(),
477 distinct=False,
478 label=_('Rack type (ID)'),
479 )
480 status = django_filters.MultipleChoiceFilter(
481 choices=RackStatusChoices,
482 distinct=False,
483 null_value=None
484 )
485 form_factor = django_filters.MultipleChoiceFilter(
486 choices=RackFormFactorChoices,
487 distinct=False,
488 )
489 width = django_filters.MultipleChoiceFilter(
490 choices=RackWidthChoices,
491 distinct=False,
492 )
493 role_id = django_filters.ModelMultipleChoiceFilter(
494 queryset=RackRole.objects.all(),
495 distinct=False,
496 label=_('Role (ID)'),
497 )
498 role = django_filters.ModelMultipleChoiceFilter(
499 field_name='role__slug',
500 queryset=RackRole.objects.all(),
501 distinct=False,
502 to_field_name='slug',
503 label=_('Role (slug)'),
504 )
505 serial = MultiValueCharFilter(
506 lookup_expr='iexact'
507 )
509 cooling_capability = django_filters.MultipleChoiceFilter(
510 choices=RackCoolingCapabilityChoices,
511 distinct=False,
512 )
514 class Meta:
515 model = Rack
516 fields = (
517 'id', 'name', 'facility_id', 'asset_tag', 'u_height', 'starting_unit', 'desc_units', 'outer_width',
518 'outer_height', 'outer_depth', 'outer_unit', 'mounting_depth', 'airflow', 'cooling_capacity',
519 'weight', 'max_weight', 'weight_unit', 'description',
520 )
522 def search(self, queryset, name, value):
523 if not value.strip():
524 return queryset
525 return queryset.filter(
526 Q(name__icontains=value) |
527 Q(facility_id__icontains=value) |
528 Q(serial__icontains=value.strip()) |
529 Q(asset_tag__icontains=value.strip()) |
530 Q(description__icontains=value) |
531 Q(comments__icontains=value)
532 )
535@register_filterset
536class RackReservationFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
537 rack_id = django_filters.ModelMultipleChoiceFilter(
538 queryset=Rack.objects.all(),
539 distinct=False,
540 label=_('Rack (ID)'),
541 )
542 site_id = django_filters.ModelMultipleChoiceFilter(
543 field_name='rack__site',
544 queryset=Site.objects.all(),
545 distinct=False,
546 label=_('Site (ID)'),
547 )
548 site = django_filters.ModelMultipleChoiceFilter(
549 field_name='rack__site__slug',
550 queryset=Site.objects.all(),
551 distinct=False,
552 to_field_name='slug',
553 label=_('Site (slug)'),
554 )
555 region_id = TreeNodeMultipleChoiceFilter(
556 queryset=Region.objects.all(),
557 field_name='rack__site__region',
558 lookup_expr='in',
559 label=_('Region (ID)'),
560 )
561 region = TreeNodeMultipleChoiceFilter(
562 queryset=Region.objects.all(),
563 field_name='rack__site__region',
564 lookup_expr='in',
565 to_field_name='slug',
566 label=_('Region (slug)'),
567 )
568 site_group_id = TreeNodeMultipleChoiceFilter(
569 queryset=SiteGroup.objects.all(),
570 field_name='rack__site__group',
571 lookup_expr='in',
572 label=_('Site group (ID)'),
573 )
574 site_group = TreeNodeMultipleChoiceFilter(
575 queryset=SiteGroup.objects.all(),
576 field_name='rack__site__group',
577 lookup_expr='in',
578 to_field_name='slug',
579 label=_('Site group (slug)'),
580 )
581 location_id = TreeNodeMultipleChoiceFilter(
582 queryset=Location.objects.all(),
583 field_name='rack__location',
584 lookup_expr='in',
585 label=_('Location (ID)'),
586 )
587 location = TreeNodeMultipleChoiceFilter(
588 queryset=Location.objects.all(),
589 field_name='rack__location',
590 lookup_expr='in',
591 to_field_name='slug',
592 label=_('Location (slug)'),
593 )
594 group_id = django_filters.ModelMultipleChoiceFilter(
595 queryset=RackGroup.objects.all(),
596 field_name='rack__group',
597 distinct=False,
598 label=_('Group (ID)'),
599 )
600 group = django_filters.ModelMultipleChoiceFilter(
601 field_name='rack__group__slug',
602 queryset=RackGroup.objects.all(),
603 distinct=False,
604 to_field_name='slug',
605 label=_('Group (slug)'),
606 )
607 status = django_filters.MultipleChoiceFilter(
608 choices=RackReservationStatusChoices,
609 distinct=False,
610 null_value=None
611 )
612 user_id = django_filters.ModelMultipleChoiceFilter(
613 queryset=User.objects.all(),
614 distinct=False,
615 label=_('User (ID)'),
616 )
617 user = django_filters.ModelMultipleChoiceFilter(
618 field_name='user__username',
619 queryset=User.objects.all(),
620 distinct=False,
621 to_field_name='username',
622 label=_('User (name)'),
623 )
624 unit = NumericArrayFilter(
625 field_name='units',
626 lookup_expr='contains'
627 )
628 unit_count_min = django_filters.NumberFilter(
629 field_name='unit_count',
630 lookup_expr='gte',
631 label=_('Minimum unit count'),
632 )
633 unit_count_max = django_filters.NumberFilter(
634 field_name='unit_count',
635 lookup_expr='lte',
636 label=_('Maximum unit count'),
637 )
639 class Meta:
640 model = RackReservation
641 fields = ('id', 'created', 'description')
643 def filter_queryset(self, queryset):
644 # Annotate unit_count here so unit_count_min/unit_count_max filters can reference it.
645 # When called from the list view the queryset is already annotated; Django silently
646 # overwrites a duplicate annotation with the same expression, so this is safe.
647 queryset = queryset.annotate(
648 unit_count=Func('units', function='CARDINALITY', output_field=IntegerField())
649 )
650 return super().filter_queryset(queryset)
652 def search(self, queryset, name, value):
653 if not value.strip():
654 return queryset
655 return queryset.filter(
656 Q(rack__name__icontains=value) |
657 Q(rack__facility_id__icontains=value) |
658 Q(user__username__icontains=value) |
659 Q(description__icontains=value)
660 )
663@register_filterset
664class ManufacturerFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
666 class Meta:
667 model = Manufacturer
668 fields = ('id', 'name', 'slug', 'description')
671@register_filterset
672class DeviceTypeFilterSet(PrimaryModelFilterSet):
673 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
674 queryset=Manufacturer.objects.all(),
675 distinct=False,
676 label=_('Manufacturer (ID)'),
677 )
678 manufacturer = django_filters.ModelMultipleChoiceFilter(
679 field_name='manufacturer__slug',
680 queryset=Manufacturer.objects.all(),
681 distinct=False,
682 to_field_name='slug',
683 label=_('Manufacturer (slug)'),
684 )
685 default_platform_id = TreeNodeMultipleChoiceFilter(
686 queryset=Platform.objects.all(),
687 field_name='default_platform',
688 lookup_expr='in',
689 label=_('Default platform (ID)'),
690 )
691 default_platform = TreeNodeMultipleChoiceFilter(
692 queryset=Platform.objects.all(),
693 field_name='default_platform',
694 to_field_name='slug',
695 lookup_expr='in',
696 label=_('Default platform (slug)'),
697 )
698 has_front_image = django_filters.BooleanFilter(
699 label=_('Has a front image'),
700 method='_has_front_image'
701 )
702 has_rear_image = django_filters.BooleanFilter(
703 label=_('Has a rear image'),
704 method='_has_rear_image'
705 )
706 console_ports = django_filters.BooleanFilter(
707 method='_console_ports',
708 label=_('Has console ports'),
709 )
710 console_server_ports = django_filters.BooleanFilter(
711 method='_console_server_ports',
712 label=_('Has console server ports'),
713 )
714 power_ports = django_filters.BooleanFilter(
715 method='_power_ports',
716 label=_('Has power ports'),
717 )
718 power_outlets = django_filters.BooleanFilter(
719 method='_power_outlets',
720 label=_('Has power outlets'),
721 )
722 interfaces = django_filters.BooleanFilter(
723 method='_interfaces',
724 label=_('Has interfaces'),
725 )
726 pass_through_ports = django_filters.BooleanFilter(
727 method='_pass_through_ports',
728 label=_('Has pass-through ports'),
729 )
730 module_bays = django_filters.BooleanFilter(
731 method='_module_bays',
732 label=_('Has module bays'),
733 )
734 device_bays = django_filters.BooleanFilter(
735 method='_device_bays',
736 label=_('Has device bays'),
737 )
738 inventory_items = django_filters.BooleanFilter(
739 method='_inventory_items',
740 label=_('Has inventory items'),
741 )
743 class Meta:
744 model = DeviceType
745 fields = (
746 'id', 'model', 'slug', 'part_number', 'u_height', 'exclude_from_utilization', 'is_full_depth',
747 'subdevice_role', 'airflow', 'cooling_method', 'weight', 'weight_unit', 'end_of_life', 'description',
749 # Counters
750 'console_port_template_count',
751 'console_server_port_template_count',
752 'power_port_template_count',
753 'power_outlet_template_count',
754 'cooling_intake_template_count',
755 'cooling_outflow_template_count',
756 'interface_template_count',
757 'front_port_template_count',
758 'rear_port_template_count',
759 'device_bay_template_count',
760 'module_bay_template_count',
761 'inventory_item_template_count',
762 'device_count',
763 )
765 def search(self, queryset, name, value):
766 if not value.strip():
767 return queryset
768 return queryset.filter(
769 Q(manufacturer__name__icontains=value) |
770 Q(model__icontains=value) |
771 Q(part_number__icontains=value) |
772 Q(description__icontains=value) |
773 Q(comments__icontains=value)
774 )
776 def _has_front_image(self, queryset, name, value):
777 if value:
778 return queryset.exclude(front_image='')
779 return queryset.filter(front_image='')
781 def _has_rear_image(self, queryset, name, value):
782 if value:
783 return queryset.exclude(rear_image='')
784 return queryset.filter(rear_image='')
786 def _console_ports(self, queryset, name, value):
787 return queryset.exclude(consoleporttemplates__isnull=value)
789 def _console_server_ports(self, queryset, name, value):
790 return queryset.exclude(consoleserverporttemplates__isnull=value)
792 def _power_ports(self, queryset, name, value):
793 return queryset.exclude(powerporttemplates__isnull=value)
795 def _power_outlets(self, queryset, name, value):
796 return queryset.exclude(poweroutlettemplates__isnull=value)
798 def _interfaces(self, queryset, name, value):
799 return queryset.exclude(interfacetemplates__isnull=value)
801 def _pass_through_ports(self, queryset, name, value):
802 return queryset.exclude(
803 frontporttemplates__isnull=value,
804 rearporttemplates__isnull=value
805 )
807 def _module_bays(self, queryset, name, value):
808 return queryset.exclude(modulebaytemplates__isnull=value)
810 def _device_bays(self, queryset, name, value):
811 return queryset.exclude(devicebaytemplates__isnull=value)
813 def _inventory_items(self, queryset, name, value):
814 return queryset.exclude(inventoryitemtemplates__isnull=value)
817@register_filterset
818class ModuleBayTypeFilterSet(PrimaryModelFilterSet):
819 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
820 queryset=Manufacturer.objects.all(),
821 distinct=False,
822 label=_('Manufacturer (ID)'),
823 )
824 manufacturer = django_filters.ModelMultipleChoiceFilter(
825 field_name='manufacturer__slug',
826 queryset=Manufacturer.objects.all(),
827 distinct=False,
828 to_field_name='slug',
829 label=_('Manufacturer (slug)'),
830 )
831 module_type_id = django_filters.ModelMultipleChoiceFilter(
832 field_name='module_types',
833 queryset=ModuleType.objects.all(),
834 label=_('Module type (ID)'),
835 )
836 module_bay_template_id = django_filters.ModelMultipleChoiceFilter(
837 field_name='module_bay_templates',
838 queryset=ModuleBayTemplate.objects.all(),
839 label=_('Module bay template (ID)'),
840 )
841 module_bay_id = django_filters.ModelMultipleChoiceFilter(
842 field_name='module_bays',
843 queryset=ModuleBay.objects.all(),
844 label=_('Module bay (ID)'),
845 )
847 class Meta:
848 model = ModuleBayType
849 fields = ('id', 'name', 'slug', 'color', 'description')
851 def search(self, queryset, name, value):
852 if not value.strip():
853 return queryset
854 return queryset.filter(
855 Q(name__icontains=value) |
856 Q(slug__icontains=value) |
857 Q(description__icontains=value) |
858 Q(comments__icontains=value)
859 )
862@register_filterset
863class ModuleTypeProfileFilterSet(PrimaryModelFilterSet):
865 class Meta:
866 model = ModuleTypeProfile
867 fields = ('id', 'name', 'description')
869 def search(self, queryset, name, value):
870 if not value.strip():
871 return queryset
872 return queryset.filter(
873 Q(name__icontains=value) |
874 Q(description__icontains=value) |
875 Q(comments__icontains=value)
876 )
879@register_filterset
880class ModuleTypeFilterSet(AttributeFiltersMixin, PrimaryModelFilterSet):
881 profile_id = django_filters.ModelMultipleChoiceFilter(
882 queryset=ModuleTypeProfile.objects.all(),
883 distinct=False,
884 label=_('Profile (ID)'),
885 )
886 profile = django_filters.ModelMultipleChoiceFilter(
887 field_name='profile__name',
888 queryset=ModuleTypeProfile.objects.all(),
889 distinct=False,
890 to_field_name='name',
891 label=_('Profile (name)'),
892 )
893 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
894 queryset=Manufacturer.objects.all(),
895 distinct=False,
896 label=_('Manufacturer (ID)'),
897 )
898 manufacturer = django_filters.ModelMultipleChoiceFilter(
899 field_name='manufacturer__slug',
900 queryset=Manufacturer.objects.all(),
901 distinct=False,
902 to_field_name='slug',
903 label=_('Manufacturer (slug)'),
904 )
905 module_bay_type_id = django_filters.ModelMultipleChoiceFilter(
906 field_name='module_bay_types',
907 queryset=ModuleBayType.objects.all(),
908 distinct=False,
909 label=_('Module bay type (ID)'),
910 )
911 module_bay_type = django_filters.ModelMultipleChoiceFilter(
912 field_name='module_bay_types__slug',
913 queryset=ModuleBayType.objects.all(),
914 distinct=False,
915 to_field_name='slug',
916 label=_('Module bay type (slug)'),
917 )
918 console_ports = django_filters.BooleanFilter(
919 method='_console_ports',
920 label=_('Has console ports'),
921 )
922 console_server_ports = django_filters.BooleanFilter(
923 method='_console_server_ports',
924 label=_('Has console server ports'),
925 )
926 power_ports = django_filters.BooleanFilter(
927 method='_power_ports',
928 label=_('Has power ports'),
929 )
930 power_outlets = django_filters.BooleanFilter(
931 method='_power_outlets',
932 label=_('Has power outlets'),
933 )
934 interfaces = django_filters.BooleanFilter(
935 method='_interfaces',
936 label=_('Has interfaces'),
937 )
938 pass_through_ports = django_filters.BooleanFilter(
939 method='_pass_through_ports',
940 label=_('Has pass-through ports'),
941 )
942 module_bays = django_filters.BooleanFilter(
943 method='_module_bays',
944 label=_('Has module bays'),
945 )
947 class Meta:
948 model = ModuleType
949 fields = (
950 'id', 'model', 'part_number', 'airflow', 'cooling_method', 'weight', 'weight_unit', 'end_of_life',
951 'description',
953 # Counters
954 'console_port_template_count',
955 'console_server_port_template_count',
956 'power_port_template_count',
957 'power_outlet_template_count',
958 'cooling_intake_template_count',
959 'cooling_outflow_template_count',
960 'interface_template_count',
961 'front_port_template_count',
962 'rear_port_template_count',
963 'module_bay_template_count',
964 'module_count',
965 )
967 def search(self, queryset, name, value):
968 if not value.strip():
969 return queryset
970 return queryset.filter(
971 Q(manufacturer__name__icontains=value) |
972 Q(model__icontains=value) |
973 Q(part_number__icontains=value) |
974 Q(description__icontains=value) |
975 Q(comments__icontains=value)
976 )
978 def _console_ports(self, queryset, name, value):
979 return queryset.exclude(consoleporttemplates__isnull=value)
981 def _console_server_ports(self, queryset, name, value):
982 return queryset.exclude(consoleserverporttemplates__isnull=value)
984 def _power_ports(self, queryset, name, value):
985 return queryset.exclude(powerporttemplates__isnull=value)
987 def _power_outlets(self, queryset, name, value):
988 return queryset.exclude(poweroutlettemplates__isnull=value)
990 def _interfaces(self, queryset, name, value):
991 return queryset.exclude(interfacetemplates__isnull=value)
993 def _pass_through_ports(self, queryset, name, value):
994 return queryset.exclude(
995 frontporttemplates__isnull=value,
996 rearporttemplates__isnull=value
997 )
999 def _module_bays(self, queryset, name, value):
1000 return queryset.exclude(modulebaytemplates__isnull=value)
1003class DeviceTypeComponentFilterSet(django_filters.FilterSet):
1004 q = django_filters.CharFilter(
1005 method='search',
1006 label=_('Search'),
1007 )
1008 device_type_id = django_filters.ModelMultipleChoiceFilter(
1009 queryset=DeviceType.objects.all(),
1010 distinct=False,
1011 field_name='device_type_id',
1012 label=_('Device type (ID)'),
1013 )
1015 def search(self, queryset, name, value):
1016 if not value.strip():
1017 return queryset
1018 return queryset.filter(
1019 Q(name__icontains=value) |
1020 Q(description__icontains=value)
1021 )
1024class ModularDeviceTypeComponentFilterSet(DeviceTypeComponentFilterSet):
1025 module_type_id = django_filters.ModelMultipleChoiceFilter(
1026 queryset=ModuleType.objects.all(),
1027 distinct=False,
1028 field_name='module_type_id',
1029 label=_('Module type (ID)'),
1030 )
1033@register_filterset
1034class ConsolePortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1036 class Meta:
1037 model = ConsolePortTemplate
1038 fields = ('id', 'name', 'label', 'type', 'description')
1041@register_filterset
1042class ConsoleServerPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1044 class Meta:
1045 model = ConsoleServerPortTemplate
1046 fields = ('id', 'name', 'label', 'type', 'description')
1049@register_filterset
1050class PowerPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1052 class Meta:
1053 model = PowerPortTemplate
1054 fields = ('id', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description')
1057@register_filterset
1058class PowerOutletTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1059 feed_leg = django_filters.MultipleChoiceFilter(
1060 choices=PowerOutletFeedLegChoices,
1061 distinct=False,
1062 null_value=None
1063 )
1064 power_port_id = django_filters.ModelMultipleChoiceFilter(
1065 queryset=PowerPortTemplate.objects.all(),
1066 distinct=False,
1067 label=_('Power port (ID)'),
1068 )
1070 class Meta:
1071 model = PowerOutletTemplate
1072 fields = ('id', 'name', 'label', 'type', 'color', 'feed_leg', 'description')
1075@register_filterset
1076class CoolingIntakeTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1077 type = django_filters.MultipleChoiceFilter(
1078 choices=CoolingConnectorTypeChoices,
1079 distinct=False,
1080 null_value=None
1081 )
1082 diameter_unit = django_filters.MultipleChoiceFilter(
1083 choices=DiameterUnitChoices,
1084 distinct=False,
1085 null_value=None
1086 )
1087 max_flow_unit = django_filters.MultipleChoiceFilter(
1088 choices=FlowRateUnitChoices,
1089 distinct=False,
1090 null_value=None
1091 )
1093 class Meta:
1094 model = CoolingIntakeTemplate
1095 fields = (
1096 'id', 'name', 'label', 'diameter', 'max_flow', 'description',
1097 )
1100@register_filterset
1101class CoolingOutflowTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1102 type = django_filters.MultipleChoiceFilter(
1103 choices=CoolingConnectorTypeChoices,
1104 distinct=False,
1105 null_value=None
1106 )
1107 diameter_unit = django_filters.MultipleChoiceFilter(
1108 choices=DiameterUnitChoices,
1109 distinct=False,
1110 null_value=None
1111 )
1112 cooling_intake_id = django_filters.ModelMultipleChoiceFilter(
1113 field_name='cooling_intake',
1114 queryset=CoolingIntakeTemplate.objects.all(),
1115 distinct=False,
1116 label=_('Cooling intake (ID)'),
1117 )
1119 class Meta:
1120 model = CoolingOutflowTemplate
1121 fields = ('id', 'name', 'label', 'diameter', 'description')
1124@register_filterset
1125class InterfaceTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1126 type = django_filters.MultipleChoiceFilter(
1127 choices=InterfaceTypeChoices,
1128 distinct=False,
1129 null_value=None
1130 )
1131 parent_id = django_filters.ModelMultipleChoiceFilter(
1132 field_name='parent',
1133 queryset=InterfaceTemplate.objects.all(),
1134 distinct=False,
1135 )
1136 bridge_id = django_filters.ModelMultipleChoiceFilter(
1137 field_name='bridge',
1138 queryset=InterfaceTemplate.objects.all(),
1139 distinct=False,
1140 )
1141 poe_mode = django_filters.MultipleChoiceFilter(
1142 choices=InterfacePoEModeChoices,
1143 distinct=False,
1144 )
1145 poe_type = django_filters.MultipleChoiceFilter(
1146 choices=InterfacePoETypeChoices,
1147 distinct=False,
1148 )
1149 rf_role = django_filters.MultipleChoiceFilter(
1150 choices=WirelessRoleChoices,
1151 distinct=False,
1152 )
1154 class Meta:
1155 model = InterfaceTemplate
1156 fields = ('id', 'name', 'label', 'type', 'channels', 'channel_id', 'enabled', 'mgmt_only', 'description')
1159@register_filterset
1160class FrontPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1161 type = django_filters.MultipleChoiceFilter(
1162 choices=PortTypeChoices,
1163 distinct=False,
1164 null_value=None
1165 )
1166 rear_port_id = django_filters.ModelMultipleChoiceFilter(
1167 field_name='mappings__rear_port',
1168 queryset=RearPortTemplate.objects.all(),
1169 to_field_name='rear_port',
1170 label=_('Rear port (ID)'),
1171 )
1173 class Meta:
1174 model = FrontPortTemplate
1175 fields = ('id', 'name', 'label', 'type', 'color', 'positions', 'description')
1178@register_filterset
1179class RearPortTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1180 type = django_filters.MultipleChoiceFilter(
1181 choices=PortTypeChoices,
1182 distinct=False,
1183 null_value=None
1184 )
1185 front_port_id = django_filters.ModelMultipleChoiceFilter(
1186 field_name='mappings__front_port',
1187 queryset=FrontPort.objects.all(),
1188 to_field_name='front_port',
1189 label=_('Front port (ID)'),
1190 )
1192 class Meta:
1193 model = RearPortTemplate
1194 fields = ('id', 'name', 'label', 'type', 'color', 'positions', 'description')
1197@register_filterset
1198class ModuleBayTemplateFilterSet(ChangeLoggedModelFilterSet, ModularDeviceTypeComponentFilterSet):
1199 module_bay_type_id = django_filters.ModelMultipleChoiceFilter(
1200 field_name='module_bay_types',
1201 queryset=ModuleBayType.objects.all(),
1202 distinct=False,
1203 label=_('Module bay type (ID)'),
1204 )
1205 module_bay_type = django_filters.ModelMultipleChoiceFilter(
1206 field_name='module_bay_types__slug',
1207 queryset=ModuleBayType.objects.all(),
1208 distinct=False,
1209 to_field_name='slug',
1210 label=_('Module bay type (slug)'),
1211 )
1213 class Meta:
1214 model = ModuleBayTemplate
1215 fields = ('id', 'name', 'label', 'position', 'enabled', 'description')
1218@register_filterset
1219class DeviceBayTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
1221 class Meta:
1222 model = DeviceBayTemplate
1223 fields = ('id', 'name', 'label', 'enabled', 'description')
1226@register_filterset
1227class InventoryItemTemplateFilterSet(ChangeLoggedModelFilterSet, DeviceTypeComponentFilterSet):
1228 parent_id = django_filters.ModelMultipleChoiceFilter(
1229 queryset=InventoryItemTemplate.objects.all(),
1230 distinct=False,
1231 label=_('Parent inventory item (ID)'),
1232 )
1233 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
1234 queryset=Manufacturer.objects.all(),
1235 distinct=False,
1236 label=_('Manufacturer (ID)'),
1237 )
1238 manufacturer = django_filters.ModelMultipleChoiceFilter(
1239 field_name='manufacturer__slug',
1240 queryset=Manufacturer.objects.all(),
1241 distinct=False,
1242 to_field_name='slug',
1243 label=_('Manufacturer (slug)'),
1244 )
1245 role_id = django_filters.ModelMultipleChoiceFilter(
1246 queryset=InventoryItemRole.objects.all(),
1247 distinct=False,
1248 label=_('Role (ID)'),
1249 )
1250 role = django_filters.ModelMultipleChoiceFilter(
1251 field_name='role__slug',
1252 queryset=InventoryItemRole.objects.all(),
1253 distinct=False,
1254 to_field_name='slug',
1255 label=_('Role (slug)'),
1256 )
1257 component_type = MultiValueContentTypeFilter()
1258 component_id = MultiValueNumberFilter()
1260 class Meta:
1261 model = InventoryItemTemplate
1262 fields = ('id', 'name', 'label', 'part_id', 'description')
1264 def search(self, queryset, name, value):
1265 if not value.strip():
1266 return queryset
1267 qs_filter = (
1268 Q(name__icontains=value) |
1269 Q(part_id__icontains=value) |
1270 Q(description__icontains=value)
1271 )
1272 return queryset.filter(qs_filter)
1275@register_filterset
1276class DeviceRoleFilterSet(NestedGroupModelFilterSet):
1277 config_template_id = django_filters.ModelMultipleChoiceFilter(
1278 queryset=ConfigTemplate.objects.all(),
1279 distinct=False,
1280 label=_('Config template (ID)'),
1281 )
1282 parent_id = django_filters.ModelMultipleChoiceFilter(
1283 queryset=DeviceRole.objects.all(),
1284 distinct=False,
1285 label=_('Parent device role (ID)'),
1286 )
1287 parent = django_filters.ModelMultipleChoiceFilter(
1288 field_name='parent__slug',
1289 queryset=DeviceRole.objects.all(),
1290 distinct=False,
1291 to_field_name='slug',
1292 label=_('Parent device role (slug)'),
1293 )
1294 ancestor_id = TreeNodeMultipleChoiceFilter(
1295 queryset=DeviceRole.objects.all(),
1296 field_name='parent',
1297 lookup_expr='in',
1298 label=_('Parent device role (ID)'),
1299 )
1300 ancestor = TreeNodeMultipleChoiceFilter(
1301 queryset=DeviceRole.objects.all(),
1302 field_name='parent',
1303 lookup_expr='in',
1304 to_field_name='slug',
1305 label=_('Parent device role (slug)'),
1306 )
1308 class Meta:
1309 model = DeviceRole
1310 fields = ('id', 'name', 'slug', 'color', 'vm_role', 'description')
1313@register_filterset
1314class PlatformFilterSet(NestedGroupModelFilterSet):
1315 parent_id = django_filters.ModelMultipleChoiceFilter(
1316 queryset=Platform.objects.all(),
1317 distinct=False,
1318 label=_('Immediate parent platform (ID)'),
1319 )
1320 parent = django_filters.ModelMultipleChoiceFilter(
1321 field_name='parent__slug',
1322 queryset=Platform.objects.all(),
1323 distinct=False,
1324 to_field_name='slug',
1325 label=_('Immediate parent platform (slug)'),
1326 )
1327 ancestor_id = TreeNodeMultipleChoiceFilter(
1328 queryset=Platform.objects.all(),
1329 field_name='parent',
1330 lookup_expr='in',
1331 label=_('Parent platform (ID)'),
1332 )
1333 ancestor = TreeNodeMultipleChoiceFilter(
1334 queryset=Platform.objects.all(),
1335 field_name='parent',
1336 lookup_expr='in',
1337 to_field_name='slug',
1338 label=_('Parent platform (slug)'),
1339 )
1340 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
1341 field_name='manufacturer',
1342 queryset=Manufacturer.objects.all(),
1343 distinct=False,
1344 label=_('Manufacturer (ID)'),
1345 )
1346 manufacturer = django_filters.ModelMultipleChoiceFilter(
1347 field_name='manufacturer__slug',
1348 queryset=Manufacturer.objects.all(),
1349 distinct=False,
1350 to_field_name='slug',
1351 label=_('Manufacturer (slug)'),
1352 )
1353 available_for_device_type = django_filters.ModelChoiceFilter(
1354 queryset=DeviceType.objects.all(),
1355 method='get_for_device_type'
1356 )
1357 config_template_id = django_filters.ModelMultipleChoiceFilter(
1358 queryset=ConfigTemplate.objects.all(),
1359 distinct=False,
1360 label=_('Config template (ID)'),
1361 )
1363 class Meta:
1364 model = Platform
1365 fields = ('id', 'name', 'slug', 'description')
1367 @extend_schema_field(OpenApiTypes.STR)
1368 def get_for_device_type(self, queryset, name, value):
1369 """
1370 Return all Platforms available for a specific manufacturer based on device type and Platforms not assigned any
1371 manufacturer
1372 """
1373 return queryset.filter(Q(manufacturer=None) | Q(manufacturer__device_types=value))
1376@register_filterset
1377class DeviceFilterSet(
1378 PrimaryModelFilterSet,
1379 TenancyFilterSet,
1380 ContactModelFilterSet,
1381 LocalConfigContextFilterSet,
1382 PrimaryIPFilterSet,
1383):
1384 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
1385 field_name='device_type__manufacturer',
1386 queryset=Manufacturer.objects.all(),
1387 distinct=False,
1388 label=_('Manufacturer (ID)'),
1389 )
1390 manufacturer = django_filters.ModelMultipleChoiceFilter(
1391 field_name='device_type__manufacturer__slug',
1392 queryset=Manufacturer.objects.all(),
1393 distinct=False,
1394 to_field_name='slug',
1395 label=_('Manufacturer (slug)'),
1396 )
1397 device_type = django_filters.ModelMultipleChoiceFilter(
1398 field_name='device_type__slug',
1399 queryset=DeviceType.objects.all(),
1400 distinct=False,
1401 to_field_name='slug',
1402 label=_('Device type (slug)'),
1403 )
1404 device_type_id = django_filters.ModelMultipleChoiceFilter(
1405 queryset=DeviceType.objects.all(),
1406 distinct=False,
1407 label=_('Device type (ID)'),
1408 )
1409 role_id = TreeNodeMultipleChoiceFilter(
1410 field_name='role',
1411 queryset=DeviceRole.objects.all(),
1412 lookup_expr='in',
1413 label=_('Role (ID)'),
1414 )
1415 role = TreeNodeMultipleChoiceFilter(
1416 queryset=DeviceRole.objects.all(),
1417 field_name='role',
1418 lookup_expr='in',
1419 to_field_name='slug',
1420 label=_('Role (slug)'),
1421 )
1422 parent_device_id = django_filters.ModelMultipleChoiceFilter(
1423 field_name='parent_bay__device',
1424 queryset=Device.objects.all(),
1425 label=_('Parent Device (ID)'),
1426 )
1427 platform_id = TreeNodeMultipleChoiceFilter(
1428 queryset=Platform.objects.all(),
1429 field_name='platform',
1430 lookup_expr='in',
1431 label=_('Platform (ID)'),
1432 )
1433 platform = TreeNodeMultipleChoiceFilter(
1434 field_name='platform',
1435 queryset=Platform.objects.all(),
1436 to_field_name='slug',
1437 lookup_expr='in',
1438 label=_('Platform (slug)'),
1439 )
1440 region_id = TreeNodeMultipleChoiceFilter(
1441 queryset=Region.objects.all(),
1442 field_name='site__region',
1443 lookup_expr='in',
1444 label=_('Region (ID)'),
1445 )
1446 region = TreeNodeMultipleChoiceFilter(
1447 queryset=Region.objects.all(),
1448 field_name='site__region',
1449 lookup_expr='in',
1450 to_field_name='slug',
1451 label=_('Region (slug)'),
1452 )
1453 site_group_id = TreeNodeMultipleChoiceFilter(
1454 queryset=SiteGroup.objects.all(),
1455 field_name='site__group',
1456 lookup_expr='in',
1457 label=_('Site group (ID)'),
1458 )
1459 site_group = TreeNodeMultipleChoiceFilter(
1460 queryset=SiteGroup.objects.all(),
1461 field_name='site__group',
1462 lookup_expr='in',
1463 to_field_name='slug',
1464 label=_('Site group (slug)'),
1465 )
1466 site_id = django_filters.ModelMultipleChoiceFilter(
1467 queryset=Site.objects.all(),
1468 distinct=False,
1469 label=_('Site (ID)'),
1470 )
1471 site = django_filters.ModelMultipleChoiceFilter(
1472 field_name='site__slug',
1473 queryset=Site.objects.all(),
1474 distinct=False,
1475 to_field_name='slug',
1476 label=_('Site name (slug)'),
1477 )
1478 location_id = TreeNodeMultipleChoiceFilter(
1479 queryset=Location.objects.all(),
1480 field_name='location',
1481 lookup_expr='in',
1482 label=_('Location (ID)'),
1483 )
1484 location = TreeNodeMultipleChoiceFilter(
1485 queryset=Location.objects.all(),
1486 field_name='location',
1487 lookup_expr='in',
1488 to_field_name='slug',
1489 label=_('Location (slug)'),
1490 )
1491 rack_id = django_filters.ModelMultipleChoiceFilter(
1492 field_name='rack',
1493 queryset=Rack.objects.all(),
1494 distinct=False,
1495 label=_('Rack (ID)'),
1496 )
1497 parent_bay_id = django_filters.ModelMultipleChoiceFilter(
1498 field_name='parent_bay',
1499 queryset=DeviceBay.objects.all(),
1500 label=_('Parent bay (ID)'),
1501 )
1502 cluster_id = django_filters.ModelMultipleChoiceFilter(
1503 queryset=Cluster.objects.all(),
1504 distinct=False,
1505 label=_('VM cluster (ID)'),
1506 )
1507 cluster_group = django_filters.ModelMultipleChoiceFilter(
1508 field_name='cluster__group__slug',
1509 queryset=ClusterGroup.objects.all(),
1510 distinct=False,
1511 to_field_name='slug',
1512 label=_('Cluster group (slug)'),
1513 )
1514 cluster_group_id = django_filters.ModelMultipleChoiceFilter(
1515 field_name='cluster__group',
1516 queryset=ClusterGroup.objects.all(),
1517 distinct=False,
1518 label=_('Cluster group (ID)'),
1519 )
1520 model = django_filters.ModelMultipleChoiceFilter(
1521 field_name='device_type__slug',
1522 queryset=DeviceType.objects.all(),
1523 distinct=False,
1524 to_field_name='slug',
1525 label=_('Device model (slug)'),
1526 )
1527 name = MultiValueCharFilter(
1528 lookup_expr='iexact'
1529 )
1530 status = django_filters.MultipleChoiceFilter(
1531 choices=DeviceStatusChoices,
1532 distinct=False,
1533 null_value=None
1534 )
1535 is_full_depth = django_filters.BooleanFilter(
1536 field_name='device_type__is_full_depth',
1537 label=_('Is full depth'),
1538 )
1539 mac_address = MultiValueMACAddressFilter(
1540 field_name='interfaces__mac_addresses__mac_address',
1541 label=_('MAC address'),
1542 )
1543 serial = MultiValueCharFilter(
1544 lookup_expr='iexact'
1545 )
1546 has_primary_ip = django_filters.BooleanFilter(
1547 method='_has_primary_ip',
1548 label=_('Has a primary IP'),
1549 )
1550 has_oob_ip = django_filters.BooleanFilter(
1551 method='_has_oob_ip',
1552 label=_('Has an out-of-band IP'),
1553 )
1554 virtual_chassis_id = django_filters.ModelMultipleChoiceFilter(
1555 field_name='virtual_chassis',
1556 queryset=VirtualChassis.objects.all(),
1557 distinct=False,
1558 label=_('Virtual chassis (ID)'),
1559 )
1560 virtual_chassis_member = django_filters.BooleanFilter(
1561 method='_virtual_chassis_member',
1562 label=_('Is a virtual chassis member')
1563 )
1564 config_template_id = django_filters.ModelMultipleChoiceFilter(
1565 queryset=ConfigTemplate.objects.all(),
1566 distinct=False,
1567 label=_('Config template (ID)'),
1568 )
1569 console_ports = django_filters.BooleanFilter(
1570 method='_console_ports',
1571 label=_('Has console ports'),
1572 )
1573 console_server_ports = django_filters.BooleanFilter(
1574 method='_console_server_ports',
1575 label=_('Has console server ports'),
1576 )
1577 power_ports = django_filters.BooleanFilter(
1578 method='_power_ports',
1579 label=_('Has power ports'),
1580 )
1581 power_outlets = django_filters.BooleanFilter(
1582 method='_power_outlets',
1583 label=_('Has power outlets'),
1584 )
1585 interfaces = django_filters.BooleanFilter(
1586 method='_interfaces',
1587 label=_('Has interfaces'),
1588 )
1589 pass_through_ports = django_filters.BooleanFilter(
1590 method='_pass_through_ports',
1591 label=_('Has pass-through ports'),
1592 )
1593 module_bays = django_filters.BooleanFilter(
1594 method='_module_bays',
1595 label=_('Has module bays'),
1596 )
1597 device_bays = django_filters.BooleanFilter(
1598 method='_device_bays',
1599 label=_('Has device bays'),
1600 )
1601 oob_ip_id = django_filters.ModelMultipleChoiceFilter(
1602 field_name='oob_ip',
1603 queryset=IPAddress.objects.all(),
1604 distinct=False,
1605 label=_('OOB IP (ID)'),
1606 )
1607 has_virtual_device_context = django_filters.BooleanFilter(
1608 method='_has_virtual_device_context',
1609 label=_('Has virtual device context'),
1610 )
1612 class Meta:
1613 model = Device
1614 fields = (
1615 'id', 'asset_tag', 'face', 'position', 'latitude', 'longitude', 'airflow', 'cooling_method', 'vc_position',
1616 'vc_priority', 'description',
1618 # Counters
1619 'console_port_count',
1620 'console_server_port_count',
1621 'power_port_count',
1622 'power_outlet_count',
1623 'cooling_intake_count',
1624 'cooling_outflow_count',
1625 'interface_count',
1626 'front_port_count',
1627 'rear_port_count',
1628 'device_bay_count',
1629 'module_bay_count',
1630 'inventory_item_count',
1631 )
1633 def search(self, queryset, name, value):
1634 if not value.strip():
1635 return queryset
1636 qs_filter = (
1637 Q(name__icontains=value) |
1638 Q(virtual_chassis__name__icontains=value) |
1639 Q(serial__icontains=value.strip()) |
1640 Q(asset_tag__icontains=value.strip()) |
1641 Q(description__icontains=value.strip()) |
1642 Q(comments__icontains=value)
1643 )
1644 # If the given value looks like an IP address, look for primary IPv4/IPv6 assignments
1645 try:
1646 ipaddress = netaddr.IPNetwork(value)
1647 if ipaddress.version == 4:
1648 qs_filter |= Q(primary_ip4__address__host__inet=ipaddress.ip)
1649 elif ipaddress.version == 6:
1650 qs_filter |= Q(primary_ip6__address__host__inet=ipaddress.ip)
1651 except (AddrFormatError, ValueError):
1652 pass
1653 return queryset.filter(qs_filter)
1655 def _has_primary_ip(self, queryset, name, value):
1656 params = Q(primary_ip4__isnull=False) | Q(primary_ip6__isnull=False)
1657 if value:
1658 return queryset.filter(params)
1659 return queryset.exclude(params)
1661 def _has_oob_ip(self, queryset, name, value):
1662 params = Q(oob_ip__isnull=False)
1663 if value:
1664 return queryset.filter(params)
1665 return queryset.exclude(params)
1667 def _virtual_chassis_member(self, queryset, name, value):
1668 return queryset.exclude(virtual_chassis__isnull=value)
1670 def _console_ports(self, queryset, name, value):
1671 return queryset.exclude(consoleports__isnull=value)
1673 def _console_server_ports(self, queryset, name, value):
1674 return queryset.exclude(consoleserverports__isnull=value)
1676 def _power_ports(self, queryset, name, value):
1677 return queryset.exclude(powerports__isnull=value)
1679 def _power_outlets(self, queryset, name, value):
1680 return queryset.exclude(poweroutlets__isnull=value)
1682 def _interfaces(self, queryset, name, value):
1683 return queryset.exclude(interfaces__isnull=value)
1685 def _pass_through_ports(self, queryset, name, value):
1686 return queryset.exclude(
1687 frontports__isnull=value,
1688 rearports__isnull=value
1689 )
1691 def _module_bays(self, queryset, name, value):
1692 return queryset.exclude(modulebays__isnull=value)
1694 def _device_bays(self, queryset, name, value):
1695 return queryset.exclude(devicebays__isnull=value)
1697 def _has_virtual_device_context(self, queryset, name, value):
1698 params = Q(vdcs__isnull=False)
1699 if value:
1700 return queryset.filter(params).distinct()
1701 return queryset.exclude(params)
1704@register_filterset
1705class VirtualDeviceContextFilterSet(PrimaryModelFilterSet, TenancyFilterSet, PrimaryIPFilterSet):
1706 device_id = django_filters.ModelMultipleChoiceFilter(
1707 field_name='device',
1708 queryset=Device.objects.all(),
1709 distinct=False,
1710 label=_('VDC (ID)')
1711 )
1712 device = django_filters.ModelMultipleChoiceFilter(
1713 field_name='device',
1714 queryset=Device.objects.all(),
1715 distinct=False,
1716 label=_('Device model')
1717 )
1718 interface_id = django_filters.ModelMultipleChoiceFilter(
1719 field_name='interfaces',
1720 queryset=Interface.objects.all(),
1721 label=_('Interface (ID)')
1722 )
1723 status = django_filters.MultipleChoiceFilter(
1724 choices=VirtualDeviceContextStatusChoices,
1725 distinct=False,
1726 )
1727 has_primary_ip = django_filters.BooleanFilter(
1728 method='_has_primary_ip',
1729 label=_('Has a primary IP')
1730 )
1732 class Meta:
1733 model = VirtualDeviceContext
1734 fields = ('id', 'device', 'name', 'identifier', 'description')
1736 def search(self, queryset, name, value):
1737 if not value.strip():
1738 return queryset
1740 qs_filter = (
1741 Q(name__icontains=value) |
1742 Q(description__icontains=value)
1743 )
1744 try:
1745 qs_filter |= Q(identifier=int(value))
1746 except ValueError:
1747 pass
1748 return queryset.filter(qs_filter).distinct()
1750 def _has_primary_ip(self, queryset, name, value):
1751 params = Q(primary_ip4__isnull=False) | Q(primary_ip6__isnull=False)
1752 if value:
1753 return queryset.filter(params)
1754 return queryset.exclude(params)
1757@register_filterset
1758class ModuleFilterSet(PrimaryModelFilterSet):
1759 profile_id = django_filters.ModelMultipleChoiceFilter(
1760 field_name='module_type__profile',
1761 queryset=ModuleTypeProfile.objects.all(),
1762 distinct=False,
1763 label=_('Profile (ID)'),
1764 )
1765 profile = django_filters.ModelMultipleChoiceFilter(
1766 field_name='module_type__profile__name',
1767 queryset=ModuleTypeProfile.objects.all(),
1768 distinct=False,
1769 to_field_name='name',
1770 label=_('Profile (name)'),
1771 )
1772 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
1773 field_name='module_type__manufacturer',
1774 queryset=Manufacturer.objects.all(),
1775 distinct=False,
1776 label=_('Manufacturer (ID)'),
1777 )
1778 manufacturer = django_filters.ModelMultipleChoiceFilter(
1779 field_name='module_type__manufacturer__slug',
1780 queryset=Manufacturer.objects.all(),
1781 distinct=False,
1782 to_field_name='slug',
1783 label=_('Manufacturer (slug)'),
1784 )
1785 module_type_id = django_filters.ModelMultipleChoiceFilter(
1786 field_name='module_type',
1787 queryset=ModuleType.objects.all(),
1788 distinct=False,
1789 label=_('Module type (ID)'),
1790 )
1791 module_type = django_filters.ModelMultipleChoiceFilter(
1792 field_name='module_type__model',
1793 queryset=ModuleType.objects.all(),
1794 distinct=False,
1795 to_field_name='model',
1796 label=_('Module type (model)'),
1797 )
1798 module_bay_id = TreeNodeMultipleChoiceFilter(
1799 queryset=ModuleBay.objects.all(),
1800 field_name='module_bay',
1801 lookup_expr='in',
1802 label=_('Module bay (ID)'),
1803 )
1804 region_id = TreeNodeMultipleChoiceFilter(
1805 queryset=Region.objects.all(),
1806 field_name='device__site__region',
1807 lookup_expr='in',
1808 label=_('Region (ID)'),
1809 )
1810 region = TreeNodeMultipleChoiceFilter(
1811 queryset=Region.objects.all(),
1812 field_name='device__site__region',
1813 lookup_expr='in',
1814 to_field_name='slug',
1815 label=_('Region (slug)'),
1816 )
1817 site_group_id = TreeNodeMultipleChoiceFilter(
1818 queryset=SiteGroup.objects.all(),
1819 field_name='device__site__group',
1820 lookup_expr='in',
1821 label=_('Site group (ID)'),
1822 )
1823 site_group = TreeNodeMultipleChoiceFilter(
1824 queryset=SiteGroup.objects.all(),
1825 field_name='device__site__group',
1826 lookup_expr='in',
1827 to_field_name='slug',
1828 label=_('Site group (slug)'),
1829 )
1830 site_id = django_filters.ModelMultipleChoiceFilter(
1831 field_name='device__site',
1832 queryset=Site.objects.all(),
1833 distinct=False,
1834 label=_('Site (ID)'),
1835 )
1836 site = django_filters.ModelMultipleChoiceFilter(
1837 field_name='device__site__slug',
1838 queryset=Site.objects.all(),
1839 distinct=False,
1840 to_field_name='slug',
1841 label=_('Site name (slug)'),
1842 )
1843 location_id = django_filters.ModelMultipleChoiceFilter(
1844 field_name='device__location',
1845 queryset=Location.objects.all(),
1846 distinct=False,
1847 label=_('Location (ID)'),
1848 )
1849 location = django_filters.ModelMultipleChoiceFilter(
1850 field_name='device__location__slug',
1851 queryset=Location.objects.all(),
1852 distinct=False,
1853 to_field_name='slug',
1854 label=_('Location (slug)'),
1855 )
1856 rack_id = django_filters.ModelMultipleChoiceFilter(
1857 field_name='device__rack',
1858 queryset=Rack.objects.all(),
1859 distinct=False,
1860 label=_('Rack (ID)'),
1861 )
1862 rack = django_filters.ModelMultipleChoiceFilter(
1863 field_name='device__rack__name',
1864 queryset=Rack.objects.all(),
1865 distinct=False,
1866 to_field_name='name',
1867 label=_('Rack (name)'),
1868 )
1869 device_id = django_filters.ModelMultipleChoiceFilter(
1870 queryset=Device.objects.all(),
1871 distinct=False,
1872 label=_('Device (ID)'),
1873 )
1874 device = django_filters.ModelMultipleChoiceFilter(
1875 field_name='device__name',
1876 queryset=Device.objects.all(),
1877 distinct=False,
1878 to_field_name='name',
1879 label=_('Device (name)'),
1880 )
1881 status = django_filters.MultipleChoiceFilter(
1882 choices=ModuleStatusChoices,
1883 distinct=False,
1884 null_value=None
1885 )
1886 serial = MultiValueCharFilter(
1887 lookup_expr='iexact'
1888 )
1890 class Meta:
1891 model = Module
1892 fields = ('id', 'status', 'asset_tag', 'description')
1894 def search(self, queryset, name, value):
1895 if not value.strip():
1896 return queryset
1897 return queryset.filter(
1898 Q(device__name__icontains=value.strip()) |
1899 Q(serial__icontains=value.strip()) |
1900 Q(asset_tag__icontains=value.strip()) |
1901 Q(description__icontains=value) |
1902 Q(comments__icontains=value)
1903 ).distinct()
1906class DeviceComponentFilterSet(OwnerFilterMixin, NetBoxModelFilterSet):
1907 q = django_filters.CharFilter(
1908 method='search',
1909 label=_('Search'),
1910 )
1911 region_id = TreeNodeMultipleChoiceFilter(
1912 queryset=Region.objects.all(),
1913 field_name='device__site__region',
1914 lookup_expr='in',
1915 label=_('Region (ID)'),
1916 )
1917 region = TreeNodeMultipleChoiceFilter(
1918 queryset=Region.objects.all(),
1919 field_name='device__site__region',
1920 lookup_expr='in',
1921 to_field_name='slug',
1922 label=_('Region (slug)'),
1923 )
1924 site_group_id = TreeNodeMultipleChoiceFilter(
1925 queryset=SiteGroup.objects.all(),
1926 field_name='device__site__group',
1927 lookup_expr='in',
1928 label=_('Site group (ID)'),
1929 )
1930 site_group = TreeNodeMultipleChoiceFilter(
1931 queryset=SiteGroup.objects.all(),
1932 field_name='device__site__group',
1933 lookup_expr='in',
1934 to_field_name='slug',
1935 label=_('Site group (slug)'),
1936 )
1937 site_id = django_filters.ModelMultipleChoiceFilter(
1938 field_name='_site',
1939 queryset=Site.objects.all(),
1940 distinct=False,
1941 label=_('Site (ID)'),
1942 )
1943 site = django_filters.ModelMultipleChoiceFilter(
1944 field_name='_site__slug',
1945 queryset=Site.objects.all(),
1946 distinct=False,
1947 to_field_name='slug',
1948 label=_('Site name (slug)'),
1949 )
1950 location_id = django_filters.ModelMultipleChoiceFilter(
1951 field_name='_location',
1952 queryset=Location.objects.all(),
1953 distinct=False,
1954 label=_('Location (ID)'),
1955 )
1956 location = django_filters.ModelMultipleChoiceFilter(
1957 field_name='_location__slug',
1958 queryset=Location.objects.all(),
1959 distinct=False,
1960 to_field_name='slug',
1961 label=_('Location (slug)'),
1962 )
1963 rack_id = django_filters.ModelMultipleChoiceFilter(
1964 field_name='_rack',
1965 queryset=Rack.objects.all(),
1966 distinct=False,
1967 label=_('Rack (ID)'),
1968 )
1969 rack = django_filters.ModelMultipleChoiceFilter(
1970 field_name='_rack__name',
1971 queryset=Rack.objects.all(),
1972 distinct=False,
1973 to_field_name='name',
1974 label=_('Rack (name)'),
1975 )
1976 device_id = django_filters.ModelMultipleChoiceFilter(
1977 queryset=Device.objects.all(),
1978 distinct=False,
1979 label=_('Device (ID)'),
1980 )
1981 device = django_filters.ModelMultipleChoiceFilter(
1982 field_name='device__name',
1983 queryset=Device.objects.all(),
1984 distinct=False,
1985 to_field_name='name',
1986 label=_('Device (name)'),
1987 )
1988 device_type_id = django_filters.ModelMultipleChoiceFilter(
1989 field_name='device__device_type',
1990 queryset=DeviceType.objects.all(),
1991 distinct=False,
1992 label=_('Device type (ID)'),
1993 )
1994 device_type = django_filters.ModelMultipleChoiceFilter(
1995 field_name='device__device_type__model',
1996 queryset=DeviceType.objects.all(),
1997 distinct=False,
1998 to_field_name='model',
1999 label=_('Device type (model)'),
2000 )
2001 device_role_id = django_filters.ModelMultipleChoiceFilter(
2002 field_name='device__role',
2003 queryset=DeviceRole.objects.all(),
2004 distinct=False,
2005 label=_('Device role (ID)'),
2006 )
2007 device_role = django_filters.ModelMultipleChoiceFilter(
2008 field_name='device__role__slug',
2009 queryset=DeviceRole.objects.all(),
2010 distinct=False,
2011 to_field_name='slug',
2012 label=_('Device role (slug)'),
2013 )
2014 virtual_chassis_id = django_filters.ModelMultipleChoiceFilter(
2015 field_name='device__virtual_chassis',
2016 queryset=VirtualChassis.objects.all(),
2017 distinct=False,
2018 label=_('Virtual Chassis (ID)')
2019 )
2020 virtual_chassis = django_filters.ModelMultipleChoiceFilter(
2021 field_name='device__virtual_chassis__name',
2022 queryset=VirtualChassis.objects.all(),
2023 distinct=False,
2024 to_field_name='name',
2025 label=_('Virtual Chassis'),
2026 )
2027 device_status = django_filters.MultipleChoiceFilter(
2028 choices=DeviceStatusChoices,
2029 distinct=False,
2030 field_name='device__status',
2031 )
2032 tenant_id = django_filters.ModelMultipleChoiceFilter(
2033 field_name='device__tenant',
2034 queryset=Tenant.objects.all(),
2035 distinct=False,
2036 label=_('Tenant (ID)'),
2037 )
2038 tenant = django_filters.ModelMultipleChoiceFilter(
2039 field_name='device__tenant__slug',
2040 queryset=Tenant.objects.all(),
2041 distinct=False,
2042 to_field_name='slug',
2043 label=_('Tenant (slug)'),
2044 )
2046 def search(self, queryset, name, value):
2047 if not value.strip():
2048 return queryset
2049 return queryset.filter(
2050 Q(name__icontains=value) |
2051 Q(label__icontains=value) |
2052 Q(description__icontains=value)
2053 )
2056class ModularDeviceComponentFilterSet(DeviceComponentFilterSet):
2057 """
2058 Extends DeviceComponentFilterSet to add a module_id filter for components
2059 which can be associated with a particular module within a device.
2060 """
2061 module_id = django_filters.ModelMultipleChoiceFilter(
2062 queryset=Module.objects.all(),
2063 distinct=False,
2064 label=_('Module (ID)'),
2065 )
2068class CabledObjectFilterSet(django_filters.FilterSet):
2069 cable_id = django_filters.ModelMultipleChoiceFilter(
2070 queryset=Cable.objects.all(),
2071 distinct=False,
2072 label=_('Cable (ID)'),
2073 )
2074 cabled = django_filters.BooleanFilter(
2075 field_name='cable',
2076 lookup_expr='isnull',
2077 exclude=True
2078 )
2079 occupied = django_filters.BooleanFilter(
2080 method='filter_occupied'
2081 )
2083 def filter_occupied(self, queryset, name, value):
2084 if value: 2084 ↛ 2086line 2084 didn't jump to line 2086 because the condition on line 2084 was always true
2085 return queryset.filter(Q(cable__isnull=False) | Q(mark_connected=True))
2086 return queryset.filter(cable__isnull=True, mark_connected=False)
2089class PathEndpointFilterSet(django_filters.FilterSet):
2090 connected = django_filters.BooleanFilter(
2091 method='filter_connected'
2092 )
2094 def filter_connected(self, queryset, name, value):
2095 if value:
2096 return queryset.filter(_path__is_active=True, _path__is_complete=True)
2097 return queryset.filter(Q(_path__isnull=True) | Q(_path__is_active=False) | Q(_path__is_complete=False))
2100@register_filterset
2101class ConsolePortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
2102 type = django_filters.MultipleChoiceFilter(
2103 choices=ConsolePortTypeChoices,
2104 distinct=False,
2105 null_value=None
2106 )
2108 class Meta:
2109 model = ConsolePort
2110 fields = (
2111 'id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end', 'cable_connector',
2112 )
2115@register_filterset
2116class ConsoleServerPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
2117 type = django_filters.MultipleChoiceFilter(
2118 choices=ConsolePortTypeChoices,
2119 distinct=False,
2120 null_value=None
2121 )
2123 class Meta:
2124 model = ConsoleServerPort
2125 fields = (
2126 'id', 'name', 'label', 'speed', 'description', 'mark_connected', 'cable_end', 'cable_connector',
2127 )
2130@register_filterset
2131class PowerPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
2132 type = django_filters.MultipleChoiceFilter(
2133 choices=PowerPortTypeChoices,
2134 distinct=False,
2135 null_value=None
2136 )
2138 class Meta:
2139 model = PowerPort
2140 fields = (
2141 'id', 'name', 'label', 'maximum_draw', 'allocated_draw', 'description', 'mark_connected', 'cable_end',
2142 'cable_connector',
2143 )
2146@register_filterset
2147class PowerOutletFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet, PathEndpointFilterSet):
2148 type = django_filters.MultipleChoiceFilter(
2149 choices=PowerOutletTypeChoices,
2150 distinct=False,
2151 null_value=None
2152 )
2153 feed_leg = django_filters.MultipleChoiceFilter(
2154 choices=PowerOutletFeedLegChoices,
2155 distinct=False,
2156 null_value=None
2157 )
2158 power_port_id = django_filters.ModelMultipleChoiceFilter(
2159 queryset=PowerPort.objects.all(),
2160 distinct=False,
2161 label=_('Power port (ID)'),
2162 )
2163 status = django_filters.MultipleChoiceFilter(
2164 choices=PowerOutletStatusChoices,
2165 distinct=False,
2166 null_value=None
2167 )
2169 class Meta:
2170 model = PowerOutlet
2171 fields = (
2172 'id', 'name', 'status', 'label', 'feed_leg', 'description', 'color', 'mark_connected', 'cable_end',
2173 'cable_connector',
2174 )
2177@register_filterset
2178class CoolingIntakeFilterSet(ModularDeviceComponentFilterSet):
2179 type = django_filters.MultipleChoiceFilter(
2180 choices=CoolingConnectorTypeChoices,
2181 distinct=False,
2182 null_value=None
2183 )
2184 diameter_unit = django_filters.MultipleChoiceFilter(
2185 choices=DiameterUnitChoices,
2186 distinct=False,
2187 null_value=None
2188 )
2189 max_flow_unit = django_filters.MultipleChoiceFilter(
2190 choices=FlowRateUnitChoices,
2191 distinct=False,
2192 null_value=None
2193 )
2194 cooling_outflow_id = django_filters.ModelMultipleChoiceFilter(
2195 queryset=CoolingOutflow.objects.all(),
2196 field_name='cooling_outflow',
2197 distinct=False,
2198 label=_('Cooling outflow (ID)'),
2199 )
2201 class Meta:
2202 model = CoolingIntake
2203 fields = (
2204 'id', 'name', 'label', 'diameter', 'max_flow', 'description',
2205 )
2208@register_filterset
2209class CoolingOutflowFilterSet(ModularDeviceComponentFilterSet):
2210 type = django_filters.MultipleChoiceFilter(
2211 choices=CoolingConnectorTypeChoices,
2212 distinct=False,
2213 null_value=None
2214 )
2215 diameter_unit = django_filters.MultipleChoiceFilter(
2216 choices=DiameterUnitChoices,
2217 distinct=False,
2218 null_value=None
2219 )
2220 cooling_intake_id = django_filters.ModelMultipleChoiceFilter(
2221 field_name='cooling_intake',
2222 queryset=CoolingIntake.objects.all(),
2223 distinct=False,
2224 label=_('Cooling intake (ID)'),
2225 )
2227 class Meta:
2228 model = CoolingOutflow
2229 fields = (
2230 'id', 'name', 'label', 'diameter', 'description',
2231 )
2234@register_filterset
2235class MACAddressFilterSet(PrimaryModelFilterSet):
2236 mac_address = MultiValueMACAddressFilter()
2237 assigned_object_type = MultiValueContentTypeFilter()
2238 device = MultiValueCharFilter(
2239 method='filter_device',
2240 field_name='name',
2241 label=_('Device (name)'),
2242 )
2243 device_id = MultiValueNumberFilter(
2244 method='filter_device',
2245 field_name='pk',
2246 label=_('Device (ID)'),
2247 )
2248 virtual_machine = MultiValueCharFilter(
2249 method='filter_virtual_machine',
2250 field_name='name',
2251 label=_('Virtual machine (name)'),
2252 )
2253 virtual_machine_id = MultiValueNumberFilter(
2254 method='filter_virtual_machine',
2255 field_name='pk',
2256 label=_('Virtual machine (ID)'),
2257 )
2258 interface = django_filters.ModelMultipleChoiceFilter(
2259 field_name='interface__name',
2260 queryset=Interface.objects.all(),
2261 to_field_name='name',
2262 label=_('Interface (name)'),
2263 )
2264 interface_id = django_filters.ModelMultipleChoiceFilter(
2265 field_name='interface',
2266 queryset=Interface.objects.all(),
2267 label=_('Interface (ID)'),
2268 )
2269 vminterface = django_filters.ModelMultipleChoiceFilter(
2270 field_name='vminterface__name',
2271 queryset=VMInterface.objects.all(),
2272 to_field_name='name',
2273 label=_('VM interface (name)'),
2274 )
2275 vminterface_id = django_filters.ModelMultipleChoiceFilter(
2276 field_name='vminterface',
2277 queryset=VMInterface.objects.all(),
2278 label=_('VM interface (ID)'),
2279 )
2280 assigned = django_filters.BooleanFilter(
2281 method='filter_assigned',
2282 label=_('Is assigned'),
2283 )
2284 primary = django_filters.BooleanFilter(
2285 method='filter_primary',
2286 label=_('Is primary'),
2287 )
2289 class Meta:
2290 model = MACAddress
2291 fields = ('id', 'description', 'assigned_object_type', 'assigned_object_id')
2293 def search(self, queryset, name, value):
2294 if not value.strip():
2295 return queryset
2296 qs_filter = (
2297 Q(mac_address__icontains=value) |
2298 Q(description__icontains=value)
2299 )
2300 return queryset.filter(qs_filter)
2302 def filter_device(self, queryset, name, value):
2303 devices = Device.objects.filter(**{f'{name}__in': value})
2304 if not devices.exists():
2305 return queryset.none()
2306 interface_ids = []
2307 for device in devices:
2308 interface_ids.extend(device.vc_interfaces().values_list('id', flat=True))
2309 return queryset.filter(
2310 interface__in=interface_ids
2311 )
2313 def filter_virtual_machine(self, queryset, name, value):
2314 virtual_machines = VirtualMachine.objects.filter(**{f'{name}__in': value})
2315 if not virtual_machines.exists():
2316 return queryset.none()
2317 interface_ids = []
2318 for vm in virtual_machines:
2319 interface_ids.extend(vm.interfaces.values_list('id', flat=True))
2320 return queryset.filter(
2321 vminterface__in=interface_ids
2322 )
2324 def filter_assigned(self, queryset, name, value):
2325 params = {
2326 'assigned_object_type__isnull': True,
2327 'assigned_object_id__isnull': True,
2328 }
2329 if value: 2329 ↛ 2330line 2329 didn't jump to line 2330 because the condition on line 2329 was never true
2330 return queryset.exclude(**params)
2331 return queryset.filter(**params)
2333 def filter_primary(self, queryset, name, value):
2334 interface_mac_ids = Interface.objects.filter(primary_mac_address_id__isnull=False).values_list(
2335 'primary_mac_address_id', flat=True
2336 )
2337 vminterface_mac_ids = VMInterface.objects.filter(primary_mac_address_id__isnull=False).values_list(
2338 'primary_mac_address_id', flat=True
2339 )
2340 query = Q(pk__in=interface_mac_ids) | Q(pk__in=vminterface_mac_ids)
2341 if value:
2342 return queryset.filter(query)
2343 return queryset.exclude(query)
2346class CommonInterfaceFilterSet(django_filters.FilterSet):
2347 mode = django_filters.MultipleChoiceFilter(
2348 choices=InterfaceModeChoices,
2349 distinct=False,
2350 label=_('802.1Q Mode')
2351 )
2352 vlan_id = django_filters.CharFilter(
2353 method='filter_vlan_id',
2354 label=_('Assigned VLAN')
2355 )
2356 vlan = django_filters.CharFilter(
2357 method='filter_vlan',
2358 label=_('Assigned VID')
2359 )
2360 vrf_id = django_filters.ModelMultipleChoiceFilter(
2361 field_name='vrf',
2362 queryset=VRF.objects.all(),
2363 distinct=False,
2364 label=_('VRF'),
2365 )
2366 vrf = django_filters.ModelMultipleChoiceFilter(
2367 field_name='vrf__rd',
2368 queryset=VRF.objects.all(),
2369 distinct=False,
2370 to_field_name='rd',
2371 label=_('VRF (RD)'),
2372 )
2373 l2vpn_id = django_filters.ModelMultipleChoiceFilter(
2374 field_name='l2vpn_terminations__l2vpn',
2375 queryset=L2VPN.objects.all(),
2376 label=_('L2VPN (ID)'),
2377 )
2378 l2vpn = django_filters.ModelMultipleChoiceFilter(
2379 field_name='l2vpn_terminations__l2vpn__identifier',
2380 queryset=L2VPN.objects.all(),
2381 to_field_name='identifier',
2382 label=_('L2VPN'),
2383 )
2384 vlan_translation_policy_id = django_filters.ModelMultipleChoiceFilter(
2385 field_name='vlan_translation_policy',
2386 queryset=VLANTranslationPolicy.objects.all(),
2387 distinct=False,
2388 label=_('VLAN Translation Policy (ID)'),
2389 )
2390 vlan_translation_policy = django_filters.ModelMultipleChoiceFilter(
2391 field_name='vlan_translation_policy__name',
2392 queryset=VLANTranslationPolicy.objects.all(),
2393 distinct=False,
2394 to_field_name='name',
2395 label=_('VLAN Translation Policy'),
2396 )
2398 def filter_vlan_id(self, queryset, name, value):
2399 value = value.strip()
2400 if not value:
2401 return queryset
2402 return queryset.filter(
2403 Q(untagged_vlan_id=value) |
2404 Q(tagged_vlans=value) |
2405 Q(qinq_svlan=value)
2406 )
2408 def filter_vlan(self, queryset, name, value):
2409 value = value.strip()
2410 if not value:
2411 return queryset
2412 return queryset.filter(
2413 Q(untagged_vlan_id__vid=value) |
2414 Q(tagged_vlans__vid=value) |
2415 Q(qinq_svlan__vid=value)
2416 )
2419@register_filterset
2420class InterfaceFilterSet(
2421 ModularDeviceComponentFilterSet,
2422 CabledObjectFilterSet,
2423 PathEndpointFilterSet,
2424 CommonInterfaceFilterSet
2425):
2426 virtual_chassis_member_or_master = MultiValueCharFilter(
2427 method='filter_virtual_chassis_member_or_master',
2428 field_name='name',
2429 label=_('Virtual Chassis Interfaces for Device when device is master')
2430 )
2431 virtual_chassis_member_or_master_id = MultiValueNumberFilter(
2432 method='filter_virtual_chassis_member_or_master',
2433 field_name='pk',
2434 label=_('Virtual Chassis Interfaces for Device when device is master (ID)')
2435 )
2436 virtual_chassis_member = MultiValueCharFilter(
2437 method='filter_virtual_chassis_member',
2438 field_name='name',
2439 label=_('Virtual Chassis Interfaces for Device')
2440 )
2441 virtual_chassis_member_id = MultiValueNumberFilter(
2442 method='filter_virtual_chassis_member',
2443 field_name='pk',
2444 label=_('Virtual Chassis Interfaces for Device (ID)')
2445 )
2446 kind = django_filters.CharFilter(
2447 method='filter_kind',
2448 label=_('Kind of interface'),
2449 )
2450 parent_id = django_filters.ModelMultipleChoiceFilter(
2451 field_name='parent',
2452 queryset=Interface.objects.all(),
2453 distinct=False,
2454 label=_('Parent interface (ID)'),
2455 )
2456 bridge_id = django_filters.ModelMultipleChoiceFilter(
2457 field_name='bridge',
2458 queryset=Interface.objects.all(),
2459 distinct=False,
2460 label=_('Bridged interface (ID)'),
2461 )
2462 lag_id = django_filters.ModelMultipleChoiceFilter(
2463 field_name='lag',
2464 queryset=Interface.objects.all(),
2465 distinct=False,
2466 label=_('LAG interface (ID)'),
2467 )
2468 speed = MultiValueBigNumberFilter(min_value=0)
2469 duplex = django_filters.MultipleChoiceFilter(
2470 choices=InterfaceDuplexChoices,
2471 distinct=False,
2472 )
2473 mac_address = MultiValueMACAddressFilter(
2474 field_name='mac_addresses__mac_address',
2475 label=_('MAC Address')
2476 )
2477 primary_mac_address_id = django_filters.ModelMultipleChoiceFilter(
2478 field_name='primary_mac_address',
2479 queryset=MACAddress.objects.all(),
2480 distinct=False,
2481 label=_('Primary MAC address (ID)'),
2482 )
2483 primary_mac_address = django_filters.ModelMultipleChoiceFilter(
2484 field_name='primary_mac_address__mac_address',
2485 queryset=MACAddress.objects.all(),
2486 distinct=False,
2487 to_field_name='mac_address',
2488 label=_('Primary MAC address'),
2489 )
2490 wwn = MultiValueWWNFilter()
2491 poe_mode = django_filters.MultipleChoiceFilter(
2492 choices=InterfacePoEModeChoices,
2493 distinct=False,
2494 )
2495 poe_type = django_filters.MultipleChoiceFilter(
2496 choices=InterfacePoETypeChoices,
2497 distinct=False,
2498 )
2499 type = django_filters.MultipleChoiceFilter(
2500 choices=InterfaceTypeChoices,
2501 distinct=False,
2502 null_value=None
2503 )
2504 rf_role = django_filters.MultipleChoiceFilter(
2505 choices=WirelessRoleChoices,
2506 distinct=False,
2507 )
2508 rf_channel = django_filters.MultipleChoiceFilter(
2509 choices=WirelessChannelChoices,
2510 distinct=False,
2511 )
2512 vdc_id = django_filters.ModelMultipleChoiceFilter(
2513 field_name='vdcs',
2514 queryset=VirtualDeviceContext.objects.all(),
2515 label=_('Virtual Device Context')
2516 )
2517 vdc_identifier = django_filters.ModelMultipleChoiceFilter(
2518 field_name='vdcs__identifier',
2519 queryset=VirtualDeviceContext.objects.all(),
2520 to_field_name='identifier',
2521 label=_('Virtual Device Context (Identifier)')
2522 )
2523 vdc = django_filters.ModelMultipleChoiceFilter(
2524 field_name='vdcs__name',
2525 queryset=VirtualDeviceContext.objects.all(),
2526 to_field_name='name',
2527 label=_('Virtual Device Context')
2528 )
2529 wireless_lan_id = django_filters.ModelMultipleChoiceFilter(
2530 field_name='wireless_lans',
2531 queryset=WirelessLAN.objects.all(),
2532 label=_('Wireless LAN')
2533 )
2534 wireless_link_id = django_filters.ModelMultipleChoiceFilter(
2535 queryset=WirelessLink.objects.all(),
2536 distinct=False,
2537 label=_('Wireless link')
2538 )
2539 virtual_circuit_id = django_filters.ModelMultipleChoiceFilter(
2540 field_name='virtual_circuit_termination__virtual_circuit',
2541 queryset=VirtualCircuit.objects.all(),
2542 label=_('Virtual circuit (ID)'),
2543 )
2544 virtual_circuit_termination_id = django_filters.ModelMultipleChoiceFilter(
2545 field_name='virtual_circuit_termination',
2546 queryset=VirtualCircuitTermination.objects.all(),
2547 label=_('Virtual circuit termination (ID)'),
2548 )
2550 class Meta:
2551 model = Interface
2552 fields = (
2553 'id', 'name', 'label', 'type', 'channels', 'channel_id', 'enabled', 'mtu', 'mgmt_only', 'poe_mode',
2554 'poe_type', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
2555 'description', 'mark_connected', 'cable_id', 'cable_end', 'cable_connector',
2556 )
2558 def filter_virtual_chassis_member_or_master(self, queryset, name, value):
2559 return self.filter_virtual_chassis_member(queryset, name, value, if_master=True)
2561 def filter_virtual_chassis_member(self, queryset, name, value, if_master=False):
2562 try:
2563 vc_interface_ids = []
2564 for device in Device.objects.filter(**{f'{name}__in': value}):
2565 vc_interface_ids.extend(device.vc_interfaces(if_master=if_master).values_list('id', flat=True))
2566 return queryset.filter(pk__in=vc_interface_ids)
2567 except Device.DoesNotExist:
2568 return queryset.none()
2570 def filter_kind(self, queryset, name, value):
2571 value = value.strip().lower()
2572 return {
2573 # A channel subinterface is excluded even if its type is otherwise connectable: it derives its cable
2574 # from its channelized parent and cannot be cabled directly (matches Interface.is_wired).
2575 'physical': queryset.exclude(type__in=NONCONNECTABLE_IFACE_TYPES).filter(channel_id__isnull=True),
2576 'virtual': queryset.filter(type__in=VIRTUAL_IFACE_TYPES),
2577 'wireless': queryset.filter(type__in=WIRELESS_IFACE_TYPES),
2578 }.get(value, queryset.none())
2580 # Override the method on CabledObjectFilterSet to also check for wireless links
2581 def filter_occupied(self, queryset, name, value):
2582 if value:
2583 return queryset.filter(
2584 Q(cable__isnull=False) |
2585 Q(wireless_link__isnull=False) |
2586 Q(mark_connected=True)
2587 )
2588 return queryset.filter(
2589 cable__isnull=True,
2590 wireless_link__isnull=True,
2591 mark_connected=False
2592 )
2595@register_filterset
2596class FrontPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet):
2597 type = django_filters.MultipleChoiceFilter(
2598 choices=PortTypeChoices,
2599 distinct=False,
2600 null_value=None
2601 )
2602 rear_port_id = django_filters.ModelMultipleChoiceFilter(
2603 field_name='mappings__rear_port',
2604 queryset=RearPort.objects.all(),
2605 to_field_name='rear_port',
2606 label=_('Rear port (ID)'),
2607 )
2609 class Meta:
2610 model = FrontPort
2611 fields = (
2612 'id', 'name', 'label', 'type', 'color', 'positions', 'description', 'mark_connected', 'cable_end',
2613 'cable_connector',
2614 )
2617@register_filterset
2618class RearPortFilterSet(ModularDeviceComponentFilterSet, CabledObjectFilterSet):
2619 type = django_filters.MultipleChoiceFilter(
2620 choices=PortTypeChoices,
2621 distinct=False,
2622 null_value=None
2623 )
2624 front_port_id = django_filters.ModelMultipleChoiceFilter(
2625 field_name='mappings__front_port',
2626 queryset=FrontPort.objects.all(),
2627 to_field_name='front_port',
2628 label=_('Front port (ID)'),
2629 )
2631 class Meta:
2632 model = RearPort
2633 fields = (
2634 'id', 'name', 'label', 'type', 'color', 'positions', 'description', 'mark_connected', 'cable_end',
2635 'cable_connector',
2636 )
2639@register_filterset
2640class ModuleBayFilterSet(ModularDeviceComponentFilterSet):
2641 parent_id = django_filters.ModelMultipleChoiceFilter(
2642 queryset=ModuleBay.objects.all(),
2643 distinct=False,
2644 label=_('Parent module bay (ID)'),
2645 )
2646 installed_module_id = django_filters.ModelMultipleChoiceFilter(
2647 field_name='installed_module',
2648 queryset=ModuleBay.objects.all(),
2649 label=_('Installed module (ID)'),
2650 )
2651 module_bay_type_id = django_filters.ModelMultipleChoiceFilter(
2652 field_name='module_bay_types',
2653 queryset=ModuleBayType.objects.all(),
2654 distinct=False,
2655 label=_('Module bay type (ID)'),
2656 )
2657 module_bay_type = django_filters.ModelMultipleChoiceFilter(
2658 field_name='module_bay_types__slug',
2659 queryset=ModuleBayType.objects.all(),
2660 distinct=False,
2661 to_field_name='slug',
2662 label=_('Module bay type (slug)'),
2663 )
2665 class Meta:
2666 model = ModuleBay
2667 fields = ('id', 'name', 'label', 'position', 'enabled', 'description')
2670@register_filterset
2671class DeviceBayFilterSet(DeviceComponentFilterSet):
2672 installed_device_id = django_filters.ModelMultipleChoiceFilter(
2673 queryset=Device.objects.all(),
2674 distinct=False,
2675 label=_('Installed device (ID)'),
2676 )
2677 installed_device = django_filters.ModelMultipleChoiceFilter(
2678 field_name='installed_device__name',
2679 queryset=Device.objects.all(),
2680 distinct=False,
2681 to_field_name='name',
2682 label=_('Installed device (name)'),
2683 )
2685 class Meta:
2686 model = DeviceBay
2687 fields = ('id', 'name', 'label', 'enabled', 'description')
2690@register_filterset
2691class InventoryItemFilterSet(DeviceComponentFilterSet):
2692 parent_id = django_filters.ModelMultipleChoiceFilter(
2693 queryset=InventoryItem.objects.all(),
2694 distinct=False,
2695 label=_('Parent inventory item (ID)'),
2696 )
2697 manufacturer_id = django_filters.ModelMultipleChoiceFilter(
2698 queryset=Manufacturer.objects.all(),
2699 distinct=False,
2700 label=_('Manufacturer (ID)'),
2701 )
2702 manufacturer = django_filters.ModelMultipleChoiceFilter(
2703 field_name='manufacturer__slug',
2704 queryset=Manufacturer.objects.all(),
2705 distinct=False,
2706 to_field_name='slug',
2707 label=_('Manufacturer (slug)'),
2708 )
2709 role_id = django_filters.ModelMultipleChoiceFilter(
2710 queryset=InventoryItemRole.objects.all(),
2711 distinct=False,
2712 label=_('Role (ID)'),
2713 )
2714 role = django_filters.ModelMultipleChoiceFilter(
2715 field_name='role__slug',
2716 queryset=InventoryItemRole.objects.all(),
2717 distinct=False,
2718 to_field_name='slug',
2719 label=_('Role (slug)'),
2720 )
2721 component_type = MultiValueContentTypeFilter()
2722 component_id = MultiValueNumberFilter()
2723 serial = MultiValueCharFilter(
2724 lookup_expr='iexact'
2725 )
2726 status = django_filters.MultipleChoiceFilter(
2727 choices=InventoryItemStatusChoices,
2728 distinct=False,
2729 null_value=None
2730 )
2732 class Meta:
2733 model = InventoryItem
2734 fields = ('id', 'name', 'label', 'part_id', 'asset_tag', 'status', 'description', 'discovered')
2736 def search(self, queryset, name, value):
2737 if not value.strip():
2738 return queryset
2739 qs_filter = (
2740 Q(name__icontains=value) |
2741 Q(part_id__icontains=value) |
2742 Q(serial__icontains=value) |
2743 Q(asset_tag__icontains=value) |
2744 Q(description__icontains=value)
2745 )
2746 return queryset.filter(qs_filter)
2749@register_filterset
2750class InventoryItemRoleFilterSet(OrganizationalModelFilterSet):
2752 class Meta:
2753 model = InventoryItemRole
2754 fields = ('id', 'name', 'slug', 'color', 'description')
2757@register_filterset
2758class VirtualChassisFilterSet(PrimaryModelFilterSet):
2759 master_id = django_filters.ModelMultipleChoiceFilter(
2760 queryset=Device.objects.all(),
2761 distinct=False,
2762 label=_('Master (ID)'),
2763 )
2764 master = django_filters.ModelMultipleChoiceFilter(
2765 field_name='master__name',
2766 queryset=Device.objects.all(),
2767 distinct=False,
2768 to_field_name='name',
2769 label=_('Master (name)'),
2770 )
2771 region_id = TreeNodeMultipleChoiceFilter(
2772 queryset=Region.objects.all(),
2773 field_name='master__site__region',
2774 lookup_expr='in',
2775 label=_('Region (ID)'),
2776 )
2777 region = TreeNodeMultipleChoiceFilter(
2778 queryset=Region.objects.all(),
2779 field_name='master__site__region',
2780 lookup_expr='in',
2781 to_field_name='slug',
2782 label=_('Region (slug)'),
2783 )
2784 site_group_id = TreeNodeMultipleChoiceFilter(
2785 queryset=SiteGroup.objects.all(),
2786 field_name='master__site__group',
2787 lookup_expr='in',
2788 label=_('Site group (ID)'),
2789 )
2790 site_group = TreeNodeMultipleChoiceFilter(
2791 queryset=SiteGroup.objects.all(),
2792 field_name='master__site__group',
2793 lookup_expr='in',
2794 to_field_name='slug',
2795 label=_('Site group (slug)'),
2796 )
2797 site_id = django_filters.ModelMultipleChoiceFilter(
2798 field_name='master__site',
2799 queryset=Site.objects.all(),
2800 distinct=False,
2801 label=_('Site (ID)'),
2802 )
2803 site = django_filters.ModelMultipleChoiceFilter(
2804 field_name='master__site__slug',
2805 queryset=Site.objects.all(),
2806 distinct=False,
2807 to_field_name='slug',
2808 label=_('Site name (slug)'),
2809 )
2810 tenant_id = django_filters.ModelMultipleChoiceFilter(
2811 field_name='master__tenant',
2812 queryset=Tenant.objects.all(),
2813 distinct=False,
2814 label=_('Tenant (ID)'),
2815 )
2816 tenant = django_filters.ModelMultipleChoiceFilter(
2817 field_name='master__tenant__slug',
2818 queryset=Tenant.objects.all(),
2819 distinct=False,
2820 to_field_name='slug',
2821 label=_('Tenant (slug)'),
2822 )
2824 class Meta:
2825 model = VirtualChassis
2826 fields = ('id', 'domain', 'name', 'description', 'member_count')
2828 def search(self, queryset, name, value):
2829 if not value.strip():
2830 return queryset
2831 qs_filter = (
2832 Q(name__icontains=value) |
2833 Q(description__icontains=value) |
2834 Q(members__name__icontains=value) |
2835 Q(domain__icontains=value)
2836 )
2837 return queryset.filter(qs_filter).distinct()
2840@register_filterset
2841class CableBundleFilterSet(PrimaryModelFilterSet):
2843 class Meta:
2844 model = CableBundle
2845 fields = ('id', 'name', 'description')
2847 def search(self, queryset, name, value):
2848 if not value.strip():
2849 return queryset
2850 return queryset.filter(
2851 Q(name__icontains=value) |
2852 Q(description__icontains=value) |
2853 Q(comments__icontains=value)
2854 )
2857@register_filterset
2858class CableFilterSet(TenancyFilterSet, PrimaryModelFilterSet):
2859 termination_a_type = MultiValueContentTypeFilter(
2860 field_name='terminations__termination_type'
2861 )
2862 termination_a_id = MultiValueNumberFilter(
2863 method='filter_by_cable_end_a',
2864 field_name='terminations__termination_id'
2865 )
2866 termination_b_type = MultiValueContentTypeFilter(
2867 field_name='terminations__termination_type'
2868 )
2869 termination_b_id = MultiValueNumberFilter(
2870 method='filter_by_cable_end_b',
2871 field_name='terminations__termination_id'
2872 )
2873 unterminated = django_filters.BooleanFilter(
2874 method='_unterminated',
2875 label=_('Unterminated'),
2876 )
2877 bundle_id = django_filters.ModelMultipleChoiceFilter(
2878 queryset=CableBundle.objects.all(),
2879 label=_('Cable bundle (ID)'),
2880 )
2881 bundle = django_filters.ModelMultipleChoiceFilter(
2882 field_name='bundle__name',
2883 queryset=CableBundle.objects.all(),
2884 to_field_name='name',
2885 label=_('Cable bundle (name)'),
2886 )
2887 type = django_filters.MultipleChoiceFilter(
2888 choices=CableTypeChoices,
2889 distinct=False,
2890 )
2891 status = django_filters.MultipleChoiceFilter(
2892 choices=LinkStatusChoices,
2893 distinct=False,
2894 )
2895 profile = django_filters.MultipleChoiceFilter(
2896 choices=CableProfileChoices,
2897 distinct=False,
2898 )
2899 color = django_filters.MultipleChoiceFilter(
2900 choices=ColorChoices,
2901 distinct=False,
2902 )
2903 device_id = MultiValueNumberFilter(
2904 method='filter_by_termination'
2905 )
2906 device = MultiValueCharFilter(
2907 method='filter_by_termination',
2908 field_name='device__name'
2909 )
2910 rack_id = MultiValueNumberFilter(
2911 method='filter_by_termination',
2912 field_name='rack_id'
2913 )
2914 rack = MultiValueCharFilter(
2915 method='filter_by_termination',
2916 field_name='rack__name'
2917 )
2918 location_id = MultiValueNumberFilter(
2919 method='filter_by_termination',
2920 field_name='location_id'
2921 )
2922 location = MultiValueCharFilter(
2923 method='filter_by_termination',
2924 field_name='location__name'
2925 )
2926 site_id = MultiValueNumberFilter(
2927 method='filter_by_termination',
2928 field_name='site_id'
2929 )
2930 site = MultiValueCharFilter(
2931 method='filter_by_termination',
2932 field_name='site__slug'
2933 )
2935 # Termination object filters
2936 consoleport_id = MultiValueNumberFilter(
2937 method='filter_by_consoleport'
2938 )
2939 consoleserverport_id = MultiValueNumberFilter(
2940 method='filter_by_consoleserverport'
2941 )
2942 powerport_id = MultiValueNumberFilter(
2943 method='filter_by_powerport'
2944 )
2945 poweroutlet_id = MultiValueNumberFilter(
2946 method='filter_by_poweroutlet'
2947 )
2948 interface_id = MultiValueNumberFilter(
2949 method='filter_by_interface'
2950 )
2951 frontport_id = MultiValueNumberFilter(
2952 method='filter_by_frontport'
2953 )
2954 rearport_id = MultiValueNumberFilter(
2955 method='filter_by_rearport'
2956 )
2957 powerfeed_id = MultiValueNumberFilter(
2958 method='filter_by_powerfeed'
2959 )
2960 circuittermination_id = MultiValueNumberFilter(
2961 method='filter_by_circuittermination'
2962 )
2964 class Meta:
2965 model = Cable
2966 fields = ('id', 'label', 'length', 'length_unit', 'description')
2968 def search(self, queryset, name, value):
2969 if not value.strip():
2970 return queryset
2971 qs_filter = (
2972 Q(label__icontains=value) |
2973 Q(description__icontains=value)
2974 )
2975 return queryset.filter(qs_filter)
2977 def filter_by_termination(self, queryset, name, value):
2978 # Filter by a related object cached on CableTermination. Note the underscore preceding the field name.
2979 # Supported objects: device, rack, location, site
2980 return queryset.filter(**{f'terminations___{name}__in': value}).distinct()
2982 def filter_by_cable_end(self, queryset, name, value, side):
2983 # Filter by termination id and cable_end type
2984 return queryset.filter(**{f'{name}__in': value, 'terminations__cable_end': side}).distinct()
2986 def filter_by_cable_end_a(self, queryset, name, value):
2987 # Filter by termination id and cable_end type
2988 return self.filter_by_cable_end(queryset, name, value, CableEndChoices.SIDE_A)
2990 def filter_by_cable_end_b(self, queryset, name, value):
2991 # Filter by termination id and cable_end type
2992 return self.filter_by_cable_end(queryset, name, value, CableEndChoices.SIDE_B)
2994 def _unterminated(self, queryset, name, value):
2995 if value:
2996 terminated_ids = (
2997 queryset.filter(terminations__cable_end=CableEndChoices.SIDE_A)
2998 .filter(terminations__cable_end=CableEndChoices.SIDE_B)
2999 .values("id")
3000 )
3001 return queryset.exclude(id__in=terminated_ids)
3002 return queryset.filter(terminations__cable_end=CableEndChoices.SIDE_A).filter(
3003 terminations__cable_end=CableEndChoices.SIDE_B
3004 )
3006 def filter_by_termination_object(self, queryset, model, value):
3007 # Filter by specific termination object(s)
3008 content_type = ContentType.objects.get_for_model(model)
3009 cable_ids = CableTermination.objects.filter(
3010 termination_type=content_type,
3011 termination_id__in=value
3012 ).values_list('cable', flat=True)
3013 return queryset.filter(pk__in=cable_ids)
3015 def filter_by_consoleport(self, queryset, name, value):
3016 return self.filter_by_termination_object(queryset, ConsolePort, value)
3018 def filter_by_consoleserverport(self, queryset, name, value):
3019 return self.filter_by_termination_object(queryset, ConsoleServerPort, value)
3021 def filter_by_powerport(self, queryset, name, value):
3022 return self.filter_by_termination_object(queryset, PowerPort, value)
3024 def filter_by_poweroutlet(self, queryset, name, value):
3025 return self.filter_by_termination_object(queryset, PowerOutlet, value)
3027 def filter_by_interface(self, queryset, name, value):
3028 return self.filter_by_termination_object(queryset, Interface, value)
3030 def filter_by_frontport(self, queryset, name, value):
3031 return self.filter_by_termination_object(queryset, FrontPort, value)
3033 def filter_by_rearport(self, queryset, name, value):
3034 return self.filter_by_termination_object(queryset, RearPort, value)
3036 def filter_by_powerfeed(self, queryset, name, value):
3037 return self.filter_by_termination_object(queryset, PowerFeed, value)
3039 def filter_by_circuittermination(self, queryset, name, value):
3040 return self.filter_by_termination_object(queryset, CircuitTermination, value)
3043@register_filterset
3044class CableTerminationFilterSet(ChangeLoggedModelFilterSet):
3045 cable_id = django_filters.ModelMultipleChoiceFilter(
3046 queryset=Cable.objects.all(),
3047 distinct=False,
3048 label=_('Cable (ID)'),
3049 )
3050 termination_type = MultiValueContentTypeFilter()
3052 # Termination object filters
3053 consoleport_id = MultiValueNumberFilter(
3054 method='filter_by_consoleport'
3055 )
3056 consoleserverport_id = MultiValueNumberFilter(
3057 method='filter_by_consoleserverport'
3058 )
3059 powerport_id = MultiValueNumberFilter(
3060 method='filter_by_powerport'
3061 )
3062 poweroutlet_id = MultiValueNumberFilter(
3063 method='filter_by_poweroutlet'
3064 )
3065 interface_id = MultiValueNumberFilter(
3066 method='filter_by_interface'
3067 )
3068 frontport_id = MultiValueNumberFilter(
3069 method='filter_by_frontport'
3070 )
3071 rearport_id = MultiValueNumberFilter(
3072 method='filter_by_rearport'
3073 )
3074 powerfeed_id = MultiValueNumberFilter(
3075 method='filter_by_powerfeed'
3076 )
3077 circuittermination_id = MultiValueNumberFilter(
3078 method='filter_by_circuittermination'
3079 )
3081 class Meta:
3082 model = CableTermination
3083 fields = ('id', 'cable', 'cable_end', 'termination_type', 'termination_id')
3085 def filter_by_termination_object(self, queryset, model, value):
3086 content_type = ContentType.objects.get_for_model(model)
3087 return queryset.filter(termination_type=content_type, termination_id__in=value)
3089 def filter_by_consoleport(self, queryset, name, value):
3090 return self.filter_by_termination_object(queryset, ConsolePort, value)
3092 def filter_by_consoleserverport(self, queryset, name, value):
3093 return self.filter_by_termination_object(queryset, ConsoleServerPort, value)
3095 def filter_by_powerport(self, queryset, name, value):
3096 return self.filter_by_termination_object(queryset, PowerPort, value)
3098 def filter_by_poweroutlet(self, queryset, name, value):
3099 return self.filter_by_termination_object(queryset, PowerOutlet, value)
3101 def filter_by_interface(self, queryset, name, value):
3102 return self.filter_by_termination_object(queryset, Interface, value)
3104 def filter_by_frontport(self, queryset, name, value):
3105 return self.filter_by_termination_object(queryset, FrontPort, value)
3107 def filter_by_rearport(self, queryset, name, value):
3108 return self.filter_by_termination_object(queryset, RearPort, value)
3110 def filter_by_powerfeed(self, queryset, name, value):
3111 return self.filter_by_termination_object(queryset, PowerFeed, value)
3113 def filter_by_circuittermination(self, queryset, name, value):
3114 return self.filter_by_termination_object(queryset, CircuitTermination, value)
3117@register_filterset
3118class PowerPanelFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
3119 region_id = TreeNodeMultipleChoiceFilter(
3120 queryset=Region.objects.all(),
3121 field_name='site__region',
3122 lookup_expr='in',
3123 label=_('Region (ID)'),
3124 )
3125 region = TreeNodeMultipleChoiceFilter(
3126 queryset=Region.objects.all(),
3127 field_name='site__region',
3128 lookup_expr='in',
3129 to_field_name='slug',
3130 label=_('Region (slug)'),
3131 )
3132 site_group_id = TreeNodeMultipleChoiceFilter(
3133 queryset=SiteGroup.objects.all(),
3134 field_name='site__group',
3135 lookup_expr='in',
3136 label=_('Site group (ID)'),
3137 )
3138 site_group = TreeNodeMultipleChoiceFilter(
3139 queryset=SiteGroup.objects.all(),
3140 field_name='site__group',
3141 lookup_expr='in',
3142 to_field_name='slug',
3143 label=_('Site group (slug)'),
3144 )
3145 site_id = django_filters.ModelMultipleChoiceFilter(
3146 queryset=Site.objects.all(),
3147 distinct=False,
3148 label=_('Site (ID)'),
3149 )
3150 site = django_filters.ModelMultipleChoiceFilter(
3151 field_name='site__slug',
3152 queryset=Site.objects.all(),
3153 distinct=False,
3154 to_field_name='slug',
3155 label=_('Site name (slug)'),
3156 )
3157 location_id = TreeNodeMultipleChoiceFilter(
3158 queryset=Location.objects.all(),
3159 field_name='location',
3160 lookup_expr='in',
3161 label=_('Location (ID)'),
3162 )
3164 class Meta:
3165 model = PowerPanel
3166 fields = ('id', 'name', 'description')
3168 def search(self, queryset, name, value):
3169 if not value.strip():
3170 return queryset
3171 qs_filter = (
3172 Q(name__icontains=value) |
3173 Q(description__icontains=value)
3174 )
3175 return queryset.filter(qs_filter)
3178@register_filterset
3179class PowerFeedFilterSet(PrimaryModelFilterSet, CabledObjectFilterSet, PathEndpointFilterSet, TenancyFilterSet):
3180 region_id = TreeNodeMultipleChoiceFilter(
3181 queryset=Region.objects.all(),
3182 field_name='power_panel__site__region',
3183 lookup_expr='in',
3184 label=_('Region (ID)'),
3185 )
3186 region = TreeNodeMultipleChoiceFilter(
3187 queryset=Region.objects.all(),
3188 field_name='power_panel__site__region',
3189 lookup_expr='in',
3190 to_field_name='slug',
3191 label=_('Region (slug)'),
3192 )
3193 site_group_id = TreeNodeMultipleChoiceFilter(
3194 queryset=SiteGroup.objects.all(),
3195 field_name='power_panel__site__group',
3196 lookup_expr='in',
3197 label=_('Site group (ID)'),
3198 )
3199 site_group = TreeNodeMultipleChoiceFilter(
3200 queryset=SiteGroup.objects.all(),
3201 field_name='power_panel__site__group',
3202 lookup_expr='in',
3203 to_field_name='slug',
3204 label=_('Site group (slug)'),
3205 )
3206 site_id = django_filters.ModelMultipleChoiceFilter(
3207 field_name='power_panel__site',
3208 queryset=Site.objects.all(),
3209 distinct=False,
3210 label=_('Site (ID)'),
3211 )
3212 site = django_filters.ModelMultipleChoiceFilter(
3213 field_name='power_panel__site__slug',
3214 queryset=Site.objects.all(),
3215 distinct=False,
3216 to_field_name='slug',
3217 label=_('Site name (slug)'),
3218 )
3219 power_panel_id = django_filters.ModelMultipleChoiceFilter(
3220 queryset=PowerPanel.objects.all(),
3221 distinct=False,
3222 label=_('Power panel (ID)'),
3223 )
3224 rack_id = django_filters.ModelMultipleChoiceFilter(
3225 field_name='rack',
3226 queryset=Rack.objects.all(),
3227 distinct=False,
3228 label=_('Rack (ID)'),
3229 )
3230 status = django_filters.MultipleChoiceFilter(
3231 choices=PowerFeedStatusChoices,
3232 distinct=False,
3233 null_value=None
3234 )
3236 class Meta:
3237 model = PowerFeed
3238 fields = (
3239 'id', 'name', 'status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization',
3240 'available_power', 'mark_connected', 'cable_end', 'cable_connector', 'description',
3241 )
3243 def search(self, queryset, name, value):
3244 if not value.strip():
3245 return queryset
3246 qs_filter = (
3247 Q(name__icontains=value) |
3248 Q(description__icontains=value) |
3249 Q(power_panel__name__icontains=value) |
3250 Q(comments__icontains=value)
3251 )
3252 return queryset.filter(qs_filter)
3255@register_filterset
3256class CoolingSourceFilterSet(PrimaryModelFilterSet, ContactModelFilterSet):
3257 region_id = TreeNodeMultipleChoiceFilter(
3258 queryset=Region.objects.all(),
3259 field_name='site__region',
3260 lookup_expr='in',
3261 label=_('Region (ID)'),
3262 )
3263 region = TreeNodeMultipleChoiceFilter(
3264 queryset=Region.objects.all(),
3265 field_name='site__region',
3266 lookup_expr='in',
3267 to_field_name='slug',
3268 label=_('Region (slug)'),
3269 )
3270 site_group_id = TreeNodeMultipleChoiceFilter(
3271 queryset=SiteGroup.objects.all(),
3272 field_name='site__group',
3273 lookup_expr='in',
3274 label=_('Site group (ID)'),
3275 )
3276 site_group = TreeNodeMultipleChoiceFilter(
3277 queryset=SiteGroup.objects.all(),
3278 field_name='site__group',
3279 lookup_expr='in',
3280 to_field_name='slug',
3281 label=_('Site group (slug)'),
3282 )
3283 site_id = django_filters.ModelMultipleChoiceFilter(
3284 queryset=Site.objects.all(),
3285 distinct=False,
3286 label=_('Site (ID)'),
3287 )
3288 site = django_filters.ModelMultipleChoiceFilter(
3289 field_name='site__slug',
3290 queryset=Site.objects.all(),
3291 distinct=False,
3292 to_field_name='slug',
3293 label=_('Site name (slug)'),
3294 )
3295 location_id = TreeNodeMultipleChoiceFilter(
3296 queryset=Location.objects.all(),
3297 field_name='location',
3298 lookup_expr='in',
3299 label=_('Location (ID)'),
3300 )
3301 location = TreeNodeMultipleChoiceFilter(
3302 queryset=Location.objects.all(),
3303 field_name='location',
3304 lookup_expr='in',
3305 to_field_name='slug',
3306 label=_('Location (slug)'),
3307 )
3308 type = django_filters.MultipleChoiceFilter(
3309 choices=CoolingSourceTypeChoices,
3310 distinct=False,
3311 null_value=None
3312 )
3313 status = django_filters.MultipleChoiceFilter(
3314 choices=CoolingSourceStatusChoices,
3315 distinct=False,
3316 null_value=None
3317 )
3318 fluid_type = django_filters.MultipleChoiceFilter(
3319 choices=FluidTypeChoices,
3320 distinct=False,
3321 null_value=None
3322 )
3324 class Meta:
3325 model = CoolingSource
3326 fields = (
3327 'id', 'name', 'cooling_capacity', 'description',
3328 )
3330 def search(self, queryset, name, value):
3331 if not value.strip():
3332 return queryset
3333 qs_filter = (
3334 Q(name__icontains=value) |
3335 Q(description__icontains=value) |
3336 Q(comments__icontains=value)
3337 )
3338 return queryset.filter(qs_filter)
3341@register_filterset
3342class CoolingFeedFilterSet(PrimaryModelFilterSet, TenancyFilterSet):
3343 region_id = TreeNodeMultipleChoiceFilter(
3344 queryset=Region.objects.all(),
3345 field_name='cooling_source__site__region',
3346 lookup_expr='in',
3347 label=_('Region (ID)'),
3348 )
3349 region = TreeNodeMultipleChoiceFilter(
3350 queryset=Region.objects.all(),
3351 field_name='cooling_source__site__region',
3352 lookup_expr='in',
3353 to_field_name='slug',
3354 label=_('Region (slug)'),
3355 )
3356 site_group_id = TreeNodeMultipleChoiceFilter(
3357 queryset=SiteGroup.objects.all(),
3358 field_name='cooling_source__site__group',
3359 lookup_expr='in',
3360 label=_('Site group (ID)'),
3361 )
3362 site_group = TreeNodeMultipleChoiceFilter(
3363 queryset=SiteGroup.objects.all(),
3364 field_name='cooling_source__site__group',
3365 lookup_expr='in',
3366 to_field_name='slug',
3367 label=_('Site group (slug)'),
3368 )
3369 site_id = django_filters.ModelMultipleChoiceFilter(
3370 field_name='cooling_source__site',
3371 queryset=Site.objects.all(),
3372 distinct=False,
3373 label=_('Site (ID)'),
3374 )
3375 site = django_filters.ModelMultipleChoiceFilter(
3376 field_name='cooling_source__site__slug',
3377 queryset=Site.objects.all(),
3378 distinct=False,
3379 to_field_name='slug',
3380 label=_('Site name (slug)'),
3381 )
3382 cooling_source_id = django_filters.ModelMultipleChoiceFilter(
3383 queryset=CoolingSource.objects.all(),
3384 distinct=False,
3385 label=_('Cooling source (ID)'),
3386 )
3387 rack_id = django_filters.ModelMultipleChoiceFilter(
3388 field_name='rack',
3389 queryset=Rack.objects.all(),
3390 distinct=False,
3391 label=_('Rack (ID)'),
3392 )
3393 status = django_filters.MultipleChoiceFilter(
3394 choices=CoolingFeedStatusChoices,
3395 distinct=False,
3396 null_value=None
3397 )
3398 max_flow_unit = django_filters.MultipleChoiceFilter(
3399 choices=FlowRateUnitChoices,
3400 distinct=False,
3401 null_value=None
3402 )
3404 class Meta:
3405 model = CoolingFeed
3406 fields = (
3407 'id', 'name', 'cooling_capacity', 'max_flow', 'description',
3408 )
3410 def search(self, queryset, name, value):
3411 if not value.strip():
3412 return queryset
3413 qs_filter = (
3414 Q(name__icontains=value) |
3415 Q(description__icontains=value) |
3416 Q(cooling_source__name__icontains=value) |
3417 Q(comments__icontains=value)
3418 )
3419 return queryset.filter(qs_filter)
3422#
3423# Connection filter sets
3424#
3426class ConnectionFilterSet(BaseFilterSet):
3427 q = django_filters.CharFilter(
3428 method='search',
3429 label=_('Search'),
3430 )
3431 site_id = MultiValueNumberFilter(
3432 method='filter_connections',
3433 field_name='device__site_id'
3434 )
3435 site = MultiValueCharFilter(
3436 method='filter_connections',
3437 field_name='device__site__slug'
3438 )
3439 device_id = MultiValueNumberFilter(
3440 method='filter_connections',
3441 field_name='device_id'
3442 )
3443 device = MultiValueCharFilter(
3444 method='filter_connections',
3445 field_name='device__name'
3446 )
3448 def filter_connections(self, queryset, name, value):
3449 if not value:
3450 return queryset
3451 return queryset.filter(**{f'{name}__in': value})
3453 def search(self, queryset, name, value):
3454 if not value.strip():
3455 return queryset
3456 qs_filter = (
3457 Q(device__name__icontains=value) |
3458 Q(cable__label__icontains=value)
3459 )
3460 return queryset.filter(qs_filter)
3463class ConsoleConnectionFilterSet(ConnectionFilterSet):
3465 class Meta:
3466 model = ConsolePort
3467 fields = ('name',)
3470class PowerConnectionFilterSet(ConnectionFilterSet):
3472 class Meta:
3473 model = PowerPort
3474 fields = ('name',)
3477class InterfaceConnectionFilterSet(ConnectionFilterSet):
3479 class Meta:
3480 model = Interface
3481 fields = tuple()