Limit cloud providers per organization origin

ref #38
This commit is contained in:
Tobias Kunze 2025-10-22 11:12:18 +02:00
parent 714cd9be54
commit b4d239a1a6
6 changed files with 57 additions and 21 deletions

View file

@ -63,6 +63,7 @@ class OrganizationAdmin(admin.ModelAdmin):
search_fields = ("name", "namespace")
autocomplete_fields = ("billing_entity", "origin")
inlines = (OrganizationMembershipInline,)
filter_horizontal = ("limit_osb_services",)
def get_readonly_fields(self, request, obj=None):
readonly_fields = list(super().get_readonly_fields(request, obj) or [])
@ -85,6 +86,7 @@ class OrganizationOriginAdmin(admin.ModelAdmin):
list_display = ("name", "billing_entity")
search_fields = ("name",)
autocomplete_fields = ("billing_entity",)
filter_horizontal = ("limit_cloudproviders",)
@admin.register(OrganizationMembership)

View file

@ -10,16 +10,6 @@ class Migration(migrations.Migration):
]
operations = [
migrations.AddField(
model_name="organization",
name="limit_cloudproviders",
field=models.ManyToManyField(
blank=True,
related_name="+",
to="core.cloudprovider",
verbose_name="Limit to these Cloud providers",
),
),
migrations.AddField(
model_name="organization",
name="limit_osb_services",

View file

@ -0,0 +1,24 @@
# Generated by Django 5.2.7 on 2025-10-21 16:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0014_servicedefinition_advanced_fields"),
]
operations = [
migrations.AddField(
model_name="organizationorigin",
name="limit_cloudproviders",
field=models.ManyToManyField(
blank=True,
help_text="If set, all organizations with this origin will be limited to these cloud providers.",
related_name="+",
to="core.cloudprovider",
verbose_name="Limit to these Cloud providers",
),
),
]

View file

@ -52,12 +52,6 @@ class Organization(ServalaModelMixin, models.Model):
related_name="organizations",
verbose_name=_("Members"),
)
limit_cloudproviders = models.ManyToManyField(
to="CloudProvider",
related_name="+",
verbose_name=_("Limit to these Cloud providers"),
blank=True,
)
limit_osb_services = models.ManyToManyField(
to="Service",
related_name="+",
@ -99,6 +93,14 @@ class Organization(ServalaModelMixin, models.Model):
def has_inherited_billing_entity(self):
return self.origin and self.billing_entity == self.origin.billing_entity
@property
def limit_cloudproviders(self):
if self.origin:
return self.origin.limit_cloudproviders.all()
from servala.core.models import CloudProvider
return CloudProvider.objects.none()
def set_owner(self, user):
with scopes_disabled():
OrganizationMembership.objects.filter(user=user, organization=self).delete()
@ -161,9 +163,8 @@ class Organization(ServalaModelMixin, models.Model):
if self.limit_osb_services.exists():
queryset = self.limit_osb_services.all()
if self.limit_cloudproviders.exists():
allowed_providers = self.limit_cloudproviders.all()
queryset = queryset.filter(
offerings__provider__in=allowed_providers
offerings__provider__in=self.limit_cloudproviders
).distinct()
return queryset.prefetch_related(
"offerings", "offerings__provider"
@ -177,9 +178,8 @@ class Organization(ServalaModelMixin, models.Model):
queryset = Service.objects.select_related("category")
if self.limit_cloudproviders.exists():
allowed_providers = self.limit_cloudproviders.all()
queryset = queryset.filter(
offerings__provider__in=allowed_providers
offerings__provider__in=self.limit_cloudproviders
).distinct()
queryset = queryset.exclude(id__in=self.limit_osb_services.all())
return queryset.prefetch_related("offerings", "offerings__provider")
@ -376,6 +376,15 @@ class OrganizationOrigin(ServalaModelMixin, models.Model):
),
null=True,
)
limit_cloudproviders = models.ManyToManyField(
to="CloudProvider",
related_name="+",
verbose_name=_("Limit to these Cloud providers"),
blank=True,
help_text=_(
"If set, all organizations with this origin will be limited to these cloud providers."
),
)
class Meta:
verbose_name = _("Organization origin")

View file

@ -21,6 +21,15 @@ class ServiceFilterForm(forms.Form):
)
q = forms.CharField(label=_("Search"), required=False)
def __init__(self, *args, organization=None, **kwargs):
super().__init__(*args, **kwargs)
if organization and organization.limit_cloudproviders.exists():
allowed_providers = organization.limit_cloudproviders
if allowed_providers.count() <= 1:
self.fields.pop("cloud_provider", None)
else:
self.fields["cloud_provider"].queryset = allowed_providers
def filter_queryset(self, queryset):
if category := self.cleaned_data.get("category"):
queryset = queryset.filter(category=category)

View file

@ -44,7 +44,9 @@ class ServiceListView(OrganizationViewMixin, ListView):
@cached_property
def filter_form(self):
return ServiceFilterForm(data=self.request.GET or None)
return ServiceFilterForm(
data=self.request.GET or None, organization=self.request.organization
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)