Просмотр исходного кода

Fixes #2813: Add Filter for TenantGroup to the following Forms and Filter classes:

* circuit.Circuit
* dcim.Site
* dcim.Rack
* dcim.RackElevation
* dcim.RackReservation
* dcim.Device
* ipam.IPAddress
* ipam.Prefix
* ipam.VRF
* ipam.VLAN
* virtualization.VirtualMachine
dansheps 7 лет назад
Родитель
Сommit
f78c228c75

+ 13 - 1
netbox/circuits/filters.py

@@ -3,7 +3,7 @@ from django.db.models import Q
 
 
 from dcim.models import Site
 from dcim.models import Site
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
-from tenancy.models import Tenant
+from tenancy.models import Tenant, TenantGroup
 from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
 from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
 from .constants import CIRCUIT_STATUS_CHOICES
 from .constants import CIRCUIT_STATUS_CHOICES
 from .models import Provider, Circuit, CircuitTermination, CircuitType
 from .models import Provider, Circuit, CircuitTermination, CircuitType
@@ -87,6 +87,18 @@ class CircuitFilter(CustomFieldFilterSet, django_filters.FilterSet):
         choices=CIRCUIT_STATUS_CHOICES,
         choices=CIRCUIT_STATUS_CHOICES,
         null_value=None
         null_value=None
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',

+ 14 - 1
netbox/circuits/forms.py

@@ -4,7 +4,7 @@ from taggit.forms import TagField
 from dcim.models import Site
 from dcim.models import Site
 from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
 from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
 from tenancy.forms import TenancyForm
 from tenancy.forms import TenancyForm
-from tenancy.models import Tenant
+from tenancy.models import Tenant, TenantGroup
 from utilities.forms import (
 from utilities.forms import (
     APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField,
     APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField,
     FilterChoiceField, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple
     FilterChoiceField, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple
@@ -292,6 +292,19 @@ class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
         required=False,
         required=False,
         widget=StaticSelect2Multiple()
         widget=StaticSelect2Multiple()
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',

+ 49 - 1
netbox/dcim/filters.py

@@ -6,7 +6,7 @@ from netaddr import EUI
 from netaddr.core import AddrFormatError
 from netaddr.core import AddrFormatError
 
 
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
-from tenancy.models import Tenant
+from tenancy.models import Tenant, TenantGroup
 from utilities.constants import COLOR_CHOICES
 from utilities.constants import COLOR_CHOICES
 from utilities.filters import NameSlugSearchFilterSet, NullableCharFieldFilter, NumericInFilter, TagFilter
 from utilities.filters import NameSlugSearchFilterSet, NullableCharFieldFilter, NumericInFilter, TagFilter
 from virtualization.models import Cluster
 from virtualization.models import Cluster
@@ -59,6 +59,18 @@ class SiteFilter(CustomFieldFilterSet, django_filters.FilterSet):
         field_name='slug',
         field_name='slug',
         label='Region (slug)',
         label='Region (slug)',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',
@@ -160,6 +172,18 @@ class RackFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Group',
         label='Group',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',
@@ -241,6 +265,18 @@ class RackReservationFilter(django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Group',
         label='Group',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',
@@ -491,6 +527,18 @@ class DeviceFilter(CustomFieldFilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Role (slug)',
         label='Role (slug)',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',

+ 53 - 1
netbox/dcim/forms.py

@@ -13,7 +13,7 @@ from timezone_field import TimeZoneFormField
 from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
 from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
 from ipam.models import IPAddress, VLAN, VLANGroup
 from ipam.models import IPAddress, VLAN, VLANGroup
 from tenancy.forms import TenancyForm
 from tenancy.forms import TenancyForm
-from tenancy.models import Tenant
+from tenancy.models import Tenant, TenantGroup
 from utilities.forms import (
 from utilities.forms import (
     APISelect, APISelectMultiple, add_blank_choice, ArrayFieldSelectMultiple, BootstrapMixin, BulkEditForm,
     APISelect, APISelectMultiple, add_blank_choice, ArrayFieldSelectMultiple, BootstrapMixin, BulkEditForm,
     BulkEditNullBooleanSelect, ChainedFieldsMixin, ChainedModelChoiceField, ColorSelect, CommentField,
     BulkEditNullBooleanSelect, ChainedFieldsMixin, ChainedModelChoiceField, ColorSelect, CommentField,
@@ -276,6 +276,19 @@ class SiteFilterForm(BootstrapMixin, CustomFieldFilterForm):
             value_field="slug",
             value_field="slug",
         )
         )
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',
@@ -619,6 +632,19 @@ class RackFilterForm(BootstrapMixin, CustomFieldFilterForm):
             null_option=True,
             null_option=True,
         )
         )
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',
@@ -711,6 +737,19 @@ class RackReservationFilterForm(BootstrapMixin, forms.Form):
             null_option=True,
             null_option=True,
         )
         )
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',
@@ -1703,6 +1742,19 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
             null_option=True,
             null_option=True,
         )
         )
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',

+ 49 - 1
netbox/ipam/filters.py

@@ -6,7 +6,7 @@ from netaddr.core import AddrFormatError
 
 
 from dcim.models import Site, Device, Interface
 from dcim.models import Site, Device, Interface
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
-from tenancy.models import Tenant
+from tenancy.models import Tenant, TenantGroup
 from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
 from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
 from virtualization.models import VirtualMachine
 from virtualization.models import VirtualMachine
 from .constants import IPADDRESS_ROLE_CHOICES, IPADDRESS_STATUS_CHOICES, PREFIX_STATUS_CHOICES, VLAN_STATUS_CHOICES
 from .constants import IPADDRESS_ROLE_CHOICES, IPADDRESS_STATUS_CHOICES, PREFIX_STATUS_CHOICES, VLAN_STATUS_CHOICES
