X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=plstackapi%2Fcore%2Fadmin.py;h=a814c55c7801986b4b655c1f3cadb17a1eabd242;hb=5cd13204975f86b9968020e313a356626ab6cd16;hp=da2bea5700a61fcdc7da98037c86787efab744b8;hpb=bc9cecff655561a1739da95b63984262e1ea8bcf;p=plstackapi.git diff --git a/plstackapi/core/admin.py b/plstackapi/core/admin.py index da2bea5..a814c55 100644 --- a/plstackapi/core/admin.py +++ b/plstackapi/core/admin.py @@ -1,10 +1,16 @@ from plstackapi.core.models import Site from plstackapi.core.models import * +from plstackapi.openstack.driver import OpenStackDriver +from plstackapi.openstack.client import OpenStackClient + from django.contrib import admin +from django.contrib.auth.models import Group from django import forms - +from django.utils.safestring import mark_safe from django.contrib.auth.admin import UserAdmin from django.contrib.admin.widgets import FilteredSelectMultiple +from django.contrib.auth.forms import ReadOnlyPasswordHashField +from django.contrib.auth.signals import user_logged_in class ReadonlyTabularInline(admin.TabularInline): @@ -25,19 +31,57 @@ class ReadonlyTabularInline(admin.TabularInline): class SliverInline(admin.TabularInline): model = Sliver + fields = ['ip', 'name', 'slice', 'image', 'key', 'node', 'deploymentNetwork'] extra = 0 class SiteInline(admin.TabularInline): model = Site extra = 0 +class SliceInline(admin.TabularInline): + model = Slice + extra = 0 + +class UserInline(admin.TabularInline): + model = PLUser + extra = 0 + +class RoleInline(admin.TabularInline): + model = Role + extra = 0 + class NodeInline(admin.TabularInline): model = Node extra = 0 +class PlainTextWidget(forms.Widget): + def render(self, _name, value, attrs): + return mark_safe(value) if value is not None else '' + class PlanetStackBaseAdmin(admin.ModelAdmin): save_on_top = False +class OSModelAdmin(PlanetStackBaseAdmin): + """Attach client connection to openstack on delete() and save()""" + + def save_model(self, request, obj, form, change): + client = OpenStackClient(tenant=request.user.site.login_base, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.save() + + def delete_model(self, request, obj): + client = OpenStackClient(tenant=request.user.site.login_base, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.delete() + +class RoleAdmin(OSModelAdmin): + fieldsets = [ + ('Role', {'fields': ['role_type']}) + ] + list_display = ('role_type',) + class DeploymentNetworkAdminForm(forms.ModelForm): sites = forms.ModelMultipleChoiceField( queryset=Site.objects.all(), @@ -57,7 +101,6 @@ class DeploymentNetworkAdminForm(forms.ModelForm): def save(self, commit=True): deploymentNetwork = super(DeploymentNetworkAdminForm, self).save(commit=False) - if commit: deploymentNetwork.save() @@ -71,7 +114,18 @@ class DeploymentNetworkAdmin(PlanetStackBaseAdmin): form = DeploymentNetworkAdminForm inlines = [NodeInline,] -class SiteAdmin(admin.ModelAdmin): + def get_formsets(self, request, obj=None): + for inline in self.get_inline_instances(request, obj): + # hide MyInline in the add view + if obj is None: + continue + # give inline object access to driver and caller + client = OpenStackClient(tenant=request.user.site.login_base, **request.session.get('auth', {})) + inline.model.driver = OpenStackDriver(client=client) + inline.model.caller = request.user + yield inline.get_formset(request, obj) + +class SiteAdmin(OSModelAdmin): fieldsets = [ (None, {'fields': ['name', 'site_url', 'enabled', 'is_public', 'login_base']}), ('Location', {'fields': ['latitude', 'longitude']}), @@ -82,13 +136,112 @@ class SiteAdmin(admin.ModelAdmin): inlines = [NodeInline,] search_fields = ['name'] -class SliceAdmin(PlanetStackBaseAdmin): + def get_formsets(self, request, obj=None): + for inline in self.get_inline_instances(request, obj): + # hide MyInline in the add view + if obj is None: + continue + # give inline object access to driver and caller + client = OpenStackClient(tenant=request.user.site.login_base, **request.session.get('auth', {})) + inline.model.driver = OpenStackDriver(client=client) + inline.model.caller = request.user + yield inline.get_formset(request, obj) + +class SitePrivilegeAdmin(PlanetStackBaseAdmin): + fieldsets = [ + (None, {'fields': ['user', 'site', 'role']}) + ] + list_display = ('user', 'site', 'role') + + def save_model(self, request, obj, form, change): + # update openstack connection to use this site/tenant + client = OpenStackClient(tenant=obj.site.login_base, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.save() + + def delete_model(self, request, obj): + # update openstack connection to use this site/tenant + client = OpenStackClient(tenant=obj.site.login_base, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.delete() + +class KeyAdmin(OSModelAdmin): + fieldsets = [ + ('Key', {'fields': ['name', 'key', 'type', 'blacklisted', 'user']}) + ] + list_display = ['name', 'key', 'type', 'blacklisted', 'user'] + + def get_queryset(self, request): + # get keys user is allowed to see + qs = super(KeyAdmin, self).get_queryset(request) + if request.user.is_superuser: + return qs + # users can only see their own keys + return qs.filter(user=request.user) + + +class SliceAdmin(OSModelAdmin): fields = ['name', 'site', 'instantiation', 'description', 'slice_url'] list_display = ('name', 'site','slice_url', 'instantiation') inlines = [SliverInline] -class SubnetAdmin(admin.ModelAdmin): + def get_formsets(self, request, obj=None): + for inline in self.get_inline_instances(request, obj): + # hide MyInline in the add view + if obj is None: + continue + # give inline object access to driver and caller + client = OpenStackClient(tenant=obj.name, **request.session.get('auth', {})) + inline.model.driver = OpenStackDriver(client=client) + inline.model.caller = request.user + yield inline.get_formset(request, obj) + + def get_queryset(self, request): + qs = super(SliceAdmin, self).get_queryset(request) + if request.user.is_superuser: + return qs + # users can only see slices at their site + return qs.filter(site=request.user.site) + +class SliceMembershipAdmin(PlanetStackBaseAdmin): + fieldsets = [ + (None, {'fields': ['user', 'slice', 'role']}) + ] + list_display = ('user', 'slice', 'role') + + def save_model(self, request, obj, form, change): + # update openstack connection to use this slice/tenant + client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.save() + + def delete_model(self, request, obj): + # update openstack connection to use this slice/tenant + client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.delete() + +class SubnetAdmin(PlanetStackBaseAdmin): fields = ['cidr', 'ip_version', 'start', 'end', 'slice'] + list_display = ('slice','cidr', 'start', 'end', 'ip_version') + + def save_model(self, request, obj, form, change): + # update openstack connection to use this subnet's slice/tenant + client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.save() + + def delete_model(self, request, obj): + # update openstack connection to use this subnet's slice/tenant + client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.delete() class ImageAdmin(admin.ModelAdmin): fields = ['image_id', 'name', 'disk_format', 'container_format'] @@ -98,23 +251,131 @@ class NodeAdmin(admin.ModelAdmin): list_filter = ('deploymentNetwork',) -class RoleAdmin(admin.ModelAdmin): +class SliverForm(forms.ModelForm): + class Meta: + ip = forms.CharField(widget=PlainTextWidget) + model = Sliver + widgets = { + 'ip': PlainTextWidget(), + } + +class SliverAdmin(PlanetStackBaseAdmin): + form = SliverForm fieldsets = [ - ('Role', {'fields': ['role_type']}) + ('Sliver', {'fields': ['ip', 'name', 'slice', 'image', 'key', 'node', 'deploymentNetwork']}) ] - list_display = ('role_type',) + list_display = ['ip', 'name', 'slice', 'image', 'key', 'node', 'deploymentNetwork'] + + def save_model(self, request, obj, form, change): + # update openstack connection to use this sliver's slice/tenant + client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.save() + + def delete_model(self, request, obj): + # update openstack connection to use this sliver's slice/tenant + client = OpenStackClient(tenant=obj.slice.name, **request.session.get('auth', {})) + obj.driver = OpenStackDriver(client=client) + obj.caller = request.user + obj.delete() + + +class UserCreationForm(forms.ModelForm): + """A form for creating new users. Includes all the required + fields, plus a repeated password.""" + password1 = forms.CharField(label='Password', widget=forms.PasswordInput) + password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput) + + class Meta: + model = PLUser + fields = ('email', 'firstname', 'lastname', 'phone', 'site') + + def clean_password2(self): + # Check that the two password entries match + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + raise forms.ValidationError("Passwords don't match") + return password2 + + def save(self, commit=True): + # Save the provided password in hashed format + user = super(UserCreationForm, self).save(commit=False) + user.password = self.cleaned_data["password1"] + #user.set_password(self.cleaned_data["password1"]) + if commit: + user.save() + return user + + +class UserChangeForm(forms.ModelForm): + """A form for updating users. Includes all the fields on + the user, but replaces the password field with admin's + password hash display field. + """ + password = ReadOnlyPasswordHashField() + + class Meta: + model = PLUser + + def clean_password(self): + # Regardless of what the user provides, return the initial value. + # This is done here, rather than on the field, because the + # field does not have access to the initial value + return self.initial["password"] + + +class PLUserAdmin(UserAdmin, OSModelAdmin): + class Meta: + app_label = "core" + + # The forms to add and change user instances + form = UserChangeForm + add_form = UserCreationForm + + # The fields to be used in displaying the User model. + # These override the definitions on the base UserAdmin + # that reference specific fields on auth.User. + list_display = ('email', 'site', 'firstname', 'lastname', 'last_login') + list_filter = ('site',) + fieldsets = ( + (None, {'fields': ('email', 'password')}), + ('Personal info', {'fields': ('firstname','lastname','phone','site')}), + #('Important dates', {'fields': ('last_login',)}), + ) + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('email', 'firstname', 'lastname', 'phone', 'site', 'password1', 'password2')} + ), + ) + search_fields = ('email',) + ordering = ('email',) + filter_horizontal = () + +# register a signal that caches the user's credentials when they log in +def cache_credentials(sender, user, request, **kwds): + auth = {'username': request.POST['username'], + 'password': request.POST['password']} + request.session['auth'] = auth +user_logged_in.connect(cache_credentials) + +# Now register the new UserAdmin... +admin.site.register(PLUser, PLUserAdmin) +# ... and, since we're not using Django's builtin permissions, +# unregister the Group model from admin. +admin.site.unregister(Group) admin.site.register(Site, SiteAdmin) -admin.site.register(SitePrivilege) +admin.site.register(SitePrivilege, SitePrivilegeAdmin) admin.site.register(Slice, SliceAdmin) -admin.site.register(SliceMembership) +admin.site.register(SliceMembership, SliceMembershipAdmin) admin.site.register(Subnet, SubnetAdmin) admin.site.register(Image, ImageAdmin) admin.site.register(Node, NodeAdmin) -admin.site.register(Sliver) -admin.site.register(Flavor) -admin.site.register(Key) +admin.site.register(Sliver, SliverAdmin) +admin.site.register(Key, KeyAdmin) admin.site.register(Role, RoleAdmin) -admin.site.register(User) admin.site.register(DeploymentNetwork, DeploymentNetworkAdmin)