diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 896146069..feebaba0e 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -13,7 +13,7 @@ from extras.models import ( ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap, UserAction, ) from extras.constants import * -from tenancy.api.serializers import NestedTenantSerializer +from tenancy.api.serializers import NestedTenantSerializer, NestedTenantGroupSerializer from users.api.serializers import NestedUserSerializer from utilities.api import ( ChoiceFieldSerializer, ContentTypeFieldSerializer, get_serializer_for_model, ValidatedModelSerializer, @@ -136,13 +136,14 @@ class ConfigContextSerializer(ValidatedModelSerializer): sites = NestedSiteSerializer(required=False, many=True) roles = NestedDeviceRoleSerializer(required=False, many=True) platforms = NestedPlatformSerializer(required=False, many=True) + tenant_groups = NestedTenantGroupSerializer(required=False, many=True) tenants = NestedTenantSerializer(required=False, many=True) class Meta: model = ConfigContext fields = [ - 'id', 'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'tenants', - 'data', + 'id', 'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', + 'tenant_groups', 'tenants', 'data', ] diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 16c38e61d..0b8d27233 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -218,7 +218,8 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm): class Meta: model = ConfigContext fields = [ - 'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'tenants', 'data', + 'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'tenant_groups', + 'tenants', 'data', ] diff --git a/netbox/extras/migrations/0014_configcontexts.py b/netbox/extras/migrations/0014_configcontexts.py index bc12e2cdf..789679e4f 100644 --- a/netbox/extras/migrations/0014_configcontexts.py +++ b/netbox/extras/migrations/0014_configcontexts.py @@ -1,14 +1,15 @@ -# Generated by Django 2.0.6 on 2018-06-29 13:34 +# Generated by Django 2.0.7 on 2018-07-27 19:44 import django.contrib.postgres.fields.jsonb from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ ('tenancy', '0005_change_logging'), - ('dcim', '0060_change_logging'), + ('dcim', '0061_platform_napalm_args'), ('extras', '0013_objectchange'), ] @@ -19,13 +20,14 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100, unique=True)), ('weight', models.PositiveSmallIntegerField(default=1000)), - ('is_active', models.BooleanField(default=True)), ('description', models.CharField(blank=True, max_length=100)), + ('is_active', models.BooleanField(default=True)), ('data', django.contrib.postgres.fields.jsonb.JSONField()), ('platforms', models.ManyToManyField(blank=True, related_name='_configcontext_platforms_+', to='dcim.Platform')), ('regions', models.ManyToManyField(blank=True, related_name='_configcontext_regions_+', to='dcim.Region')), ('roles', models.ManyToManyField(blank=True, related_name='_configcontext_roles_+', to='dcim.DeviceRole')), ('sites', models.ManyToManyField(blank=True, related_name='_configcontext_sites_+', to='dcim.Site')), + ('tenant_groups', models.ManyToManyField(blank=True, related_name='_configcontext_tenant_groups_+', to='tenancy.TenantGroup')), ('tenants', models.ManyToManyField(blank=True, related_name='_configcontext_tenants_+', to='tenancy.Tenant')), ], options={ @@ -35,11 +37,16 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='customfield', name='obj_type', - field=models.ManyToManyField(help_text='The object(s) to which this field applies.', limit_choices_to={'model__in': ('provider', 'circuit', 'site', 'rack', 'devicetype', 'device', 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', 'tenant', 'cluster', 'virtualmachine')}, related_name='custom_fields', to='contenttypes.ContentType', verbose_name='Object(s)'), + field=models.ManyToManyField(help_text='The object(s) to which this field applies.', limit_choices_to={'model__in': ('provider', 'circuit', 'site', 'rack', 'devicetype', 'device', 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', 'secret', 'tenant', 'cluster', 'virtualmachine')}, related_name='custom_fields', to='contenttypes.ContentType', verbose_name='Object(s)'), + ), + migrations.AlterField( + model_name='exporttemplate', + name='content_type', + field=models.ForeignKey(limit_choices_to={'model__in': ['provider', 'circuit', 'site', 'region', 'rack', 'rackgroup', 'manufacturer', 'devicetype', 'device', 'consoleport', 'powerport', 'interfaceconnection', 'virtualchassis', 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', 'secret', 'tenant', 'cluster', 'virtualmachine']}, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'), ), migrations.AlterField( model_name='webhook', name='obj_type', - field=models.ManyToManyField(help_text='The object(s) to which this Webhook applies.', limit_choices_to={'model__in': ('provider', 'circuit', 'site', 'rack', 'rackgroup', 'device', 'interface', 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vlangroup', 'vrf', 'service', 'tenant', 'tenantgroup', 'cluster', 'clustergroup', 'virtualmachine')}, related_name='webhooks', to='contenttypes.ContentType', verbose_name='Object types'), + field=models.ManyToManyField(help_text='The object(s) to which this Webhook applies.', limit_choices_to={'model__in': ('provider', 'circuit', 'site', 'rack', 'devicetype', 'device', 'virtualchassis', 'consoleport', 'consoleserverport', 'powerport', 'poweroutlet', 'interface', 'devicebay', 'inventoryitem', 'aggregate', 'prefix', 'ipaddress', 'vlan', 'vrf', 'service', 'secret', 'tenant', 'cluster', 'virtualmachine')}, related_name='webhooks', to='contenttypes.ContentType', verbose_name='Object types'), ), ] diff --git a/netbox/extras/models.py b/netbox/extras/models.py index 8b90bd5c2..a97eebd74 100644 --- a/netbox/extras/models.py +++ b/netbox/extras/models.py @@ -677,6 +677,11 @@ class ConfigContext(models.Model): related_name='+', blank=True ) + tenant_groups = models.ManyToManyField( + to='tenancy.TenantGroup', + related_name='+', + blank=True + ) tenants = models.ManyToManyField( to='tenancy.Tenant', related_name='+', diff --git a/netbox/extras/querysets.py b/netbox/extras/querysets.py index 4ee9630cb..bcc6f1e54 100644 --- a/netbox/extras/querysets.py +++ b/netbox/extras/querysets.py @@ -13,6 +13,9 @@ class ConfigContextQuerySet(QuerySet): # `device_role` for Device; `role` for VirtualMachine role = getattr(obj, 'device_role', None) or obj.role + # Get the group of the assigned tenant, if any + tenant_group = obj.tenant.group if obj.tenant else None + # Match against the directly assigned region as well as any parent regions. region = getattr(obj.site, 'region', None) if region: @@ -24,7 +27,8 @@ class ConfigContextQuerySet(QuerySet): Q(regions__in=regions) | Q(regions=None), Q(sites=obj.site) | Q(sites=None), Q(roles=role) | Q(roles=None), - Q(tenants=obj.tenant) | Q(tenants=None), Q(platforms=obj.platform) | Q(platforms=None), + Q(tenant_groups=tenant_group) | Q(tenant_groups=None), + Q(tenants=obj.tenant) | Q(tenants=None), is_active=True, ).order_by('weight', 'name') diff --git a/netbox/templates/extras/configcontext_edit.html b/netbox/templates/extras/configcontext_edit.html index 4b7e53044..7a3566a00 100644 --- a/netbox/templates/extras/configcontext_edit.html +++ b/netbox/templates/extras/configcontext_edit.html @@ -18,6 +18,7 @@ {% render_field form.sites %} {% render_field form.roles %} {% render_field form.platforms %} + {% render_field form.tenant_groups %} {% render_field form.tenants %}