mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-27 02:48:38 -06:00
Initial work on #5892
This commit is contained in:
parent
1c66733b8a
commit
07e412e06c
@ -2,7 +2,7 @@ import django_filters
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from dcim.filters import CableTerminationFilterSet, PathEndpointFilterSet
|
from dcim.filters import CableTerminationFilterSet, PathEndpointFilterSet
|
||||||
from dcim.models import Region, Site
|
from dcim.models import Region, Site, SiteGroup
|
||||||
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet
|
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet
|
||||||
from tenancy.filters import TenancyFilterSet
|
from tenancy.filters import TenancyFilterSet
|
||||||
from utilities.filters import (
|
from utilities.filters import (
|
||||||
@ -37,6 +37,19 @@ class ProviderFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdated
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='circuits__terminations__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='circuits__terminations__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='circuits__terminations__site',
|
field_name='circuits__terminations__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
@ -102,17 +115,6 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldModelFilterSet, TenancyFilterSe
|
|||||||
choices=CircuitStatusChoices,
|
choices=CircuitStatusChoices,
|
||||||
null_value=None
|
null_value=None
|
||||||
)
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
field_name='terminations__site',
|
|
||||||
queryset=Site.objects.all(),
|
|
||||||
label='Site (ID)',
|
|
||||||
)
|
|
||||||
site = django_filters.ModelMultipleChoiceFilter(
|
|
||||||
field_name='terminations__site__slug',
|
|
||||||
queryset=Site.objects.all(),
|
|
||||||
to_field_name='slug',
|
|
||||||
label='Site (slug)',
|
|
||||||
)
|
|
||||||
region_id = TreeNodeMultipleChoiceFilter(
|
region_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='terminations__site__region',
|
field_name='terminations__site__region',
|
||||||
@ -126,6 +128,30 @@ class CircuitFilterSet(BaseFilterSet, CustomFieldModelFilterSet, TenancyFilterSe
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='terminations__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='terminations__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='terminations__site',
|
||||||
|
queryset=Site.objects.all(),
|
||||||
|
label='Site (ID)',
|
||||||
|
)
|
||||||
|
site = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='terminations__site__slug',
|
||||||
|
queryset=Site.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site (slug)',
|
||||||
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -3,7 +3,7 @@ from django.test import TestCase
|
|||||||
from circuits.choices import *
|
from circuits.choices import *
|
||||||
from circuits.filters import *
|
from circuits.filters import *
|
||||||
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
from circuits.models import Circuit, CircuitTermination, CircuitType, Provider
|
||||||
from dcim.models import Cable, Region, Site
|
from dcim.models import Cable, Region, Site, SiteGroup
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
|
|
||||||
|
|
||||||
@ -27,13 +27,20 @@ class ProviderTestCase(TestCase):
|
|||||||
Region(name='Test Region 1', slug='test-region-1'),
|
Region(name='Test Region 1', slug='test-region-1'),
|
||||||
Region(name='Test Region 2', slug='test-region-2'),
|
Region(name='Test Region 2', slug='test-region-2'),
|
||||||
)
|
)
|
||||||
# Can't use bulk_create for models with MPTT fields
|
|
||||||
for r in regions:
|
for r in regions:
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
site_groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for site_group in site_groups:
|
||||||
|
site_group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
Site(name='Test Site 1', slug='test-site-1', region=regions[0], group=site_groups[0]),
|
||||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
Site(name='Test Site 2', slug='test-site-2', region=regions[1], group=site_groups[1]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -74,13 +81,6 @@ class ProviderTestCase(TestCase):
|
|||||||
params = {'account': ['1234', '2345']}
|
params = {'account': ['1234', '2345']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
|
||||||
sites = Site.objects.all()[:2]
|
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
|
||||||
params = {'site': [sites[0].slug, sites[1].slug]}
|
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
|
||||||
|
|
||||||
def test_region(self):
|
def test_region(self):
|
||||||
regions = Region.objects.all()[:2]
|
regions = Region.objects.all()[:2]
|
||||||
params = {'region_id': [regions[0].pk, regions[1].pk]}
|
params = {'region_id': [regions[0].pk, regions[1].pk]}
|
||||||
@ -88,6 +88,20 @@ class ProviderTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site(self):
|
||||||
|
sites = Site.objects.all()[:2]
|
||||||
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site': [sites[0].slug, sites[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
|
||||||
class CircuitTypeTestCase(TestCase):
|
class CircuitTypeTestCase(TestCase):
|
||||||
queryset = CircuitType.objects.all()
|
queryset = CircuitType.objects.all()
|
||||||
@ -127,14 +141,21 @@ class CircuitTestCase(TestCase):
|
|||||||
Region(name='Test Region 2', slug='test-region-2'),
|
Region(name='Test Region 2', slug='test-region-2'),
|
||||||
Region(name='Test Region 3', slug='test-region-3'),
|
Region(name='Test Region 3', slug='test-region-3'),
|
||||||
)
|
)
|
||||||
# Can't use bulk_create for models with MPTT fields
|
|
||||||
for r in regions:
|
for r in regions:
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
site_groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for site_group in site_groups:
|
||||||
|
site_group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
Site(name='Test Site 1', slug='test-site-1', region=regions[0], group=site_groups[0]),
|
||||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
Site(name='Test Site 2', slug='test-site-2', region=regions[1], group=site_groups[1]),
|
||||||
Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
|
Site(name='Test Site 3', slug='test-site-3', region=regions[2], group=site_groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -223,6 +244,13 @@ class CircuitTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
|
@ -35,6 +35,7 @@ __all__ = [
|
|||||||
'NestedRearPortTemplateSerializer',
|
'NestedRearPortTemplateSerializer',
|
||||||
'NestedRegionSerializer',
|
'NestedRegionSerializer',
|
||||||
'NestedSiteSerializer',
|
'NestedSiteSerializer',
|
||||||
|
'NestedSiteGroupSerializer',
|
||||||
'NestedVirtualChassisSerializer',
|
'NestedVirtualChassisSerializer',
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -53,6 +54,16 @@ class NestedRegionSerializer(WritableNestedSerializer):
|
|||||||
fields = ['id', 'url', 'name', 'slug', 'site_count', '_depth']
|
fields = ['id', 'url', 'name', 'slug', 'site_count', '_depth']
|
||||||
|
|
||||||
|
|
||||||
|
class NestedSiteGroupSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:sitegroup-detail')
|
||||||
|
site_count = serializers.IntegerField(read_only=True)
|
||||||
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.SiteGroup
|
||||||
|
fields = ['id', 'url', 'name', 'slug', 'site_count', '_depth']
|
||||||
|
|
||||||
|
|
||||||
class NestedSiteSerializer(WritableNestedSerializer):
|
class NestedSiteSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
||||||
|
|
||||||
|
@ -6,13 +6,7 @@ from rest_framework.validators import UniqueTogetherValidator
|
|||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import (
|
from dcim.models import *
|
||||||
Cable, CablePath, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
|
||||||
DeviceBayTemplate, DeviceType, DeviceRole, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
|
||||||
Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
|
||||||
PowerPortTemplate, Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
|
||||||
VirtualChassis,
|
|
||||||
)
|
|
||||||
from netbox.api.serializers import CustomFieldModelSerializer
|
from netbox.api.serializers import CustomFieldModelSerializer
|
||||||
from extras.api.serializers import TaggedObjectSerializer
|
from extras.api.serializers import TaggedObjectSerializer
|
||||||
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer
|
from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer
|
||||||
@ -94,10 +88,24 @@ class RegionSerializer(NestedGroupModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SiteGroupSerializer(NestedGroupModelSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:sitegroup-detail')
|
||||||
|
parent = NestedRegionSerializer(required=False, allow_null=True)
|
||||||
|
site_count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SiteGroup
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'name', 'slug', 'parent', 'description', 'custom_fields', 'created', 'last_updated',
|
||||||
|
'site_count', '_depth',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class SiteSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
|
class SiteSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='dcim-api:site-detail')
|
||||||
status = ChoiceField(choices=SiteStatusChoices, required=False)
|
status = ChoiceField(choices=SiteStatusChoices, required=False)
|
||||||
region = NestedRegionSerializer(required=False, allow_null=True)
|
region = NestedRegionSerializer(required=False, allow_null=True)
|
||||||
|
group = NestedSiteGroupSerializer(required=False, allow_null=True)
|
||||||
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
tenant = NestedTenantSerializer(required=False, allow_null=True)
|
||||||
time_zone = TimeZoneField(required=False)
|
time_zone = TimeZoneField(required=False)
|
||||||
circuit_count = serializers.IntegerField(read_only=True)
|
circuit_count = serializers.IntegerField(read_only=True)
|
||||||
@ -110,10 +118,10 @@ class SiteSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Site
|
model = Site
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description',
|
'id', 'url', 'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'asn', 'time_zone',
|
||||||
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
|
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name',
|
||||||
'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated', 'circuit_count',
|
'contact_phone', 'contact_email', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
'device_count', 'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
|
'circuit_count', 'device_count', 'prefix_count', 'rack_count', 'virtualmachine_count', 'vlan_count',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ router.APIRootView = views.DCIMRootView
|
|||||||
|
|
||||||
# Sites
|
# Sites
|
||||||
router.register('regions', views.RegionViewSet)
|
router.register('regions', views.RegionViewSet)
|
||||||
|
router.register('site-groups', views.SiteGroupViewSet)
|
||||||
router.register('sites', views.SiteViewSet)
|
router.register('sites', views.SiteViewSet)
|
||||||
|
|
||||||
# Racks
|
# Racks
|
||||||
|
@ -16,13 +16,7 @@ from rest_framework.viewsets import GenericViewSet, ViewSet
|
|||||||
|
|
||||||
from circuits.models import Circuit
|
from circuits.models import Circuit
|
||||||
from dcim import filters
|
from dcim import filters
|
||||||
from dcim.models import (
|
from dcim.models import *
|
||||||
Cable, CablePath, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
|
||||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
|
||||||
Manufacturer, InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
|
||||||
PowerPortTemplate, Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
|
||||||
VirtualChassis,
|
|
||||||
)
|
|
||||||
from extras.api.views import ConfigContextQuerySetMixin, CustomFieldModelViewSet
|
from extras.api.views import ConfigContextQuerySetMixin, CustomFieldModelViewSet
|
||||||
from ipam.models import Prefix, VLAN
|
from ipam.models import Prefix, VLAN
|
||||||
from netbox.api.views import ModelViewSet
|
from netbox.api.views import ModelViewSet
|
||||||
@ -111,6 +105,22 @@ class RegionViewSet(CustomFieldModelViewSet):
|
|||||||
filterset_class = filters.RegionFilterSet
|
filterset_class = filters.RegionFilterSet
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Site groups
|
||||||
|
#
|
||||||
|
|
||||||
|
class SiteGroupViewSet(CustomFieldModelViewSet):
|
||||||
|
queryset = SiteGroup.objects.add_related_count(
|
||||||
|
SiteGroup.objects.all(),
|
||||||
|
Site,
|
||||||
|
'group',
|
||||||
|
'site_count',
|
||||||
|
cumulative=True
|
||||||
|
)
|
||||||
|
serializer_class = serializers.SiteGroupSerializer
|
||||||
|
filterset_class = filters.SiteGroupFilterSet
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sites
|
# Sites
|
||||||
#
|
#
|
||||||
|
@ -12,13 +12,7 @@ from utilities.filters import (
|
|||||||
from virtualization.models import Cluster
|
from virtualization.models import Cluster
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .models import (
|
from .models import *
|
||||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
|
||||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
|
||||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort,
|
|
||||||
PowerPortTemplate, Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
|
||||||
VirtualChassis,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -58,6 +52,7 @@ __all__ = (
|
|||||||
'RearPortTemplateFilterSet',
|
'RearPortTemplateFilterSet',
|
||||||
'RegionFilterSet',
|
'RegionFilterSet',
|
||||||
'SiteFilterSet',
|
'SiteFilterSet',
|
||||||
|
'SiteGroupFilterSet',
|
||||||
'VirtualChassisFilterSet',
|
'VirtualChassisFilterSet',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -79,6 +74,23 @@ class RegionFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
fields = ['id', 'name', 'slug', 'description']
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
|
class SiteGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
||||||
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
label='Parent site group (ID)',
|
||||||
|
)
|
||||||
|
parent = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='parent__slug',
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Parent site group (slug)',
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SiteGroup
|
||||||
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
|
class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
@ -96,11 +108,22 @@ class SiteFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet,
|
|||||||
)
|
)
|
||||||
region = TreeNodeMultipleChoiceFilter(
|
region = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
field_name='region',
|
|
||||||
lookup_expr='in',
|
lookup_expr='in',
|
||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Group (ID)',
|
||||||
|
)
|
||||||
|
group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Group (slug)',
|
||||||
|
)
|
||||||
tag = TagFilter()
|
tag = TagFilter()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -145,6 +168,19 @@ class LocationFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label='Site (ID)',
|
||||||
@ -196,6 +232,19 @@ class RackFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet,
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label='Site (ID)',
|
||||||
@ -565,6 +614,19 @@ class DeviceFilterSet(
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label='Site (ID)',
|
||||||
@ -721,6 +783,19 @@ class DeviceComponentFilterSet(CustomFieldModelFilterSet):
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='device__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='device__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='device__site',
|
field_name='device__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
@ -1043,6 +1118,19 @@ class VirtualChassisFilterSet(BaseFilterSet):
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='master__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='master__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='master__site',
|
field_name='master__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
@ -1231,6 +1319,19 @@ class PowerPanelFilterSet(BaseFilterSet):
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label='Site (ID)',
|
||||||
@ -1286,6 +1387,19 @@ class PowerFeedFilterSet(
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='power_panel__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='power_panel__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='power_panel__site',
|
field_name='power_panel__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
|
@ -31,12 +31,7 @@ from utilities.forms import (
|
|||||||
from virtualization.models import Cluster, ClusterGroup
|
from virtualization.models import Cluster, ClusterGroup
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .models import (
|
from .models import *
|
||||||
Cable, DeviceBay, DeviceBayTemplate, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate,
|
|
||||||
Device, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate, Manufacturer,
|
|
||||||
InventoryItem, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel, PowerPort, PowerPortTemplate,
|
|
||||||
Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
|
||||||
)
|
|
||||||
|
|
||||||
DEVICE_BY_PK_RE = r'{\d+\}'
|
DEVICE_BY_PK_RE = r'{\d+\}'
|
||||||
|
|
||||||
@ -61,7 +56,7 @@ def get_device_by_name_or_pk(name):
|
|||||||
|
|
||||||
class DeviceComponentFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
class DeviceComponentFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||||
field_order = [
|
field_order = [
|
||||||
'q', 'region_id', 'site_id'
|
'q', 'region_id', 'site_group_id', 'site_id'
|
||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -72,6 +67,11 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Region')
|
label=_('Region')
|
||||||
)
|
)
|
||||||
|
site_group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Site group')
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -209,6 +209,45 @@ class RegionFilterForm(BootstrapMixin, forms.Form):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Site groups
|
||||||
|
#
|
||||||
|
|
||||||
|
class SiteGroupForm(BootstrapMixin, CustomFieldModelForm):
|
||||||
|
parent = DynamicModelChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
slug = SlugField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SiteGroup
|
||||||
|
fields = (
|
||||||
|
'parent', 'name', 'slug', 'description',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SiteGroupCSVForm(CustomFieldModelCSVForm):
|
||||||
|
parent = CSVModelChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Name of parent site group'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SiteGroup
|
||||||
|
fields = SiteGroup.csv_headers
|
||||||
|
|
||||||
|
|
||||||
|
class SiteGroupFilterForm(BootstrapMixin, forms.Form):
|
||||||
|
model = Site
|
||||||
|
q = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label=_('Search')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sites
|
# Sites
|
||||||
#
|
#
|
||||||
@ -218,6 +257,10 @@ class SiteForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
group = DynamicModelChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
comments = CommentField()
|
comments = CommentField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
@ -228,12 +271,14 @@ class SiteForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Site
|
model = Site
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'slug', 'status', 'region', 'tenant_group', 'tenant', 'facility', 'asn', 'time_zone', 'description',
|
'name', 'slug', 'status', 'region', 'group', 'tenant_group', 'tenant', 'facility', 'asn', 'time_zone',
|
||||||
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
|
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name',
|
||||||
'contact_email', 'comments', 'tags',
|
'contact_phone', 'contact_email', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Site', ('name', 'slug', 'status', 'region', 'facility', 'asn', 'time_zone', 'description', 'tags')),
|
('Site', (
|
||||||
|
'name', 'slug', 'status', 'region', 'group', 'facility', 'asn', 'time_zone', 'description', 'tags',
|
||||||
|
)),
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
('Contact Info', (
|
('Contact Info', (
|
||||||
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
|
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
|
||||||
@ -279,6 +324,12 @@ class SiteCSVForm(CustomFieldModelCSVForm):
|
|||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Assigned region'
|
help_text='Assigned region'
|
||||||
)
|
)
|
||||||
|
group = CSVModelChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Assigned group'
|
||||||
|
)
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -311,6 +362,10 @@ class SiteBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
|||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
group = DynamicModelChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
tenant = DynamicModelChoiceField(
|
tenant = DynamicModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -333,7 +388,7 @@ class SiteBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
nullable_fields = [
|
nullable_fields = [
|
||||||
'region', 'tenant', 'asn', 'description', 'time_zone',
|
'region', 'group', 'tenant', 'asn', 'description', 'time_zone',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -354,6 +409,11 @@ class SiteFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Region')
|
label=_('Region')
|
||||||
)
|
)
|
||||||
|
group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Group')
|
||||||
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
@ -4451,7 +4511,7 @@ class VirtualChassisCSVForm(CustomFieldModelCSVForm):
|
|||||||
|
|
||||||
class VirtualChassisFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
class VirtualChassisFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||||
model = VirtualChassis
|
model = VirtualChassis
|
||||||
field_order = ['q', 'region_id', 'site_id', 'tenant_group_id', 'tenant_id']
|
field_order = ['q', 'region_id', 'site_group_id', 'site_id', 'tenant_group_id', 'tenant_id']
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label=_('Search')
|
label=_('Search')
|
||||||
@ -4461,6 +4521,11 @@ class VirtualChassisFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Region')
|
label=_('Region')
|
||||||
)
|
)
|
||||||
|
site_group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Site group')
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -4580,6 +4645,11 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Region')
|
label=_('Region')
|
||||||
)
|
)
|
||||||
|
site_group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Site group')
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -4803,6 +4873,11 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Region')
|
label=_('Region')
|
||||||
)
|
)
|
||||||
|
site_group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Site group')
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
39
netbox/dcim/migrations/0130_sitegroup.py
Normal file
39
netbox/dcim/migrations/0130_sitegroup.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import django.core.serializers.json
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import mptt.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0129_interface_parent'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SiteGroup',
|
||||||
|
fields=[
|
||||||
|
('created', models.DateField(auto_now_add=True, null=True)),
|
||||||
|
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||||
|
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=100, unique=True)),
|
||||||
|
('slug', models.SlugField(max_length=100, unique=True)),
|
||||||
|
('description', models.CharField(blank=True, max_length=200)),
|
||||||
|
('lft', models.PositiveIntegerField(editable=False)),
|
||||||
|
('rght', models.PositiveIntegerField(editable=False)),
|
||||||
|
('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
|
||||||
|
('level', models.PositiveIntegerField(editable=False)),
|
||||||
|
('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='dcim.sitegroup')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='site',
|
||||||
|
name='group',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sites', to='dcim.sitegroup'),
|
||||||
|
),
|
||||||
|
]
|
@ -41,5 +41,6 @@ __all__ = (
|
|||||||
'RearPortTemplate',
|
'RearPortTemplate',
|
||||||
'Region',
|
'Region',
|
||||||
'Site',
|
'Site',
|
||||||
|
'SiteGroup',
|
||||||
'VirtualChassis',
|
'VirtualChassis',
|
||||||
)
|
)
|
||||||
|
@ -17,6 +17,7 @@ from utilities.querysets import RestrictedQuerySet
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'Region',
|
'Region',
|
||||||
'Site',
|
'Site',
|
||||||
|
'SiteGroup',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -27,7 +28,9 @@ __all__ = (
|
|||||||
@extras_features('custom_fields', 'export_templates', 'webhooks')
|
@extras_features('custom_fields', 'export_templates', 'webhooks')
|
||||||
class Region(NestedGroupModel):
|
class Region(NestedGroupModel):
|
||||||
"""
|
"""
|
||||||
Sites can be grouped within geographic Regions.
|
A region represents a geographic collection of sites. For example, you might create regions representing countries,
|
||||||
|
states, and/or cities. Regions are recursively nested into a hierarchy: all sites belonging to a child region are
|
||||||
|
also considered to be members of its parent and ancestor region(s).
|
||||||
"""
|
"""
|
||||||
parent = TreeForeignKey(
|
parent = TreeForeignKey(
|
||||||
to='self',
|
to='self',
|
||||||
@ -70,6 +73,58 @@ class Region(NestedGroupModel):
|
|||||||
).count()
|
).count()
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Site groups
|
||||||
|
#
|
||||||
|
|
||||||
|
@extras_features('custom_fields', 'export_templates', 'webhooks')
|
||||||
|
class SiteGroup(NestedGroupModel):
|
||||||
|
"""
|
||||||
|
A site group is an arbitrary grouping of sites. For example, you might have corporate sites and customer sites; and
|
||||||
|
within corporate sites you might distinguish between offices and data centers. Like regions, site groups can be
|
||||||
|
nested recursively to form a hierarchy.
|
||||||
|
"""
|
||||||
|
parent = TreeForeignKey(
|
||||||
|
to='self',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='children',
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
db_index=True
|
||||||
|
)
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=100,
|
||||||
|
unique=True
|
||||||
|
)
|
||||||
|
slug = models.SlugField(
|
||||||
|
max_length=100,
|
||||||
|
unique=True
|
||||||
|
)
|
||||||
|
description = models.CharField(
|
||||||
|
max_length=200,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
csv_headers = ['name', 'slug', 'parent', 'description']
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return "{}?group={}".format(reverse('dcim:site_list'), self.slug)
|
||||||
|
|
||||||
|
def to_csv(self):
|
||||||
|
return (
|
||||||
|
self.name,
|
||||||
|
self.slug,
|
||||||
|
self.parent.name if self.parent else None,
|
||||||
|
self.description,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_site_count(self):
|
||||||
|
return Site.objects.filter(
|
||||||
|
Q(group=self) |
|
||||||
|
Q(group__in=self.get_descendants())
|
||||||
|
).count()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sites
|
# Sites
|
||||||
#
|
#
|
||||||
@ -105,6 +160,13 @@ class Site(PrimaryModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
group = models.ForeignKey(
|
||||||
|
to='dcim.SiteGroup',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name='sites',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
tenant = models.ForeignKey(
|
tenant = models.ForeignKey(
|
||||||
to='tenancy.Tenant',
|
to='tenancy.Tenant',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
@ -175,11 +237,12 @@ class Site(PrimaryModel):
|
|||||||
objects = RestrictedQuerySet.as_manager()
|
objects = RestrictedQuerySet.as_manager()
|
||||||
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
|
'name', 'slug', 'status', 'region', 'group', 'tenant', 'facility', 'asn', 'time_zone', 'description',
|
||||||
'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'contact_email', 'comments',
|
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
|
||||||
|
'contact_email', 'comments',
|
||||||
]
|
]
|
||||||
clone_fields = [
|
clone_fields = [
|
||||||
'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
|
'status', 'region', 'group', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
|
||||||
'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'contact_email',
|
'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'contact_email',
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -198,6 +261,7 @@ class Site(PrimaryModel):
|
|||||||
self.slug,
|
self.slug,
|
||||||
self.get_status_display(),
|
self.get_status_display(),
|
||||||
self.region.name if self.region else None,
|
self.region.name if self.region else None,
|
||||||
|
self.group.name if self.group else None,
|
||||||
self.tenant.name if self.tenant else None,
|
self.tenant.name if self.tenant else None,
|
||||||
self.facility,
|
self.facility,
|
||||||
self.asn,
|
self.asn,
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
|
||||||
from dcim.models import Region, Site
|
from dcim.models import Region, Site, SiteGroup
|
||||||
from tenancy.tables import TenantColumn
|
from tenancy.tables import TenantColumn
|
||||||
from utilities.tables import BaseTable, ButtonsColumn, ChoiceFieldColumn, MPTTColumn, TagColumn, ToggleColumn
|
from utilities.tables import BaseTable, ButtonsColumn, ChoiceFieldColumn, MPTTColumn, TagColumn, ToggleColumn
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'RegionTable',
|
'RegionTable',
|
||||||
'SiteTable',
|
'SiteTable',
|
||||||
|
'SiteGroupTable',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -28,6 +29,24 @@ class RegionTable(BaseTable):
|
|||||||
default_columns = ('pk', 'name', 'site_count', 'description', 'actions')
|
default_columns = ('pk', 'name', 'site_count', 'description', 'actions')
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Site groups
|
||||||
|
#
|
||||||
|
|
||||||
|
class SiteGroupTable(BaseTable):
|
||||||
|
pk = ToggleColumn()
|
||||||
|
name = MPTTColumn()
|
||||||
|
site_count = tables.Column(
|
||||||
|
verbose_name='Sites'
|
||||||
|
)
|
||||||
|
actions = ButtonsColumn(SiteGroup)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = SiteGroup
|
||||||
|
fields = ('pk', 'name', 'slug', 'site_count', 'description', 'actions')
|
||||||
|
default_columns = ('pk', 'name', 'site_count', 'description', 'actions')
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sites
|
# Sites
|
||||||
#
|
#
|
||||||
@ -41,6 +60,9 @@ class SiteTable(BaseTable):
|
|||||||
region = tables.Column(
|
region = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
group = tables.Column(
|
||||||
|
linkify=True
|
||||||
|
)
|
||||||
tenant = TenantColumn()
|
tenant = TenantColumn()
|
||||||
tags = TagColumn(
|
tags = TagColumn(
|
||||||
url_name='dcim:site_list'
|
url_name='dcim:site_list'
|
||||||
@ -49,8 +71,8 @@ class SiteTable(BaseTable):
|
|||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = Site
|
model = Site
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'name', 'slug', 'status', 'facility', 'region', 'tenant', 'asn', 'time_zone', 'description',
|
'pk', 'name', 'slug', 'status', 'facility', 'region', 'group', 'tenant', 'asn', 'time_zone', 'description',
|
||||||
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
|
'physical_address', 'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone',
|
||||||
'contact_email', 'tags',
|
'contact_email', 'tags',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'tenant', 'asn', 'description')
|
default_columns = ('pk', 'name', 'status', 'facility', 'region', 'group', 'tenant', 'asn', 'description')
|
||||||
|
@ -4,12 +4,7 @@ from rest_framework import status
|
|||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import (
|
from dcim.models import *
|
||||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
|
||||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate, Manufacturer,
|
|
||||||
InventoryItem, Platform, PowerFeed, PowerPort, PowerPortTemplate, PowerOutlet, PowerOutletTemplate, PowerPanel,
|
|
||||||
Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site, VirtualChassis,
|
|
||||||
)
|
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
from utilities.testing import APITestCase, APIViewTestCases
|
from utilities.testing import APITestCase, APIViewTestCases
|
||||||
from virtualization.models import Cluster, ClusterType
|
from virtualization.models import Cluster, ClusterType
|
||||||
@ -102,14 +97,19 @@ class SiteTest(APIViewTestCases.APIViewTestCase):
|
|||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
regions = (
|
regions = (
|
||||||
Region.objects.create(name='Test Region 1', slug='test-region-1'),
|
Region.objects.create(name='Region 1', slug='region-1'),
|
||||||
Region.objects.create(name='Test Region 2', slug='test-region-2'),
|
Region.objects.create(name='Region 2', slug='region-2'),
|
||||||
|
)
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup.objects.create(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup.objects.create(name='Site Group 2', slug='site-group-2'),
|
||||||
)
|
)
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(region=regions[0], name='Site 1', slug='site-1'),
|
Site(region=regions[0], group=groups[0], name='Site 1', slug='site-1'),
|
||||||
Site(region=regions[0], name='Site 2', slug='site-2'),
|
Site(region=regions[0], group=groups[0], name='Site 2', slug='site-2'),
|
||||||
Site(region=regions[0], name='Site 3', slug='site-3'),
|
Site(region=regions[0], group=groups[0], name='Site 3', slug='site-3'),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -118,18 +118,21 @@ class SiteTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'name': 'Site 4',
|
'name': 'Site 4',
|
||||||
'slug': 'site-4',
|
'slug': 'site-4',
|
||||||
'region': regions[1].pk,
|
'region': regions[1].pk,
|
||||||
|
'group': groups[1].pk,
|
||||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Site 5',
|
'name': 'Site 5',
|
||||||
'slug': 'site-5',
|
'slug': 'site-5',
|
||||||
'region': regions[1].pk,
|
'region': regions[1].pk,
|
||||||
|
'group': groups[1].pk,
|
||||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Site 6',
|
'name': 'Site 6',
|
||||||
'slug': 'site-6',
|
'slug': 'site-6',
|
||||||
'region': regions[1].pk,
|
'region': regions[1].pk,
|
||||||
|
'group': groups[1].pk,
|
||||||
'status': SiteStatusChoices.STATUS_ACTIVE,
|
'status': SiteStatusChoices.STATUS_ACTIVE,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -3,13 +3,7 @@ from django.test import TestCase
|
|||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.filters import *
|
from dcim.filters import *
|
||||||
from dcim.models import (
|
from dcim.models import *
|
||||||
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
|
|
||||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
|
||||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerPanel, PowerPort, PowerPortTemplate, PowerOutlet,
|
|
||||||
PowerOutletTemplate, Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
|
||||||
VirtualChassis,
|
|
||||||
)
|
|
||||||
from ipam.models import IPAddress
|
from ipam.models import IPAddress
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from virtualization.models import Cluster, ClusterType
|
from virtualization.models import Cluster, ClusterType
|
||||||
@ -80,6 +74,14 @@ class SiteTestCase(TestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
tenant_groups = (
|
tenant_groups = (
|
||||||
TenantGroup(name='Tenant group 1', slug='tenant-group-1'),
|
TenantGroup(name='Tenant group 1', slug='tenant-group-1'),
|
||||||
TenantGroup(name='Tenant group 2', slug='tenant-group-2'),
|
TenantGroup(name='Tenant group 2', slug='tenant-group-2'),
|
||||||
@ -96,9 +98,9 @@ class SiteTestCase(TestCase):
|
|||||||
Tenant.objects.bulk_create(tenants)
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0], tenant=tenants[0], status=SiteStatusChoices.STATUS_ACTIVE, facility='Facility 1', asn=65001, latitude=10, longitude=10, contact_name='Contact 1', contact_phone='123-555-0001', contact_email='contact1@example.com'),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0], tenant=tenants[0], status=SiteStatusChoices.STATUS_ACTIVE, facility='Facility 1', asn=65001, latitude=10, longitude=10, contact_name='Contact 1', contact_phone='123-555-0001', contact_email='contact1@example.com'),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1], tenant=tenants[1], status=SiteStatusChoices.STATUS_PLANNED, facility='Facility 2', asn=65002, latitude=20, longitude=20, contact_name='Contact 2', contact_phone='123-555-0002', contact_email='contact2@example.com'),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1], tenant=tenants[1], status=SiteStatusChoices.STATUS_PLANNED, facility='Facility 2', asn=65002, latitude=20, longitude=20, contact_name='Contact 2', contact_phone='123-555-0002', contact_email='contact2@example.com'),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2], tenant=tenants[2], status=SiteStatusChoices.STATUS_RETIRED, facility='Facility 3', asn=65003, latitude=30, longitude=30, contact_name='Contact 3', contact_phone='123-555-0003', contact_email='contact3@example.com'),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2], tenant=tenants[2], status=SiteStatusChoices.STATUS_RETIRED, facility='Facility 3', asn=65003, latitude=30, longitude=30, contact_name='Contact 3', contact_phone='123-555-0003', contact_email='contact3@example.com'),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -153,6 +155,13 @@ class SiteTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'group_id': [groups[0].pk, groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'group': [groups[0].slug, groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_tenant(self):
|
def test_tenant(self):
|
||||||
tenants = Tenant.objects.all()[:2]
|
tenants = Tenant.objects.all()[:2]
|
||||||
params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
|
params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
|
||||||
@ -183,10 +192,18 @@ class LocationTestCase(TestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -229,6 +246,13 @@ class LocationTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -290,10 +314,18 @@ class RackTestCase(TestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -388,6 +420,13 @@ class RackTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -1161,10 +1200,18 @@ class DeviceTestCase(TestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -1324,6 +1371,13 @@ class DeviceTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -1463,10 +1517,19 @@ class ConsolePortTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = Site.objects.bulk_create((
|
sites = Site.objects.bulk_create((
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
Site(name='Site X', slug='site-x'),
|
Site(name='Site X', slug='site-x'),
|
||||||
))
|
))
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
@ -1524,6 +1587,13 @@ class ConsolePortTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -1559,10 +1629,19 @@ class ConsoleServerPortTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = Site.objects.bulk_create((
|
sites = Site.objects.bulk_create((
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
Site(name='Site X', slug='site-x'),
|
Site(name='Site X', slug='site-x'),
|
||||||
))
|
))
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
@ -1620,6 +1699,13 @@ class ConsoleServerPortTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -1655,10 +1741,19 @@ class PowerPortTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = Site.objects.bulk_create((
|
sites = Site.objects.bulk_create((
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
Site(name='Site X', slug='site-x'),
|
Site(name='Site X', slug='site-x'),
|
||||||
))
|
))
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
@ -1724,6 +1819,13 @@ class PowerPortTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -1759,10 +1861,19 @@ class PowerOutletTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = Site.objects.bulk_create((
|
sites = Site.objects.bulk_create((
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
Site(name='Site X', slug='site-x'),
|
Site(name='Site X', slug='site-x'),
|
||||||
))
|
))
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
@ -1825,6 +1936,13 @@ class PowerOutletTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -1860,10 +1978,19 @@ class InterfaceTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = Site.objects.bulk_create((
|
sites = Site.objects.bulk_create((
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
Site(name='Site X', slug='site-x'),
|
Site(name='Site X', slug='site-x'),
|
||||||
))
|
))
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
@ -1966,6 +2093,13 @@ class InterfaceTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -2015,10 +2149,19 @@ class FrontPortTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = Site.objects.bulk_create((
|
sites = Site.objects.bulk_create((
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
Site(name='Site X', slug='site-x'),
|
Site(name='Site X', slug='site-x'),
|
||||||
))
|
))
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
@ -2082,6 +2225,13 @@ class FrontPortTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -2117,10 +2267,19 @@ class RearPortTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = Site.objects.bulk_create((
|
sites = Site.objects.bulk_create((
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
Site(name='Site X', slug='site-x'),
|
Site(name='Site X', slug='site-x'),
|
||||||
))
|
))
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
@ -2178,6 +2337,13 @@ class RearPortTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -2213,10 +2379,19 @@ class DeviceBayTestCase(TestCase):
|
|||||||
)
|
)
|
||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = Site.objects.bulk_create((
|
sites = Site.objects.bulk_create((
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
Site(name='Site X', slug='site-x'),
|
Site(name='Site X', slug='site-x'),
|
||||||
))
|
))
|
||||||
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
manufacturer = Manufacturer.objects.create(name='Manufacturer 1', slug='manufacturer-1')
|
||||||
@ -2256,6 +2431,13 @@ class DeviceBayTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -2296,10 +2478,18 @@ class InventoryItemTestCase(TestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -2356,6 +2546,13 @@ class InventoryItemTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -2409,10 +2606,18 @@ class VirtualChassisTestCase(TestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -2463,6 +2668,13 @@ class VirtualChassisTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -2610,10 +2822,18 @@ class PowerPanelTestCase(TestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -2647,6 +2867,13 @@ class PowerPanelTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -2675,10 +2902,18 @@ class PowerFeedTestCase(TestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[0]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[1]),
|
Site(name='Site 2', slug='site-2', region=regions[1], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[2]),
|
Site(name='Site 3', slug='site-3', region=regions[2], group=groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -2759,6 +2994,13 @@ class PowerFeedTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
|
@ -71,10 +71,17 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
for region in regions:
|
for region in regions:
|
||||||
region.save()
|
region.save()
|
||||||
|
|
||||||
|
groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
)
|
||||||
|
for group in groups:
|
||||||
|
group.save()
|
||||||
|
|
||||||
Site.objects.bulk_create([
|
Site.objects.bulk_create([
|
||||||
Site(name='Site 1', slug='site-1', region=regions[0]),
|
Site(name='Site 1', slug='site-1', region=regions[0], group=groups[1]),
|
||||||
Site(name='Site 2', slug='site-2', region=regions[0]),
|
Site(name='Site 2', slug='site-2', region=regions[0], group=groups[1]),
|
||||||
Site(name='Site 3', slug='site-3', region=regions[0]),
|
Site(name='Site 3', slug='site-3', region=regions[0], group=groups[1]),
|
||||||
])
|
])
|
||||||
|
|
||||||
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
|
tags = cls.create_tags('Alpha', 'Bravo', 'Charlie')
|
||||||
@ -84,6 +91,7 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
'slug': 'site-x',
|
'slug': 'site-x',
|
||||||
'status': SiteStatusChoices.STATUS_PLANNED,
|
'status': SiteStatusChoices.STATUS_PLANNED,
|
||||||
'region': regions[1].pk,
|
'region': regions[1].pk,
|
||||||
|
'group': groups[1].pk,
|
||||||
'tenant': None,
|
'tenant': None,
|
||||||
'facility': 'Facility X',
|
'facility': 'Facility X',
|
||||||
'asn': 65001,
|
'asn': 65001,
|
||||||
@ -110,6 +118,7 @@ class SiteTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
'status': SiteStatusChoices.STATUS_PLANNED,
|
'status': SiteStatusChoices.STATUS_PLANNED,
|
||||||
'region': regions[1].pk,
|
'region': regions[1].pk,
|
||||||
|
'group': groups[1].pk,
|
||||||
'tenant': None,
|
'tenant': None,
|
||||||
'asn': 65009,
|
'asn': 65009,
|
||||||
'time_zone': pytz.timezone('US/Eastern'),
|
'time_zone': pytz.timezone('US/Eastern'),
|
||||||
|
@ -3,11 +3,7 @@ from django.urls import path
|
|||||||
from extras.views import ObjectChangeLogView, ImageAttachmentEditView
|
from extras.views import ObjectChangeLogView, ImageAttachmentEditView
|
||||||
from ipam.views import ServiceEditView
|
from ipam.views import ServiceEditView
|
||||||
from . import views
|
from . import views
|
||||||
from .models import (
|
from .models import *
|
||||||
Cable, ConsolePort, ConsoleServerPort, Device, DeviceBay, DeviceRole, DeviceType, FrontPort, Interface,
|
|
||||||
InventoryItem, Manufacturer, Platform, PowerFeed, PowerPanel, PowerPort, PowerOutlet, Rack, Location,
|
|
||||||
RackReservation, RackRole, RearPort, Region, Site, VirtualChassis,
|
|
||||||
)
|
|
||||||
|
|
||||||
app_name = 'dcim'
|
app_name = 'dcim'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@ -21,6 +17,15 @@ urlpatterns = [
|
|||||||
path('regions/<int:pk>/delete/', views.RegionDeleteView.as_view(), name='region_delete'),
|
path('regions/<int:pk>/delete/', views.RegionDeleteView.as_view(), name='region_delete'),
|
||||||
path('regions/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
|
path('regions/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='region_changelog', kwargs={'model': Region}),
|
||||||
|
|
||||||
|
# Site groups
|
||||||
|
path('site-groups/', views.SiteGroupListView.as_view(), name='sitegroup_list'),
|
||||||
|
path('site-groups/add/', views.SiteGroupEditView.as_view(), name='sitegroup_add'),
|
||||||
|
path('site-groups/import/', views.SiteGroupBulkImportView.as_view(), name='sitegroup_import'),
|
||||||
|
path('site-groups/delete/', views.SiteGroupBulkDeleteView.as_view(), name='sitegroup_bulk_delete'),
|
||||||
|
path('site-groups/<int:pk>/edit/', views.SiteGroupEditView.as_view(), name='sitegroup_edit'),
|
||||||
|
path('site-groups/<int:pk>/delete/', views.SiteGroupDeleteView.as_view(), name='sitegroup_delete'),
|
||||||
|
path('site-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='sitegroup_changelog', kwargs={'model': SiteGroup}),
|
||||||
|
|
||||||
# Sites
|
# Sites
|
||||||
path('sites/', views.SiteListView.as_view(), name='site_list'),
|
path('sites/', views.SiteListView.as_view(), name='site_list'),
|
||||||
path('sites/add/', views.SiteEditView.as_view(), name='site_add'),
|
path('sites/add/', views.SiteEditView.as_view(), name='site_add'),
|
||||||
|
@ -31,7 +31,7 @@ from .models import (
|
|||||||
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
|
||||||
InventoryItem, Manufacturer, PathEndpoint, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel,
|
InventoryItem, Manufacturer, PathEndpoint, Platform, PowerFeed, PowerOutlet, PowerOutletTemplate, PowerPanel,
|
||||||
PowerPort, PowerPortTemplate, Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
PowerPort, PowerPortTemplate, Rack, Location, RackReservation, RackRole, RearPort, RearPortTemplate, Region, Site,
|
||||||
VirtualChassis,
|
SiteGroup, VirtualChassis,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -138,6 +138,50 @@ class RegionBulkDeleteView(generic.BulkDeleteView):
|
|||||||
table = tables.RegionTable
|
table = tables.RegionTable
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Site groups
|
||||||
|
#
|
||||||
|
|
||||||
|
class SiteGroupListView(generic.ObjectListView):
|
||||||
|
queryset = SiteGroup.objects.add_related_count(
|
||||||
|
SiteGroup.objects.all(),
|
||||||
|
Site,
|
||||||
|
'group',
|
||||||
|
'site_count',
|
||||||
|
cumulative=True
|
||||||
|
)
|
||||||
|
filterset = filters.SiteGroupFilterSet
|
||||||
|
filterset_form = forms.SiteGroupFilterForm
|
||||||
|
table = tables.SiteGroupTable
|
||||||
|
|
||||||
|
|
||||||
|
class SiteGroupEditView(generic.ObjectEditView):
|
||||||
|
queryset = SiteGroup.objects.all()
|
||||||
|
model_form = forms.SiteGroupForm
|
||||||
|
|
||||||
|
|
||||||
|
class SiteGroupDeleteView(generic.ObjectDeleteView):
|
||||||
|
queryset = SiteGroup.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class SiteGroupBulkImportView(generic.BulkImportView):
|
||||||
|
queryset = SiteGroup.objects.all()
|
||||||
|
model_form = forms.SiteGroupCSVForm
|
||||||
|
table = tables.SiteGroupTable
|
||||||
|
|
||||||
|
|
||||||
|
class SiteGroupBulkDeleteView(generic.BulkDeleteView):
|
||||||
|
queryset = SiteGroup.objects.add_related_count(
|
||||||
|
SiteGroup.objects.all(),
|
||||||
|
Site,
|
||||||
|
'group',
|
||||||
|
'site_count',
|
||||||
|
cumulative=True
|
||||||
|
)
|
||||||
|
filterset = filters.SiteGroupFilterSet
|
||||||
|
table = tables.SiteGroupTable
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sites
|
# Sites
|
||||||
#
|
#
|
||||||
|
@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.forms import DateField, IntegerField, NullBooleanField
|
from django.forms import DateField, IntegerField, NullBooleanField
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.filters import BaseFilterSet, ContentTypeFilter
|
from utilities.filters import BaseFilterSet, ContentTypeFilter
|
||||||
from virtualization.models import Cluster, ClusterGroup
|
from virtualization.models import Cluster, ClusterGroup
|
||||||
@ -129,6 +129,17 @@ class ConfigContextFilterSet(BaseFilterSet):
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='site_groups__slug',
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
|
site_group_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='site_groups',
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
label='Site group',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='sites',
|
field_name='sites',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
|
@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect,
|
add_blank_choice, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ColorSelect,
|
||||||
@ -210,6 +210,10 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
|||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
site_groups = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
sites = DynamicModelMultipleChoiceField(
|
sites = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -249,8 +253,8 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = ConfigContext
|
model = ConfigContext
|
||||||
fields = (
|
fields = (
|
||||||
'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'cluster_groups',
|
'name', 'weight', 'description', 'is_active', 'regions', 'site_groups', 'sites', 'roles', 'platforms',
|
||||||
'clusters', 'tenant_groups', 'tenants', 'tags', 'data',
|
'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -280,8 +284,8 @@ class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
|
|
||||||
class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
||||||
field_order = [
|
field_order = [
|
||||||
'q', 'region_id', 'site_id', 'role_id', 'platform_id', 'cluster_group_id', 'cluster_id', 'tenant_group_id',
|
'q', 'region_id', 'site_group_id', 'site_id', 'role_id', 'platform_id', 'cluster_group_id', 'cluster_id',
|
||||||
'tenant_id',
|
'tenant_group_id', 'tenant_id',
|
||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -292,6 +296,11 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Regions')
|
label=_('Regions')
|
||||||
)
|
)
|
||||||
|
site_group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Site groups')
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
17
netbox/extras/migrations/0056_sitegroup.py
Normal file
17
netbox/extras/migrations/0056_sitegroup.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('dcim', '0130_sitegroup'),
|
||||||
|
('extras', '0055_objectchange_data'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='configcontext',
|
||||||
|
name='site_groups',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='_extras_configcontext_site_groups_+', to='dcim.SiteGroup'),
|
||||||
|
),
|
||||||
|
]
|
@ -386,6 +386,11 @@ class ConfigContext(ChangeLoggingMixin, BigIDModel):
|
|||||||
related_name='+',
|
related_name='+',
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
site_groups = models.ManyToManyField(
|
||||||
|
to='dcim.SiteGroup',
|
||||||
|
related_name='+',
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
sites = models.ManyToManyField(
|
sites = models.ManyToManyField(
|
||||||
to='dcim.Site',
|
to='dcim.Site',
|
||||||
related_name='+',
|
related_name='+',
|
||||||
|
@ -4,7 +4,7 @@ from django.contrib.auth.models import User
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Rack, Region, Site
|
from dcim.models import DeviceRole, Platform, Rack, Region, Site, SiteGroup
|
||||||
from extras.choices import ObjectChangeActionChoices
|
from extras.choices import ObjectChangeActionChoices
|
||||||
from extras.filters import *
|
from extras.filters import *
|
||||||
from extras.models import ConfigContext, ExportTemplate, ImageAttachment, ObjectChange, Tag
|
from extras.models import ConfigContext, ExportTemplate, ImageAttachment, ObjectChange, Tag
|
||||||
@ -132,10 +132,17 @@ class ConfigContextTestCase(TestCase):
|
|||||||
Region(name='Test Region 2', slug='test-region-2'),
|
Region(name='Test Region 2', slug='test-region-2'),
|
||||||
Region(name='Test Region 3', slug='test-region-3'),
|
Region(name='Test Region 3', slug='test-region-3'),
|
||||||
)
|
)
|
||||||
# Can't use bulk_create for models with MPTT fields
|
|
||||||
for r in regions:
|
for r in regions:
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
site_groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for site_group in site_groups:
|
||||||
|
site_group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Test Site 1', slug='test-site-1'),
|
Site(name='Test Site 1', slug='test-site-1'),
|
||||||
Site(name='Test Site 2', slug='test-site-2'),
|
Site(name='Test Site 2', slug='test-site-2'),
|
||||||
@ -195,6 +202,7 @@ class ConfigContextTestCase(TestCase):
|
|||||||
data='{"foo": 123}'
|
data='{"foo": 123}'
|
||||||
)
|
)
|
||||||
c.regions.set([regions[i]])
|
c.regions.set([regions[i]])
|
||||||
|
c.site_groups.set([site_groups[i]])
|
||||||
c.sites.set([sites[i]])
|
c.sites.set([sites[i]])
|
||||||
c.roles.set([device_roles[i]])
|
c.roles.set([device_roles[i]])
|
||||||
c.platforms.set([platforms[i]])
|
c.platforms.set([platforms[i]])
|
||||||
@ -224,6 +232,13 @@ class ConfigContextTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
|
@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from netaddr.core import AddrFormatError
|
from netaddr.core import AddrFormatError
|
||||||
|
|
||||||
from dcim.models import Device, Interface, Region, Site
|
from dcim.models import Device, Interface, Region, Site, SiteGroup
|
||||||
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet
|
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet
|
||||||
from tenancy.filters import TenancyFilterSet
|
from tenancy.filters import TenancyFilterSet
|
||||||
from utilities.filters import (
|
from utilities.filters import (
|
||||||
@ -254,6 +254,19 @@ class PrefixFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label='Site (ID)',
|
||||||
@ -535,6 +548,19 @@ class VLANGroupFilterSet(BaseFilterSet, NameSlugSearchFilterSet):
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label='Site (ID)',
|
||||||
@ -569,6 +595,19 @@ class VLANFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSet,
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label='Site (ID)',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Device, Interface, Rack, Region, Site
|
from dcim.models import Device, Interface, Rack, Region, Site, SiteGroup
|
||||||
from extras.forms import (
|
from extras.forms import (
|
||||||
AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldModelCSVForm, CustomFieldModelForm, CustomFieldFilterForm,
|
AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldModelCSVForm, CustomFieldModelForm, CustomFieldFilterForm,
|
||||||
)
|
)
|
||||||
@ -547,8 +547,8 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF
|
|||||||
class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||||
model = Prefix
|
model = Prefix
|
||||||
field_order = [
|
field_order = [
|
||||||
'q', 'within_include', 'family', 'mask_length', 'vrf_id', 'present_in_vrf_id', 'status', 'region_id', 'site_id',
|
'q', 'within_include', 'family', 'mask_length', 'vrf_id', 'present_in_vrf_id', 'status', 'region_id',
|
||||||
'role_id', 'tenant_group_id', 'tenant_id', 'is_pool',
|
'site_group_id', 'site_id', 'role_id', 'tenant_group_id', 'tenant_id', 'is_pool',
|
||||||
]
|
]
|
||||||
mask_length__lte = forms.IntegerField(
|
mask_length__lte = forms.IntegerField(
|
||||||
widget=forms.HiddenInput()
|
widget=forms.HiddenInput()
|
||||||
@ -599,6 +599,11 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm)
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Region')
|
label=_('Region')
|
||||||
)
|
)
|
||||||
|
site_group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Site group')
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -1125,6 +1130,11 @@ class VLANGroupFilterForm(BootstrapMixin, forms.Form):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Region')
|
label=_('Region')
|
||||||
)
|
)
|
||||||
|
site_group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Site group')
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -1292,7 +1302,9 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor
|
|||||||
|
|
||||||
class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
||||||
model = VLAN
|
model = VLAN
|
||||||
field_order = ['q', 'region_id', 'site_id', 'group_id', 'status', 'role_id', 'tenant_group_id', 'tenant_id']
|
field_order = [
|
||||||
|
'q', 'region_id', 'site_group_id', 'site_id', 'group_id', 'status', 'role_id', 'tenant_group_id', 'tenant_id',
|
||||||
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='Search'
|
label='Search'
|
||||||
@ -1302,6 +1314,11 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Region')
|
label=_('Region')
|
||||||
)
|
)
|
||||||
|
site_group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Site group')
|
||||||
|
)
|
||||||
site_id = DynamicModelMultipleChoiceField(
|
site_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Region, Site
|
from dcim.models import Device, DeviceRole, DeviceType, Interface, Manufacturer, Region, Site, SiteGroup
|
||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
from ipam.filters import *
|
from ipam.filters import *
|
||||||
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
|
||||||
@ -343,14 +343,21 @@ class PrefixTestCase(TestCase):
|
|||||||
Region(name='Test Region 2', slug='test-region-2'),
|
Region(name='Test Region 2', slug='test-region-2'),
|
||||||
Region(name='Test Region 3', slug='test-region-3'),
|
Region(name='Test Region 3', slug='test-region-3'),
|
||||||
)
|
)
|
||||||
# Can't use bulk_create for models with MPTT fields
|
|
||||||
for r in regions:
|
for r in regions:
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
site_groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for site_group in site_groups:
|
||||||
|
site_group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
Site(name='Test Site 1', slug='test-site-1', region=regions[0], group=site_groups[0]),
|
||||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
Site(name='Test Site 2', slug='test-site-2', region=regions[1], group=site_groups[1]),
|
||||||
Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
|
Site(name='Test Site 3', slug='test-site-3', region=regions[2], group=site_groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -468,6 +475,13 @@ class PrefixTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -701,14 +715,21 @@ class VLANGroupTestCase(TestCase):
|
|||||||
Region(name='Test Region 2', slug='test-region-2'),
|
Region(name='Test Region 2', slug='test-region-2'),
|
||||||
Region(name='Test Region 3', slug='test-region-3'),
|
Region(name='Test Region 3', slug='test-region-3'),
|
||||||
)
|
)
|
||||||
# Can't use bulk_create for models with MPTT fields
|
|
||||||
for r in regions:
|
for r in regions:
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
site_groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for site_group in site_groups:
|
||||||
|
site_group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
Site(name='Test Site 1', slug='test-site-1', region=regions[0], group=site_groups[0]),
|
||||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
Site(name='Test Site 2', slug='test-site-2', region=regions[1], group=site_groups[1]),
|
||||||
Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
|
Site(name='Test Site 3', slug='test-site-3', region=regions[2], group=site_groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -743,6 +764,13 @@ class VLANGroupTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -763,14 +791,21 @@ class VLANTestCase(TestCase):
|
|||||||
Region(name='Test Region 2', slug='test-region-2'),
|
Region(name='Test Region 2', slug='test-region-2'),
|
||||||
Region(name='Test Region 3', slug='test-region-3'),
|
Region(name='Test Region 3', slug='test-region-3'),
|
||||||
)
|
)
|
||||||
# Can't use bulk_create for models with MPTT fields
|
|
||||||
for r in regions:
|
for r in regions:
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
site_groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for site_group in site_groups:
|
||||||
|
site_group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
Site(name='Test Site 1', slug='test-site-1', region=regions[0], group=site_groups[0]),
|
||||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
Site(name='Test Site 2', slug='test-site-2', region=regions[1], group=site_groups[1]),
|
||||||
Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
|
Site(name='Test Site 3', slug='test-site-3', region=regions[2], group=site_groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -832,6 +867,13 @@ class VLANTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
|
@ -41,6 +41,19 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Group</td>
|
||||||
|
<td>
|
||||||
|
{% if object.group %}
|
||||||
|
{% for group in object.group.get_ancestors %}
|
||||||
|
<a href="{{ group.get_absolute_url }}">{{ group }}</a> /
|
||||||
|
{% endfor %}
|
||||||
|
<a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Tenant</td>
|
<td>Tenant</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -38,6 +38,15 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{% url 'dcim:region_list' %}">Regions</a>
|
<a href="{% url 'dcim:region_list' %}">Regions</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li{% if not perms.dcim.view_sitegroup %} class="disabled"{% endif %}>
|
||||||
|
{% if perms.dcim.add_sitegroup %}
|
||||||
|
<div class="buttons pull-right">
|
||||||
|
<a href="{% url 'dcim:sitegroup_add' %}" class="btn btn-xs btn-success" title="Add"><i class="mdi mdi-plus-thick"></i></a>
|
||||||
|
<a href="{% url 'dcim:sitegroup_import' %}" class="btn btn-xs btn-info" title="Import"><i class="mdi mdi-database-import-outline"></i></a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{% url 'dcim:sitegroup_list' %}">Site Groups</a>
|
||||||
|
</li>
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li class="dropdown-header">Racks</li>
|
<li class="dropdown-header">Racks</li>
|
||||||
<li{% if not perms.dcim.view_rack %} class="disabled"{% endif %}>
|
<li{% if not perms.dcim.view_rack %} class="disabled"{% endif %}>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import django_filters
|
import django_filters
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
||||||
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet, LocalConfigContextFilterSet
|
from extras.filters import CustomFieldModelFilterSet, CreatedUpdatedFilterSet, LocalConfigContextFilterSet
|
||||||
from tenancy.filters import TenancyFilterSet
|
from tenancy.filters import TenancyFilterSet
|
||||||
from utilities.filters import (
|
from utilities.filters import (
|
||||||
@ -52,6 +52,19 @@ class ClusterFilterSet(BaseFilterSet, TenancyFilterSet, CustomFieldModelFilterSe
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
label='Site (ID)',
|
label='Site (ID)',
|
||||||
@ -151,6 +164,19 @@ class VirtualMachineFilterSet(
|
|||||||
to_field_name='slug',
|
to_field_name='slug',
|
||||||
label='Region (slug)',
|
label='Region (slug)',
|
||||||
)
|
)
|
||||||
|
site_group_id = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='cluster__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
label='Site group (ID)',
|
||||||
|
)
|
||||||
|
site_group = TreeNodeMultipleChoiceFilter(
|
||||||
|
queryset=SiteGroup.objects.all(),
|
||||||
|
field_name='cluster__site__group',
|
||||||
|
lookup_expr='in',
|
||||||
|
to_field_name='slug',
|
||||||
|
label='Site group (slug)',
|
||||||
|
)
|
||||||
site_id = django_filters.ModelMultipleChoiceFilter(
|
site_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
field_name='cluster__site',
|
field_name='cluster__site',
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site
|
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
||||||
from ipam.models import IPAddress
|
from ipam.models import IPAddress
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
from virtualization.choices import *
|
from virtualization.choices import *
|
||||||
@ -96,14 +96,21 @@ class ClusterTestCase(TestCase):
|
|||||||
Region(name='Test Region 2', slug='test-region-2'),
|
Region(name='Test Region 2', slug='test-region-2'),
|
||||||
Region(name='Test Region 3', slug='test-region-3'),
|
Region(name='Test Region 3', slug='test-region-3'),
|
||||||
)
|
)
|
||||||
# Can't use bulk_create for models with MPTT fields
|
|
||||||
for r in regions:
|
for r in regions:
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
site_groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for site_group in site_groups:
|
||||||
|
site_group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
Site(name='Test Site 1', slug='test-site-1', region=regions[0], group=site_groups[0]),
|
||||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
Site(name='Test Site 2', slug='test-site-2', region=regions[1], group=site_groups[1]),
|
||||||
Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
|
Site(name='Test Site 3', slug='test-site-3', region=regions[2], group=site_groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -144,6 +151,13 @@ class ClusterTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
@ -206,14 +220,21 @@ class VirtualMachineTestCase(TestCase):
|
|||||||
Region(name='Test Region 2', slug='test-region-2'),
|
Region(name='Test Region 2', slug='test-region-2'),
|
||||||
Region(name='Test Region 3', slug='test-region-3'),
|
Region(name='Test Region 3', slug='test-region-3'),
|
||||||
)
|
)
|
||||||
# Can't use bulk_create for models with MPTT fields
|
|
||||||
for r in regions:
|
for r in regions:
|
||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
|
site_groups = (
|
||||||
|
SiteGroup(name='Site Group 1', slug='site-group-1'),
|
||||||
|
SiteGroup(name='Site Group 2', slug='site-group-2'),
|
||||||
|
SiteGroup(name='Site Group 3', slug='site-group-3'),
|
||||||
|
)
|
||||||
|
for site_group in site_groups:
|
||||||
|
site_group.save()
|
||||||
|
|
||||||
sites = (
|
sites = (
|
||||||
Site(name='Test Site 1', slug='test-site-1', region=regions[0]),
|
Site(name='Test Site 1', slug='test-site-1', region=regions[0], group=site_groups[0]),
|
||||||
Site(name='Test Site 2', slug='test-site-2', region=regions[1]),
|
Site(name='Test Site 2', slug='test-site-2', region=regions[1], group=site_groups[1]),
|
||||||
Site(name='Test Site 3', slug='test-site-3', region=regions[2]),
|
Site(name='Test Site 3', slug='test-site-3', region=regions[2], group=site_groups[2]),
|
||||||
)
|
)
|
||||||
Site.objects.bulk_create(sites)
|
Site.objects.bulk_create(sites)
|
||||||
|
|
||||||
@ -329,6 +350,13 @@ class VirtualMachineTestCase(TestCase):
|
|||||||
params = {'region': [regions[0].slug, regions[1].slug]}
|
params = {'region': [regions[0].slug, regions[1].slug]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_site_group(self):
|
||||||
|
site_groups = SiteGroup.objects.all()[:2]
|
||||||
|
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_site(self):
|
def test_site(self):
|
||||||
sites = Site.objects.all()[:2]
|
sites = Site.objects.all()[:2]
|
||||||
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
params = {'site_id': [sites[0].pk, sites[1].pk]}
|
||||||
|
Loading…
Reference in New Issue
Block a user