Updated syndicate admin and models:
[plstackapi.git] / planetstack / syndicate_storage / admin.py
diff --git a/planetstack/syndicate_storage/admin.py b/planetstack/syndicate_storage/admin.py
new file mode 100644 (file)
index 0000000..418ba2b
--- /dev/null
@@ -0,0 +1,166 @@
+from django.contrib import admin
+
+from syndicate_storage.models import *
+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
+from django.utils import timezone
+from django.contrib.contenttypes import generic
+from suit.widgets import LinkedSelect
+from core.admin import ReadOnlyTabularInline,ReadOnlyAwareAdmin,SingletonAdmin,SliceInline,ServiceAttrAsTabInline,PlanetStackBaseAdmin, PlStackTabularInline,SliceROInline,ServiceAttrAsTabROInline
+from suit.widgets import LinkedSelect
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
+
+class SyndicateServiceAdmin(SingletonAdmin,ReadOnlyAwareAdmin):
+    model = SyndicateService
+    verbose_name = "Syndicate Storage"
+    verbose_name_plural = "Syndicate Storage"
+    list_display = ("name","enabled")
+    fieldsets = [(None, {'fields': ['name','enabled','versionNumber', 'description',], 'classes':['suit-tab suit-tab-general']})]
+    inlines = [SliceInline,ServiceAttrAsTabInline]
+
+    user_readonly_fields = ['name','enabled','versionNumber','description']
+    user_readonly_inlines = [SliceROInline, ServiceAttrAsTabROInline]
+
+    suit_form_tabs =(('general', 'Syndicate Storage Details'),
+        ('slices','Slices'),
+        ('serviceattrs','Additional Attributes'),
+    )
+
+
+class VolumeAccessRightForUserROInline(ReadOnlyTabularInline):
+    model = VolumeAccessRight
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-volumeAccessRights'
+    fields = ['volume','cap_read_data', 'cap_write_data', 'cap_host_data']
+
+class VolumeAccessRightROInline(ReadOnlyTabularInline):
+    model = VolumeAccessRight
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-volumeAccessRights'
+    fields = ['owner_id','cap_read_data', 'cap_write_data', 'cap_host_data']
+
+class VolumeAccessRightInline(PlStackTabularInline):
+    model = VolumeAccessRight
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-volumeAccessRights'
+
+
+class VolumeSliceFormSet( forms.models.BaseInlineFormSet ):
+    # verify that our VolumeSlice is valid
+
+    @classmethod
+    def verify_unchanged( cls, volume_pk, slice_pk, field_name, new_value ):
+        vs = None
+        try:
+           vs = VolumeSlice.objects.get( volume_id=volume_pk, slice_id=slice_pk )
+        except ObjectDoesNotExist, dne:
+           return True, None
+
+        old_value = getattr( vs, field_name )
+        if old_value != new_value:
+            return False, old_value
+        else:
+            return True, None
+
+
+    def clean( self ):
+        for form in self.forms:
+            # check each inline's cleaned data, if it's valid
+            cleaned_data = None
+            try:
+                if form.cleaned_data:
+                    cleaned_data = form.cleaned_data
+            except AttributeError:
+                continue
+             
+            # verify that the ports haven't changed 
+            volume_pk = cleaned_data['volume_id'].pk
+            slice_pk = cleaned_data['slice_id'].pk
+           
+            if not cleaned_data.has_key('UG_portnum'):
+                raise ValidationError("Missing UG port number")
+
+            if not cleaned_data.has_key('RG_portnum'):
+                raise ValidationError("Missing RG port number")
+
+            rc1, old_peer_port = VolumeSliceFormSet.verify_unchanged( volume_pk, slice_pk, 'UG_portnum', cleaned_data['UG_portnum'] )
+            rc2, old_replicate_port = VolumeSliceFormSet.verify_unchanged( volume_pk, slice_pk, 'RG_portnum', cleaned_data['RG_portnum'] )
+
+            err1str = ""
+            err2str = ""
+            if not rc1:
+                err1str = "change %s back to %s" % (cleaned_data['UG_portnum'], old_peer_port)
+            if not rc2:
+                err2str = " and change %s back to %s" % (cleaned_data['RG_portnum'], old_replicate_port )
+
+            if not rc1 or not rc2:
+                raise ValidationError("At this time, port numbers cannot be changed once they are set. Please %s %s" % (err1str, err2str))
+            
+            
+
+class VolumeSliceInline(PlStackTabularInline):
+    model = VolumeSlice
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-volumeSlices'
+    fields = ['volume_id', 'slice_id', 'cap_read_data', 'cap_write_data', 'cap_host_data', 'UG_portnum', 'RG_portnum']
+
+    formset = VolumeSliceFormSet
+    
+    readonly_fields = ['credentials_blob']
+
+class VolumeSliceROInline(ReadOnlyTabularInline):
+    model = VolumeSlice
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-volumeSlices'
+    fields = ['volume_id', 'slice_id', 'cap_read_data', 'cap_write_data', 'cap_host_data', 'UG_portnum', 'RG_portnum']
+
+    formset = VolumeSliceFormSet
+
+    readonly_fields = ['credentials_blob']
+    
+
+class VolumeAdmin(ReadOnlyAwareAdmin):
+    model = Volume
+   
+    def get_readonly_fields(self, request, obj=None ):
+       always_readonly = []
+       if obj == None:
+          # all fields are editable on add
+          return always_readonly
+
+       else:
+          # can't change owner, slice id, or block size on update
+          return ['blocksize', 'owner_id'] + always_readonly
+
+
+    list_display = ['name', 'owner_id']
+
+    detailsFieldList = ['name', 'owner_id', 'description','blocksize', 'private','archive', 'cap_read_data', 'cap_write_data', 'cap_host_data' ]
+
+    fieldsets = [
+        (None, {'fields': detailsFieldList, 'classes':['suit-tab suit-tab-general']}),
+    ]
+
+    inlines = [VolumeAccessRightInline, VolumeSliceInline]
+
+    user_readonly_fields = ['name','owner_id','description','blocksize','private', 'archive', 'cap_read_data', 'cap_write_data', 'cap_host_data']
+    
+    user_readonly_inlines = [VolumeAccessRightROInline, VolumeSliceROInline]
+
+    suit_form_tabs =(('general', 'Volume Details'),
+                     ('volumeSlices', 'Slices'),
+                     ('volumeAccessRights', 'Volume Access Rights'))
+    
+    def queryset(self, request):
+       # only show volumes that are public, or owned by the caller 
+       return Volume.select_by_user(request.user)
+    
+    
+# left panel:
+admin.site.register(SyndicateService, SyndicateServiceAdmin)
+admin.site.register(Volume, VolumeAdmin)