@@ -22,6 +22,18 @@ class VRFFilter(CustomFieldFilterSet, django_filters.FilterSet):
         method='search',
         method='search',
         label='Search',
         label='Search',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',
@@ -146,6 +158,18 @@ class PrefixFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='rd',
         to_field_name='rd',
         label='VRF (RD)',
         label='VRF (RD)',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',
@@ -285,6 +309,18 @@ class IPAddressFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='rd',
         to_field_name='rd',
         label='VRF (RD)',
         label='VRF (RD)',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',
@@ -423,6 +459,18 @@ class VLANFilter(CustomFieldFilterSet, django_filters.FilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Group',
         label='Group',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',

+ 53 - 1
netbox/ipam/forms.py

@@ -6,7 +6,7 @@ from taggit.forms import TagField
 from dcim.models import Site, Rack, Device, Interface
 from dcim.models import Site, Rack, Device, Interface
 from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
 from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
 from tenancy.forms import TenancyForm
 from tenancy.forms import TenancyForm
-from tenancy.models import Tenant
+from tenancy.models import Tenant, TenantGroup
 from utilities.forms import (
 from utilities.forms import (
     add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditNullBooleanSelect, ChainedModelChoiceField,
     add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditNullBooleanSelect, ChainedModelChoiceField,
     CSVChoiceField, ExpandableIPAddressField, FilterChoiceField, FlexibleModelChoiceField, ReturnURLForm, SlugField,
     CSVChoiceField, ExpandableIPAddressField, FilterChoiceField, FlexibleModelChoiceField, ReturnURLForm, SlugField,
@@ -103,6 +103,19 @@ class VRFFilterForm(BootstrapMixin, CustomFieldFilterForm):
         required=False,
         required=False,
         label='Search'
         label='Search'
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',
@@ -535,6 +548,19 @@ class PrefixFilterForm(BootstrapMixin, CustomFieldFilterForm):
             null_option=True,
             null_option=True,
         )
         )
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',
@@ -984,6 +1010,19 @@ class IPAddressFilterForm(BootstrapMixin, CustomFieldFilterForm):
             null_option=True,
             null_option=True,
         )
         )
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',
@@ -1250,6 +1289,19 @@ class VLANFilterForm(BootstrapMixin, CustomFieldFilterForm):
             null_option=True,
             null_option=True,
         )
         )
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',

+ 13 - 1
netbox/virtualization/filters.py

@@ -6,7 +6,7 @@ from netaddr.core import AddrFormatError
 
 
 from dcim.models import DeviceRole, Interface, Platform, Region, Site
 from dcim.models import DeviceRole, Interface, Platform, Region, Site
 from extras.filters import CustomFieldFilterSet
 from extras.filters import CustomFieldFilterSet
-from tenancy.models import Tenant
+from tenancy.models import Tenant, TenantGroup
 from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
 from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter
 from .constants import VM_STATUS_CHOICES
 from .constants import VM_STATUS_CHOICES
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@@ -150,6 +150,18 @@ class VirtualMachineFilter(CustomFieldFilterSet):
         to_field_name='slug',
         to_field_name='slug',
         label='Role (slug)',
         label='Role (slug)',
     )
     )
+    tenant_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__id',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='id',
+        label='Tenant Group (ID)',
+    )
+    tenant_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='tenant__group__slug',
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        label='Tenant Group (slug)',
+    )
     tenant_id = django_filters.ModelMultipleChoiceFilter(
     tenant_id = django_filters.ModelMultipleChoiceFilter(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         label='Tenant (ID)',
         label='Tenant (ID)',

+ 14 - 1
netbox/virtualization/forms.py

@@ -8,7 +8,7 @@ from dcim.models import Device, DeviceRole, Interface, Platform, Rack, Region, S
 from extras.forms import AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
 from extras.forms import AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
 from ipam.models import IPAddress
 from ipam.models import IPAddress
 from tenancy.forms import TenancyForm
 from tenancy.forms import TenancyForm
-from tenancy.models import Tenant
+from tenancy.models import Tenant, TenantGroup
 from utilities.forms import (
 from utilities.forms import (
     add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
     add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
     ChainedFieldsMixin, ChainedModelChoiceField, ChainedModelMultipleChoiceField, CommentField, ComponentForm,
     ChainedFieldsMixin, ChainedModelChoiceField, ChainedModelMultipleChoiceField, CommentField, ComponentForm,
@@ -591,6 +591,19 @@ class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
         required=False,
         required=False,
         widget=StaticSelect2Multiple()
         widget=StaticSelect2Multiple()
     )
     )
+    tenant_group = FilterChoiceField(
+        queryset=TenantGroup.objects.all(),
+        to_field_name='slug',
+        null_label='-- None --',
+        widget=APISelectMultiple(
+            api_url="/api/tenancy/tenant-groups/",
+            value_field="slug",
+            null_option=True,
+            filter_for={
+                'tenant': 'group'
+            }
+        )
+    )
     tenant = FilterChoiceField(
     tenant = FilterChoiceField(
         queryset=Tenant.objects.all(),
         queryset=Tenant.objects.all(),
         to_field_name='slug',
         to_field_name='slug',