fix VolumeAdmin not obeying readonly user bit
[plstackapi.git] / planetstack / syndicate / admin.py
1 from django.contrib import admin
2
3 from syndicate.models import *
4 from django import forms
5 from django.utils.safestring import mark_safe
6 from django.contrib.auth.admin import UserAdmin
7 from django.contrib.admin.widgets import FilteredSelectMultiple
8 from django.contrib.auth.forms import ReadOnlyPasswordHashField
9 from django.contrib.auth.signals import user_logged_in
10 from django.utils import timezone
11 from django.contrib.contenttypes import generic
12 from suit.widgets import LinkedSelect
13 from core.admin import ReadOnlyTabularInline,ReadOnlyAwareAdmin,SingletonAdmin,SliceInline,ServiceAttrAsTabInline,PlanetStackBaseAdmin, PlStackTabularInline,SliceROInline,ServiceAttrAsTabROInline
14 from suit.widgets import LinkedSelect
15 from bitfield import BitField
16 from bitfield.forms import BitFieldCheckboxSelectMultiple
17 from django.core.exceptions import ValidationError, ObjectDoesNotExist
18
19 class SyndicateServiceAdmin(SingletonAdmin,ReadOnlyAwareAdmin):
20     model = SyndicateService
21     verbose_name = "Syndicate Service"
22     verbose_name_plural = "Syndicate Service"
23     list_display = ("name","enabled")
24     fieldsets = [(None, {'fields': ['name','enabled','versionNumber', 'description',], 'classes':['suit-tab suit-tab-general']})]
25     inlines = [SliceInline,ServiceAttrAsTabInline]
26
27     user_readonly_fields = ['name','enabled','versionNumber','description']
28     user_readonly_inlines = [SliceROInline, ServiceAttrAsTabROInline]
29
30     suit_form_tabs =(('general', 'Syndicate Service Details'),
31         ('slices','Slices'),
32         ('serviceattrs','Additional Attributes'),
33     )
34
35
36 class VolumeAccessRightForUserROInline(ReadOnlyTabularInline):
37     model = VolumeAccessRight
38     extra = 0
39     suit_classes = 'suit-tab suit-tab-volumeAccessRights'
40     fields = ['volume','gateway_caps']
41
42 class VolumeAccessRightROInline(ReadOnlyTabularInline):
43     model = VolumeAccessRight
44     extra = 0
45     suit_classes = 'suit-tab suit-tab-volumeAccessRights'
46     fields = ['owner_id','gateway_caps']
47
48 class VolumeAccessRightInline(PlStackTabularInline):
49     model = VolumeAccessRight
50     extra = 0
51     suit_classes = 'suit-tab suit-tab-volumeAccessRights'
52     formfield_overrides = {
53         BitField: {'widget': BitFieldCheckboxSelectMultiple}
54     }
55     fields = ('owner_id', 'gateway_caps')
56
57 class VolumeInline(PlStackTabularInline):
58     model = Volume
59     extra = 0
60     suit_classes = 'suit-tab suit-tab-volumes'
61     fields = ['name', 'owner_id']
62
63 class VolumeROInline(ReadOnlyTabularInline):
64     model = Volume
65     extra = 0
66     suit_classes = 'suit-tab suit-tab-volumes'
67     fields = ['name', 'owner_id']
68
69
70 class VolumeSliceFormSet( forms.models.BaseInlineFormSet ):
71     # verify that our VolumeSlice is valid
72
73     @classmethod
74     def verify_unchanged( cls, volume_pk, slice_pk, field_name, new_value ):
75         vs = None
76         try:
77            vs = VolumeSlice.objects.get( volume_id=volume_pk, slice_id=slice_pk )
78         except ObjectDoesNotExist, dne:
79            return True, None
80
81         old_value = getattr( vs, field_name )
82         if old_value != new_value:
83             return False, old_value
84         else:
85             return True, None
86
87
88     def clean( self ):
89         for form in self.forms:
90             # check each inline's cleaned data, if it's valid
91             cleaned_data = None
92             try:
93                 if form.cleaned_data:
94                     cleaned_data = form.cleaned_data
95             except AttributeError:
96                 continue
97
98             # verify that the ports haven't changed 
99             volume_pk = cleaned_data['volume_id'].pk
100             slice_pk = cleaned_data['slice_id'].pk
101            
102             if not cleaned_data.has_key('peer_portnum'):
103                 raise ValidationError("Missing client peer-to-peer cache port number")
104
105             if not cleaned_data.has_key('replicate_portnum'):
106                 raise ValidationError("Missing replication service port number")
107
108             rc1, old_peer_port = VolumeSliceFormSet.verify_unchanged( volume_pk, slice_pk, 'peer_portnum', cleaned_data['peer_portnum'] )
109             rc2, old_replicate_port = VolumeSliceFormSet.verify_unchanged( volume_pk, slice_pk, 'replicate_portnum', cleaned_data['replicate_portnum'] )
110
111             err1str = ""
112             err2str = ""
113             if not rc1:
114                 err1str = "change %s back to %s" % (cleaned_data['peer_portnum'], old_peer_port)
115             if not rc2:
116                 err2str = " and change %s back to %s" % (cleaned_data['replicate_portnum'], old_replicate_port )
117
118             if not rc1 or not rc2:
119                 raise ValidationError("Port numbers cannot be changed once they are set. Please %s %s" % (err1str, err2str))
120             
121             
122
123 class VolumeSliceInline(PlStackTabularInline):
124     model = VolumeSlice
125     extra = 0
126     suit_classes = 'suit-tab suit-tab-volumeSlices'
127     fields = ['volume_id', 'slice_id', 'gateway_caps', 'peer_portnum', 'replicate_portnum']
128     formfield_overrides = { BitField: {'widget': BitFieldCheckboxSelectMultiple},}
129
130     formset = VolumeSliceFormSet
131     
132     readonly_fields = ['credentials_blob']
133  
134
135 class VolumeSliceROInline(ReadOnlyTabularInline):
136     model = VolumeSlice
137     extra = 0
138     suit_classes = 'suit-tab suit-tab-volumeSlices'
139     fields = ['volume_id', 'slice_id', 'gateway_caps', 'peer_portnum', 'replicate_portnum']
140     formfield_overrides = { BitField: {'widget': BitFieldCheckboxSelectMultiple},}
141
142     formset = VolumeSliceFormSet
143
144     readonly_fields = ['credentials_blob']
145
146
147 class VolumeAdmin(ReadOnlyAwareAdmin):
148     model = Volume
149    
150     def get_readonly_fields(self, request, obj=None ):
151        always_readonly = list(super(VolumeAdmin, self).get_readonly_fields(request, obj))
152        if obj == None:
153           # all fields are editable on add
154           return always_readonly
155
156        else:
157           # can't change owner, slice id, or block size on update
158           return ['blocksize', 'owner_id'] + always_readonly
159
160
161     list_display = ['name', 'owner_id']
162
163     formfield_overrides = { BitField: {'widget': BitFieldCheckboxSelectMultiple},}
164
165     #detailsFieldList = ['name', 'owner_id', 'description','file_quota','blocksize', 'private','archive', 'default_gateway_caps' ]
166     detailsFieldList = ['name', 'owner_id', 'description','blocksize', 'private','archive', 'default_gateway_caps' ]
167     
168     fieldsets = [
169         (None, {'fields': detailsFieldList, 'classes':['suit-tab suit-tab-general']}),
170         #(None, {'fields': keyList, 'classes':['suit-tab suit-tab-volumeKeys']}),
171     ]
172
173     inlines = [VolumeAccessRightInline, VolumeSliceInline]
174
175     user_readonly_fields = ['name','owner_id','description','blocksize','private','default_gateway_caps']
176     
177     user_readonly_inlines = [VolumeAccessRightROInline, VolumeSliceROInline]
178
179     suit_form_tabs =(('general', 'Volume Details'),
180                      #('volumeKeys', 'Access Keys'),
181                      ('volumeSlices', 'Slices'),
182                      ('volumeAccessRights', 'Volume Access Rights'),
183     )
184     
185
186 # left panel:
187 admin.site.register(SyndicateService, SyndicateServiceAdmin)
188 admin.site.register(Volume, VolumeAdmin)