blob: cd0e5dd99efe3d568ae064776a1812f1deee2d97 [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Siobhan Tully4bc09f22013-04-10 21:15:21 -040010from django.contrib.admin.widgets import FilteredSelectMultiple
Scott Baker1a6a3902014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Scott Bakere2bbf7e2014-01-13 12:09:31 -080017from django.core.urlresolvers import reverse, NoReverseMatch
Scott Baker92d22172014-10-21 21:03:08 -070018from cgi import escape as html_escape
Tony Mack7130ac32013-03-22 21:58:00 -040019
Scott Baker36f50872014-08-21 13:01:25 -070020import django_evolution
Scott Baker6a995352014-10-06 17:51:20 -070021import threading
22
23# thread locals necessary to work around a django-suit issue
24_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070025
Scott Baker40c00762014-08-21 16:55:59 -070026def backend_icon(obj): # backend_status, enacted, updated):
27 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
28 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Bakerb171e522014-09-09 10:38:15 -070029 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
Scott Baker40c00762014-08-21 16:55:59 -070030 else:
31 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
Scott Bakerb171e522014-09-09 10:38:15 -070032 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
Scott Baker63d1a552014-08-21 15:19:07 -070033 else:
Scott Baker92d22172014-10-21 21:03:08 -070034 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % html_escape(obj.backend_status, quote=True)
Scott Baker40c00762014-08-21 16:55:59 -070035
36def backend_text(obj):
37 icon = backend_icon(obj)
38 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Baker92d22172014-10-21 21:03:08 -070039 return "%s %s" % (icon, "successfully enacted")
Scott Baker40c00762014-08-21 16:55:59 -070040 else:
Scott Baker92d22172014-10-21 21:03:08 -070041 return "%s %s" % (icon, html_escape(obj.backend_status, quote=True))
Scott Baker63d1a552014-08-21 15:19:07 -070042
Scott Baker36f50872014-08-21 13:01:25 -070043class PlainTextWidget(forms.HiddenInput):
44 input_type = 'hidden'
45
46 def render(self, name, value, attrs=None):
47 if value is None:
48 value = ''
49 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
50
Scott Bakerf4aeedc2014-10-03 13:10:47 -070051class PermissionCheckingAdminMixin(object):
Scott Baker1a6a3902014-10-03 00:32:37 -070052 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050053
54 def has_add_permission(self, request, obj=None):
55 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070056
Siobhan Tullycf04fb62014-01-11 11:25:57 -050057 def has_delete_permission(self, request, obj=None):
58 return (not self.__user_is_readonly(request))
59
60 def save_model(self, request, obj, form, change):
61 if self.__user_is_readonly(request):
Scott Baker1a6a3902014-10-03 00:32:37 -070062 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050063 raise PermissionDenied
Scott Baker1a6a3902014-10-03 00:32:37 -070064
65 obj.caller = request.user
66 # update openstack connection to use this site/tenant
67 obj.save_by_user(request.user)
68
69 def delete_model(self, request, obj):
70 obj.delete_by_user(request.user)
71
72 def save_formset(self, request, form, formset, change):
73 instances = formset.save(commit=False)
74 for instance in instances:
75 instance.save_by_user(request.user)
76
77 # BUG in django 1.7? Objects are not deleted by formset.save if
78 # commit is False. So let's delete them ourselves.
79 #
80 # code from forms/models.py save_existing_objects()
81 try:
82 forms_to_delete = formset.deleted_forms
83 except AttributeError:
84 forms_to_delete = []
85 if formset.initial_forms:
86 for form in formset.initial_forms:
87 obj = form.instance
88 if form in forms_to_delete:
89 if obj.pk is None:
90 continue
91 formset.deleted_objects.append(obj)
92 obj.delete()
93
94 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -050095
96 def get_actions(self,request):
Scott Bakerf4aeedc2014-10-03 13:10:47 -070097 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -050098
99 if self.__user_is_readonly(request):
100 if 'delete_selected' in actions:
101 del actions['delete_selected']
102
103 return actions
104
105 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500106 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -0700107 if not hasattr(self, "readonly_save"):
108 # save the original readonly fields
109 self.readonly_save = self.readonly_fields
110 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -0700111 if hasattr(self, "user_readonly_fields"):
112 self.readonly_fields=self.user_readonly_fields
113 if hasattr(self, "user_readonly_inlines"):
114 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -0700115 else:
116 if hasattr(self, "readonly_save"):
117 # restore the original readonly fields
118 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -0700119 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700120 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500121
122 try:
Scott Bakerf4aeedc2014-10-03 13:10:47 -0700123 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500124 except PermissionDenied:
125 pass
126 if request.method == 'POST':
127 raise PermissionDenied
128 request.readonly = True
Scott Bakerf4aeedc2014-10-03 13:10:47 -0700129 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500130
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500131 def __user_is_readonly(self, request):
132 return request.user.isReadOnlyUser()
133
Scott Baker40c00762014-08-21 16:55:59 -0700134 def backend_status_text(self, obj):
135 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700136
Scott Baker63d1a552014-08-21 15:19:07 -0700137 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700138 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700139 backend_status_icon.short_description = ""
140
Scott Bakerdc4724c2014-11-05 09:05:38 -0800141 def get_form(self, request, obj=None, **kwargs):
Scott Baker9b3c1af2014-10-16 00:57:55 -0700142 # Save obj and request in thread-local storage, so suit_form_tabs can
143 # use it to determine whether we're in edit or add mode, and can
144 # determine whether the user is an admin.
145 _thread_locals.request = request
146 _thread_locals.obj = obj
Scott Bakerdc4724c2014-11-05 09:05:38 -0800147 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700148
149 def get_inline_instances(self, request, obj=None):
150 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
151
152 # inlines that should only be shown to an admin user
153 if request.user.is_admin:
154 for inline_class in getattr(self, "admin_inlines", []):
155 inlines.append(inline_class(self.model, self.admin_site))
156
157 return inlines
158
Scott Bakerf4aeedc2014-10-03 13:10:47 -0700159class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
160 # Note: Make sure PermissionCheckingAdminMixin is listed before
161 # admin.ModelAdmin in the class declaration.
162
Scott Baker1a6a3902014-10-03 00:32:37 -0700163 pass
164
165class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
166 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700167
Scott Bakere8859f92014-05-23 12:42:40 -0700168class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400169 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700170 if not super(SingletonAdmin, self).has_add_permission(request):
171 return False
172
Siobhan Tullyce652d02013-10-08 21:52:35 -0400173 num_objects = self.model.objects.count()
174 if num_objects >= 1:
175 return False
176 else:
177 return True
178
Siobhan Tullyd3515752013-06-21 16:34:53 -0400179class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800180 def __init__(self, *args, **kwargs):
181 super(PlStackTabularInline, self).__init__(*args, **kwargs)
182
183 # InlineModelAdmin as no get_fields() method, so in order to add
184 # the selflink field, we override __init__ to modify self.fields and
185 # self.readonly_fields.
186
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800187 self.setup_selflink()
188
Scott Baker874936e2014-01-13 18:15:34 -0800189 def get_change_url(self, model, id):
190 """ Get the URL to a change form in the admin for this model """
191 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800192 try:
Scott Baker874936e2014-01-13 18:15:34 -0800193 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800194 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800195 return None
196
197 return url
198
199 def setup_selflink(self):
200 if hasattr(self, "selflink_fieldname"):
201 """ self.selflink_model can be defined to punch through a relation
202 to its target object. For example, in SliceNetworkInline, set
203 selflink_model = "network", and the URL will lead to the Network
204 object instead of trying to bring up a change view of the
205 SliceNetwork object.
206 """
207 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
208 else:
209 self.selflink_model = self.model
210
211 url = self.get_change_url(self.selflink_model, 0)
212
213 # We don't have an admin for this object, so don't create the
214 # selflink.
215 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800216 return
217
Scott Baker874936e2014-01-13 18:15:34 -0800218 # Since we need to add "selflink" to the field list, we need to create
219 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800220 if (self.fields is None):
221 self.fields = []
222 for f in self.model._meta.fields:
223 if f.editable and f.name != "id":
224 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800225
Scott Baker874936e2014-01-13 18:15:34 -0800226 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800227
Scott Baker874936e2014-01-13 18:15:34 -0800228 if self.readonly_fields is None:
229 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800230
Scott Baker874936e2014-01-13 18:15:34 -0800231 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800232
233 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800234 if hasattr(self, "selflink_fieldname"):
235 obj = getattr(obj, self.selflink_fieldname)
236
Scott Baker86568322014-01-12 16:53:31 -0800237 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800238 url = self.get_change_url(self.selflink_model, obj.id)
239 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800240 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800241 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800242
243 selflink.allow_tags = True
244 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400245
Scott Bakerb27b62c2014-08-15 16:29:16 -0700246 def has_add_permission(self, request):
247 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500248
249 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700250 readonly_fields = list(self.readonly_fields)[:]
251 if request.user.isReadOnlyUser():
252 for field in self.fields:
253 if not field in readonly_fields:
254 readonly_fields.append(field)
255 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500256
Scott Baker40c00762014-08-21 16:55:59 -0700257 def backend_status_icon(self, obj):
258 return mark_safe(backend_icon(obj))
259 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700260
Scott Bakerb27b62c2014-08-15 16:29:16 -0700261class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500262 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700263 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500264
Scott Bakerb27b62c2014-08-15 16:29:16 -0700265 def get_readonly_fields(self, request, obj=None):
266 readonly_fields = list(self.readonly_fields)[:]
267 if request.user.isReadOnlyUser():
268 for field in self.fields:
269 if not field in readonly_fields:
270 readonly_fields.append(field)
271 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500272
Scott Baker40c00762014-08-21 16:55:59 -0700273 def backend_status_icon(self, obj):
274 return mark_safe(backend_icon(obj))
275 backend_status_icon.short_description = ""
276
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400277class ReservationInline(PlStackTabularInline):
278 model = Reservation
279 extra = 0
280 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700281
Tony Mack5b061472014-02-04 07:57:10 -0500282 def queryset(self, request):
283 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400284
Scott Bakerb27b62c2014-08-15 16:29:16 -0700285class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400286 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400287 extra = 0
288 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500289 fields = ['service', 'name', 'value']
290
291 def queryset(self, request):
292 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400293
Scott Baker74d8e622013-07-29 16:04:22 -0700294class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400295 """ This is a callable that looks up a network name in a sliver and returns
296 the ip address for that network.
297 """
298
Scott Baker434ca7e2014-08-15 12:29:20 -0700299 byNetworkName = {} # class variable
300
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400301 def __init__(self, name):
302 self.short_description = name
303 self.__name__ = name
304 self.network_name = name
305
306 def __call__(self, obj):
307 if obj is not None:
308 for nbs in obj.networksliver_set.all():
309 if (nbs.network.name == self.network_name):
310 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700311 return ""
312
313 def __str__(self):
314 return self.network_name
315
Scott Baker434ca7e2014-08-15 12:29:20 -0700316 @staticmethod
317 def get(network_name):
318 """ We want to make sure we alwars return the same NetworkLookerUpper
319 because sometimes django will cause them to be instantiated multiple
320 times (and we don't want different ones in form.fields vs
321 SliverInline.readonly_fields).
322 """
323 if network_name not in NetworkLookerUpper.byNetworkName:
324 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
325 return NetworkLookerUpper.byNetworkName[network_name]
326
Siobhan Tullyd3515752013-06-21 16:34:53 -0400327class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400328 model = Sliver
Scott Baker7a61dc42014-09-02 17:08:20 -0700329 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deploymentNetwork', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400330 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700331 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400332 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700333
Tony Mack5b061472014-02-04 07:57:10 -0500334 def queryset(self, request):
335 return Sliver.select_by_user(request.user)
336
Scott Bakerb24cc932014-06-09 10:51:16 -0700337 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700338 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700339 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker7a61dc42014-09-02 17:08:20 -0700340 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Scott Baker4b6d9442014-09-08 12:14:14 -0700341 elif db_field.name == 'flavor':
342 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700343
344 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700345
346 return field
347
Siobhan Tullyd3515752013-06-21 16:34:53 -0400348class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400349 model = Site
350 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400351 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400352
Tony Mack5b061472014-02-04 07:57:10 -0500353 def queryset(self, request):
354 return Site.select_by_user(request.user)
355
Siobhan Tullyd3515752013-06-21 16:34:53 -0400356class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400357 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700358 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
359 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400360 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400361 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400362
Tony Mack5b061472014-02-04 07:57:10 -0500363 def queryset(self, request):
364 return User.select_by_user(request.user)
365
Siobhan Tullyd3515752013-06-21 16:34:53 -0400366class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400367 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700368 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
369 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400370 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400371 suit_classes = 'suit-tab suit-tab-slices'
372
Tony Mack5b061472014-02-04 07:57:10 -0500373 def queryset(self, request):
374 return Slice.select_by_user(request.user)
375
Siobhan Tullyd3515752013-06-21 16:34:53 -0400376class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400377 model = Node
378 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400379 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker40c00762014-08-21 16:55:59 -0700380 fields = ['backend_status_icon', 'name','deployment','site']
381 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400382
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400383class DeploymentPrivilegeInline(PlStackTabularInline):
384 model = DeploymentPrivilege
385 extra = 0
386 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700387 fields = ['backend_status_icon', 'user','role','deployment']
388 readonly_fields = ('backend_status_icon', )
Tony Mack5b061472014-02-04 07:57:10 -0500389
390 def queryset(self, request):
391 return DeploymentPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400392
Siobhan Tullyd3515752013-06-21 16:34:53 -0400393class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400394 model = SitePrivilege
395 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400396 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700397 fields = ['backend_status_icon', 'user','site', 'role']
398 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400399
Tony Mackc2835a92013-05-28 09:18:49 -0400400 def formfield_for_foreignkey(self, db_field, request, **kwargs):
401 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500402 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400403
404 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500405 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400406 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
407
Tony Mack5b061472014-02-04 07:57:10 -0500408 def queryset(self, request):
409 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400410
Tony Macke4be32f2014-03-11 20:45:25 -0400411class SiteDeploymentInline(PlStackTabularInline):
Sapan Bhatia3d872e52014-11-11 21:41:12 -0500412 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400413 extra = 0
414 suit_classes = 'suit-tab suit-tab-deployments'
Scott Baker40c00762014-08-21 16:55:59 -0700415 fields = ['backend_status_icon', 'deployment','site']
416 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400417
418 def formfield_for_foreignkey(self, db_field, request, **kwargs):
419 if db_field.name == 'site':
420 kwargs['queryset'] = Site.select_by_user(request.user)
421
422 if db_field.name == 'deployment':
423 kwargs['queryset'] = Deployment.select_by_user(request.user)
424 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
425
426 def queryset(self, request):
Sapan Bhatia3d872e52014-11-11 21:41:12 -0500427 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400428
429
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400430class SlicePrivilegeInline(PlStackTabularInline):
431 model = SlicePrivilege
432 suit_classes = 'suit-tab suit-tab-sliceprivileges'
433 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700434 fields = ('backend_status_icon', 'user', 'slice', 'role')
435 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400436
Tony Mackc2835a92013-05-28 09:18:49 -0400437 def formfield_for_foreignkey(self, db_field, request, **kwargs):
438 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700439 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400440 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700441 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400442
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400443 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400444
Tony Mack5b061472014-02-04 07:57:10 -0500445 def queryset(self, request):
446 return SlicePrivilege.select_by_user(request.user)
447
Scott Bakera0015eb2013-08-14 17:28:14 -0700448class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700449 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800450 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700451 extra = 0
452 verbose_name = "Network Connection"
453 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400454 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700455 fields = ['backend_status_icon', 'network']
456 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700457
Sapan Bhatiab19c22a2014-11-11 23:05:53 -0500458class ImageDeploymentInline(PlStackTabularInline):
459 model = ImageDeployment
Scott Baker2170b972014-06-03 12:14:07 -0700460 extra = 0
461 verbose_name = "Image Deployments"
462 verbose_name_plural = "Image Deployments"
463 suit_classes = 'suit-tab suit-tab-imagedeployments'
Scott Baker40c00762014-08-21 16:55:59 -0700464 fields = ['backend_status_icon', 'image', 'deployment', 'glance_image_id']
465 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700466
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400467class SliceRoleAdmin(PlanetStackBaseAdmin):
468 model = SliceRole
469 pass
470
471class SiteRoleAdmin(PlanetStackBaseAdmin):
472 model = SiteRole
473 pass
474
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400475class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400476 sites = forms.ModelMultipleChoiceField(
477 queryset=Site.objects.all(),
478 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700479 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400480 widget=FilteredSelectMultiple(
481 verbose_name=('Sites'), is_stacked=False
482 )
483 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700484 images = forms.ModelMultipleChoiceField(
485 queryset=Image.objects.all(),
486 required=False,
487 help_text="Select which images should be deployed on this deployment",
488 widget=FilteredSelectMultiple(
489 verbose_name=('Images'), is_stacked=False
490 )
491 )
Scott Baker37b47902014-09-02 14:37:41 -0700492 flavors = forms.ModelMultipleChoiceField(
493 queryset=Flavor.objects.all(),
494 required=False,
495 help_text="Select which flavors should be usable on this deployment",
496 widget=FilteredSelectMultiple(
497 verbose_name=('Flavors'), is_stacked=False
498 )
499 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400500 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400501 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700502 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400503
Siobhan Tully320b4622014-01-17 15:11:14 -0500504 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700505 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500506 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
507
Scott Baker5380c522014-06-06 14:49:43 -0700508 self.fields['accessControl'].initial = "allow site " + request.user.site.name
509
Siobhan Tully320b4622014-01-17 15:11:14 -0500510 if self.instance and self.instance.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700511 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments_set.all()]
Scott Bakerde0f4412014-06-11 15:40:26 -0700512 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments_set.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700513 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700514
515 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
516 """ helper function for handling m2m relations from the MultipleChoiceField
517
518 this_obj: the source object we want to link from
519
520 selected_objs: a list of destination objects we want to link to
521
522 all_relations: the full set of relations involving this_obj, including ones we don't want
523
524 relation_class: the class that implements the relation from source to dest
525
526 local_attrname: field name representing this_obj in relation_class
527
528 foreign_attrname: field name representing selected_objs in relation_class
529
530 This function will remove all newobjclass relations from this_obj
531 that are not contained in selected_objs, and add any relations that
532 are in selected_objs but don't exist in the data model yet.
533 """
534
535 existing_dest_objs = []
536 for relation in list(all_relations):
537 if getattr(relation, foreign_attrname) not in selected_objs:
538 #print "deleting site", sdp.site
539 relation.delete()
540 else:
541 existing_dest_objs.append(getattr(relation, foreign_attrname))
542
543 for dest_obj in selected_objs:
544 if dest_obj not in existing_dest_objs:
545 #print "adding site", site
546 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
547 relation = relation_class(**kwargs)
548 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500549
550 def save(self, commit=True):
551 deployment = super(DeploymentAdminForm, self).save(commit=False)
552
553 if commit:
554 deployment.save()
Scott Baker0057d052014-10-06 17:17:40 -0700555 # this has to be done after save() if/when a deployment is first created
556 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500557
558 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700559 # save_m2m() doesn't seem to work with 'through' relations. So we
560 # create/destroy the through models ourselves. There has to be
561 # a better way...
562
Sapan Bhatia3d872e52014-11-11 21:41:12 -0500563 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments_set.all(), SiteDeployment, "deployment", "site")
Sapan Bhatiab19c22a2014-11-11 23:05:53 -0500564 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments_set.all(), ImageDeployment, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700565
Scott Baker37b47902014-09-02 14:37:41 -0700566 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500567
568 return deployment
569
Scott Bakerff5e0f32014-05-22 14:40:27 -0700570class DeploymentAdminROForm(DeploymentAdminForm):
571 def save(self, commit=True):
572 raise PermissionDenied
573
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500574class SiteAssocInline(PlStackTabularInline):
575 model = Site.deployments.through
576 extra = 0
577 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400578
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400579class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500580 model = Deployment
Tony Macke75841e2014-09-29 16:10:52 -0400581 fieldList = ['backend_status_text', 'name', 'availability_zone', 'sites', 'images', 'flavors', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500582 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Sapan Bhatiab19c22a2014-11-11 23:05:53 -0500583 inlines = [DeploymentPrivilegeInline,NodeInline,TagInline] # ,ImageDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700584 list_display = ['backend_status_icon', 'name']
585 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700586 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500587
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500588 user_readonly_fields = ['name']
589
Scott Bakerde0f4412014-06-11 15:40:26 -0700590 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500591
Scott Bakerff5e0f32014-05-22 14:40:27 -0700592 def get_form(self, request, obj=None, **kwargs):
593 if request.user.isReadOnlyUser():
594 kwargs["form"] = DeploymentAdminROForm
595 else:
596 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700597 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
598
599 # from stackexchange: pass the request object into the form
600
601 class AdminFormMetaClass(adminForm):
602 def __new__(cls, *args, **kwargs):
603 kwargs['request'] = request
604 return adminForm(*args, **kwargs)
605
606 return AdminFormMetaClass
607
Siobhan Tullyce652d02013-10-08 21:52:35 -0400608class ServiceAttrAsTabInline(PlStackTabularInline):
609 model = ServiceAttribute
610 fields = ['name','value']
611 extra = 0
612 suit_classes = 'suit-tab suit-tab-serviceattrs'
613
Siobhan Tullyce652d02013-10-08 21:52:35 -0400614class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700615 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
616 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700617 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500618 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
619 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700620 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500621
622 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500623
624 suit_form_tabs =(('general', 'Service Details'),
625 ('slices','Slices'),
626 ('serviceattrs','Additional Attributes'),
627 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400628
Tony Mack0553f282013-06-10 22:54:50 -0400629class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700630 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400631 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500632 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400633 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400634 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400635 suit_form_tabs =(('general', 'Site Details'),
636 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400637 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400638 ('deployments','Deployments'),
639 ('slices','Slices'),
Scott Baker36f50872014-08-21 13:01:25 -0700640 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400641 ('tags','Tags'),
642 )
Scott Baker40c00762014-08-21 16:55:59 -0700643 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500644
645 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500646
Scott Baker63d1a552014-08-21 15:19:07 -0700647 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
648 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400649 filter_horizontal = ('deployments',)
Tony Macke4be32f2014-03-11 20:45:25 -0400650 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400651 search_fields = ['name']
652
Tony Mack04062832013-05-10 08:22:44 -0400653 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500654 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400655
Tony Mack5cd13202013-05-01 21:48:38 -0400656 def get_formsets(self, request, obj=None):
657 for inline in self.get_inline_instances(request, obj):
658 # hide MyInline in the add view
659 if obj is None:
660 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400661 if isinstance(inline, SliverInline):
662 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400663 yield inline.get_formset(request, obj)
664
Scott Baker545db2a2013-12-09 18:44:43 -0800665 def accountLink(self, obj):
666 link_obj = obj.accounts.all()
667 if link_obj:
668 reverse_path = "admin:core_account_change"
669 url = reverse(reverse_path, args =(link_obj[0].id,))
670 return "<a href='%s'>%s</a>" % (url, "view billing details")
671 else:
672 return "no billing data for this site"
673 accountLink.allow_tags = True
674 accountLink.short_description = "Billing"
675
Tony Mack332ee1d2014-02-04 15:33:45 -0500676 def save_model(self, request, obj, form, change):
677 # update openstack connection to use this site/tenant
678 obj.save_by_user(request.user)
679
680 def delete_model(self, request, obj):
681 obj.delete_by_user(request.user)
682
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500683
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400684class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700685 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400686 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500687 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400688 ]
Scott Baker40c00762014-08-21 16:55:59 -0700689 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700690 list_display = ('backend_status_icon', 'user', 'site', 'role')
691 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500692 user_readonly_fields = fieldList
693 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400694
Tony Mackc2835a92013-05-28 09:18:49 -0400695 def formfield_for_foreignkey(self, db_field, request, **kwargs):
696 if db_field.name == 'site':
697 if not request.user.is_admin:
698 # only show sites where user is an admin or pi
699 sites = set()
700 for site_privilege in SitePrivilege.objects.filer(user=request.user):
701 if site_privilege.role.role_type in ['admin', 'pi']:
702 sites.add(site_privilege.site)
703 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
704
705 if db_field.name == 'user':
706 if not request.user.is_admin:
707 # only show users from sites where caller has admin or pi role
708 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
709 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
710 sites = [site_privilege.site for site_privilege in site_privileges]
711 site_privileges = SitePrivilege.objects.filter(site__in=sites)
712 emails = [site_privilege.user.email for site_privilege in site_privileges]
713 users = User.objects.filter(email__in=emails)
714 kwargs['queryset'] = users
715
716 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
717
Tony Mack04062832013-05-10 08:22:44 -0400718 def queryset(self, request):
719 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400720 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400721 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500722 #if not request.user.is_admin:
723 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
724 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
725 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
726 # sites = Site.objects.filter(login_base__in=login_bases)
727 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400728 return qs
729
Siobhan Tullyce652d02013-10-08 21:52:35 -0400730class SliceForm(forms.ModelForm):
731 class Meta:
732 model = Slice
733 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700734 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400735 }
736
Tony Macke75841e2014-09-29 16:10:52 -0400737 def clean(self):
738 cleaned_data = super(SliceForm, self).clean()
739 name = cleaned_data.get('name')
Scott Baker3cb382c2014-10-06 23:09:59 -0700740 site = cleaned_data.get('site')
Tony Mackcc9e2592014-10-22 12:54:19 -0400741 slice_id = self.instance.id
742 if not site and slice_id:
743 site = Slice.objects.get(id=slice_id).site
Scott Baker3cb382c2014-10-06 23:09:59 -0700744 if (not isinstance(site,Site)):
745 # previous code indicates 'site' could be a site_id and not a site?
746 site = Slice.objects.get(id=site.id)
Tony Macke75841e2014-09-29 16:10:52 -0400747 if not name.startswith(site.login_base):
748 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
749 return cleaned_data
750
Sapan Bhatia18e01e32014-11-11 21:43:31 -0500751class SliceDeploymentInline(PlStackTabularInline):
752 model = SliceDeployment
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700753 extra = 0
754 verbose_name = "Slice Deployment"
755 verbose_name_plural = "Slice Deployments"
756 suit_classes = 'suit-tab suit-tab-admin-only'
757 fields = ['backend_status_icon', 'deployment', 'tenant_id']
758 readonly_fields = ('backend_status_icon', )
759
Tony Mack2bd5b412013-06-11 21:05:06 -0400760class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400761 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400762 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500763 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700764 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400765 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
766 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400767 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Sapan Bhatia18e01e32014-11-11 21:43:31 -0500768 admin_inlines = [SliceDeploymentInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400769
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500770 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400771
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700772 @property
773 def suit_form_tabs(self):
774 tabs =[('general', 'Slice Details'),
775 ('slicenetworks','Networks'),
776 ('sliceprivileges','Privileges'),
777 ('slivers','Slivers'),
778 ('tags','Tags'),
779 ('reservations','Reservations'),
780 ]
781
782 request=getattr(_thread_locals, "request", None)
783 if request and request.user.is_admin:
784 tabs.append( ('admin-only', 'Admin-Only') )
785
786 return tabs
Tony Mack0aa732a2014-10-22 11:54:29 -0400787
788 def add_view(self, request, form_url='', extra_context=None):
789 # revert to default read-only fields
790 self.readonly_fields = ('backend_status_text',)
791 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
792
793 def change_view(self, request, object_id, form_url='', extra_context=None):
794 print object_id
795 # cannot change the site of an existing slice so make the site field read only
796 if object_id:
797 self.readonly_fields = ('backend_status_text','site')
798 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400799
Scott Baker510fdbb2014-08-05 17:19:24 -0700800 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700801 deployment_nodes = []
802 for node in Node.objects.all():
803 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
804
Scott Baker7a61dc42014-09-02 17:08:20 -0700805 deployment_flavors = []
806 for flavor in Flavor.objects.all():
807 for deployment in flavor.deployments.all():
808 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
809
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700810 deployment_images = []
811 for image in Image.objects.all():
812 for imageDeployment in image.imagedeployments_set.all():
813 deployment_images.append( (imageDeployment.deployment.id, image.id, image.name) )
814
Tony Mackec23b992014-09-02 21:18:45 -0400815 site_login_bases = []
816 for site in Site.objects.all():
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700817 site_login_bases.append((site.id, site.login_base))
818
Scott Baker510fdbb2014-08-05 17:19:24 -0700819 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700820 context["deployment_flavors"] = deployment_flavors
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700821 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400822 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700823 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
824
Tony Mackc2835a92013-05-28 09:18:49 -0400825 def formfield_for_foreignkey(self, db_field, request, **kwargs):
826 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500827 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400828 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
Scott Baker40c00762014-08-21 16:55:59 -0700829
Tony Mackc2835a92013-05-28 09:18:49 -0400830 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
831
Tony Mack04062832013-05-10 08:22:44 -0400832 def queryset(self, request):
833 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500834 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400835
Tony Mack79748612013-05-01 14:52:03 -0400836 def get_formsets(self, request, obj=None):
837 for inline in self.get_inline_instances(request, obj):
838 # hide MyInline in the add view
839 if obj is None:
840 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400841 if isinstance(inline, SliverInline):
842 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400843 yield inline.get_formset(request, obj)
844
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400845class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400846 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700847 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400848 ]
Scott Baker40c00762014-08-21 16:55:59 -0700849 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700850 list_display = ('backend_status_icon', 'user', 'slice', 'role')
851 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400852
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500853 user_readonly_fields = ['user', 'slice', 'role']
854 user_readonly_inlines = []
855
Tony Mackc2835a92013-05-28 09:18:49 -0400856 def formfield_for_foreignkey(self, db_field, request, **kwargs):
857 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500858 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400859
860 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500861 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400862
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400863 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400864
Tony Mack04062832013-05-10 08:22:44 -0400865 def queryset(self, request):
866 # admins can see all memberships. Users can only see memberships of
867 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500868 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400869
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400870 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400871 # update openstack connection to use this site/tenant
872 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400873 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400874 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400875 obj.save()
876
877 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400878 # update openstack connection to use this site/tenant
879 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400880 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400881 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400882 obj.delete()
883
Siobhan Tully567e3e62013-06-21 18:03:16 -0400884
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400885class ImageAdmin(PlanetStackBaseAdmin):
886
Scott Baker36f50872014-08-21 13:01:25 -0700887 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -0700888 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400889 'classes': ['suit-tab suit-tab-general']})
890 ]
Scott Baker40c00762014-08-21 16:55:59 -0700891 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400892
Scott Baker2170b972014-06-03 12:14:07 -0700893 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400894
Sapan Bhatiab19c22a2014-11-11 23:05:53 -0500895 inlines = [SliverInline, ImageDeploymentInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700896
Tony Mack32e1ce32014-05-07 13:29:41 -0400897 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -0700898
Scott Baker63d1a552014-08-21 15:19:07 -0700899 list_display = ['backend_status_icon', 'name']
900 list_display_links = ('backend_status_icon', 'name', )
901
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400902class NodeForm(forms.ModelForm):
903 class Meta:
904 widgets = {
905 'site': LinkedSelect,
906 'deployment': LinkedSelect
907 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400908
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500909class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400910 form = NodeForm
Scott Baker63d1a552014-08-21 15:19:07 -0700911 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
912 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400913 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500914
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400915 inlines = [TagInline,SliverInline]
Scott Baker40c00762014-08-21 16:55:59 -0700916 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
917 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400918
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500919 user_readonly_fields = ['name','site','deployment']
920 user_readonly_inlines = [TagInline,SliverInline]
921
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400922 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400923
Siobhan Tully567e3e62013-06-21 18:03:16 -0400924
Tony Mackd90cdbf2013-04-16 22:48:40 -0400925class SliverForm(forms.ModelForm):
926 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400927 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400928 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400929 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400930 widgets = {
931 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400932 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400933 'slice': LinkedSelect,
934 'deploymentNetwork': LinkedSelect,
935 'node': LinkedSelect,
936 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400937 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400938
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500939class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700940 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
941 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500942 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
943 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400944
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400945class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400946 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400947 fieldsets = [
Scott Baker7a61dc42014-09-02 17:08:20 -0700948 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -0400949 ]
Scott Baker40c00762014-08-21 16:55:59 -0700950 readonly_fields = ('backend_status_text', )
Scott Baker7a61dc42014-09-02 17:08:20 -0700951 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
Scott Baker63d1a552014-08-21 15:19:07 -0700952 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400953
954 suit_form_tabs =(('general', 'Sliver Details'),
955 ('tags','Tags'),
956 )
957
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400958 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -0400959
Scott Baker7a61dc42014-09-02 17:08:20 -0700960 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500961
Tony Mackc2835a92013-05-28 09:18:49 -0400962 def formfield_for_foreignkey(self, db_field, request, **kwargs):
963 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500964 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400965
966 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
967
Tony Mack04062832013-05-10 08:22:44 -0400968 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -0700969 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -0400970 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500971 return Sliver.select_by_user(request.user)
972
Tony Mack04062832013-05-10 08:22:44 -0400973
Tony Mack1d6b85f2013-05-07 18:49:14 -0400974 def get_formsets(self, request, obj=None):
975 # make some fields read only if we are updating an existing record
976 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -0700977 #self.readonly_fields = ('ip', 'instance_name')
Scott Baker8806cdf2014-10-17 16:27:23 -0700978 self.readonly_fields = ('backend_status_text',)
Tony Mack1d6b85f2013-05-07 18:49:14 -0400979 else:
Scott Baker8806cdf2014-10-17 16:27:23 -0700980 self.readonly_fields = ('backend_status_text',)
Scott Baker36f50872014-08-21 13:01:25 -0700981 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -0400982
983 for inline in self.get_inline_instances(request, obj):
984 # hide MyInline in the add view
985 if obj is None:
986 continue
Scott Baker526b71e2014-05-13 13:18:01 -0700987 if isinstance(inline, SliverInline):
988 inline.model.caller = request.user
989 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -0400990
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500991 #def save_model(self, request, obj, form, change):
992 # # update openstack connection to use this site/tenant
993 # auth = request.session.get('auth', {})
994 # auth['tenant'] = obj.slice.name
995 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
996 # obj.creator = request.user
997 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -0400998
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500999 #def delete_model(self, request, obj):
1000 # # update openstack connection to use this site/tenant
1001 # auth = request.session.get('auth', {})
1002 # auth['tenant'] = obj.slice.name
1003 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1004 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001005
Siobhan Tully53437282013-04-26 19:30:27 -04001006class UserCreationForm(forms.ModelForm):
1007 """A form for creating new users. Includes all the required
1008 fields, plus a repeated password."""
1009 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1010 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1011
1012 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001013 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001014 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001015
1016 def clean_password2(self):
1017 # Check that the two password entries match
1018 password1 = self.cleaned_data.get("password1")
1019 password2 = self.cleaned_data.get("password2")
1020 if password1 and password2 and password1 != password2:
1021 raise forms.ValidationError("Passwords don't match")
1022 return password2
1023
1024 def save(self, commit=True):
1025 # Save the provided password in hashed format
1026 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001027 user.password = self.cleaned_data["password1"]
1028 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001029 if commit:
1030 user.save()
1031 return user
1032
Siobhan Tully567e3e62013-06-21 18:03:16 -04001033
Siobhan Tully53437282013-04-26 19:30:27 -04001034class UserChangeForm(forms.ModelForm):
1035 """A form for updating users. Includes all the fields on
1036 the user, but replaces the password field with admin's
1037 password hash display field.
1038 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001039 password = ReadOnlyPasswordHashField(label='Password',
1040 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001041
1042 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001043 model = User
Siobhan Tully53437282013-04-26 19:30:27 -04001044
1045 def clean_password(self):
1046 # Regardless of what the user provides, return the initial value.
1047 # This is done here, rather than on the field, because the
1048 # field does not have access to the initial value
1049 return self.initial["password"]
1050
Scott Baker2c3cb642014-05-19 17:55:56 -07001051class UserDashboardViewInline(PlStackTabularInline):
1052 model = UserDashboardView
1053 extra = 0
1054 suit_classes = 'suit-tab suit-tab-dashboards'
1055 fields = ['user', 'dashboardView', 'order']
1056
Scott Bakerf4aeedc2014-10-03 13:10:47 -07001057class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1058 # Note: Make sure PermissionCheckingAdminMixin is listed before
1059 # admin.ModelAdmin in the class declaration.
1060
Siobhan Tully53437282013-04-26 19:30:27 -04001061 class Meta:
1062 app_label = "core"
1063
1064 # The forms to add and change user instances
1065 form = UserChangeForm
1066 add_form = UserCreationForm
1067
1068 # The fields to be used in displaying the User model.
1069 # These override the definitions on the base UserAdmin
1070 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001071 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001072 list_filter = ('site',)
Scott Baker2c3cb642014-05-19 17:55:56 -07001073 inlines = [SlicePrivilegeInline,SitePrivilegeInline,DeploymentPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001074
Scott Baker1a6a3902014-10-03 00:32:37 -07001075 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001076 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1077
Siobhan Tully53437282013-04-26 19:30:27 -04001078 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001079 ('Login Details', {'fields': ['backend_status_text', 'email', 'site','password', 'is_active', 'is_readonly', 'is_admin', 'public_key'], 'classes':['suit-tab suit-tab-general']}),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001080 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001081 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001082 #('Important dates', {'fields': ('last_login',)}),
1083 )
1084 add_fieldsets = (
1085 (None, {
1086 'classes': ('wide',),
Scott Baker6a995352014-10-06 17:51:20 -07001087 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001088 ),
1089 )
Scott Baker40c00762014-08-21 16:55:59 -07001090 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001091 search_fields = ('email',)
1092 ordering = ('email',)
1093 filter_horizontal = ()
1094
Scott Baker3ca51f62014-05-23 12:05:11 -07001095 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001096
Scott Baker6a995352014-10-06 17:51:20 -07001097 @property
1098 def suit_form_tabs(self):
1099 if getattr(_thread_locals, "obj", None) is None:
1100 return []
1101 else:
1102 return (('general','Login Details'),
1103 ('contact','Contact Information'),
1104 ('sliceprivileges','Slice Privileges'),
1105 ('siteprivileges','Site Privileges'),
1106 ('deploymentprivileges','Deployment Privileges'),
1107 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001108
Tony Mackc2835a92013-05-28 09:18:49 -04001109 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1110 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001111 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001112
1113 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1114
Tony Mack5b061472014-02-04 07:57:10 -05001115 def queryset(self, request):
1116 return User.select_by_user(request.user)
1117
Scott Baker2c3cb642014-05-19 17:55:56 -07001118class DashboardViewAdmin(PlanetStackBaseAdmin):
1119 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001120 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001121 'classes': ['suit-tab suit-tab-general']})
1122 ]
Scott Baker40c00762014-08-21 16:55:59 -07001123 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001124
Scott Baker2c3cb642014-05-19 17:55:56 -07001125 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001126
Scott Baker0165fac2014-01-13 11:49:26 -08001127class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001128 model = ServiceResource
1129 extra = 0
1130
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001131class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001132 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1133 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001134 inlines = [ServiceResourceInline]
1135
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001136 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1137 user_readonly_inlines = []
1138
Scott Baker0165fac2014-01-13 11:49:26 -08001139class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001140 model = ReservedResource
1141 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001142 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001143
1144 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1145 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1146
1147 if db_field.name == 'resource':
1148 # restrict resources to those that the slice's service class allows
1149 if request._slice is not None:
1150 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1151 if len(field.queryset) > 0:
1152 field.initial = field.queryset.all()[0]
1153 else:
1154 field.queryset = field.queryset.none()
1155 elif db_field.name == 'sliver':
1156 # restrict slivers to those that belong to the slice
1157 if request._slice is not None:
1158 field.queryset = field.queryset.filter(slice = request._slice)
1159 else:
1160 field.queryset = field.queryset.none()
1161
1162 return field
1163
Tony Mack5b061472014-02-04 07:57:10 -05001164 def queryset(self, request):
1165 return ReservedResource.select_by_user(request.user)
1166
Scott Baker133c9212013-05-17 09:09:11 -07001167class ReservationChangeForm(forms.ModelForm):
1168 class Meta:
1169 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001170 widgets = {
1171 'slice' : LinkedSelect
1172 }
Scott Baker133c9212013-05-17 09:09:11 -07001173
1174class ReservationAddForm(forms.ModelForm):
1175 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1176 refresh = forms.CharField(widget=forms.HiddenInput())
1177
1178 class Media:
1179 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1180
1181 def clean_slice(self):
1182 slice = self.cleaned_data.get("slice")
1183 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1184 if len(x) == 0:
1185 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1186 return slice
1187
1188 class Meta:
1189 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001190 widgets = {
1191 'slice' : LinkedSelect
1192 }
1193
Scott Baker133c9212013-05-17 09:09:11 -07001194
1195class ReservationAddRefreshForm(ReservationAddForm):
1196 """ This form is displayed when the Reservation Form receives an update
1197 from the Slice dropdown onChange handler. It doesn't validate the
1198 data and doesn't save the data. This will cause the form to be
1199 redrawn.
1200 """
1201
Scott Baker8737e5f2013-05-17 09:35:32 -07001202 """ don't validate anything other than slice """
1203 dont_validate_fields = ("startTime", "duration")
1204
Scott Baker133c9212013-05-17 09:09:11 -07001205 def full_clean(self):
1206 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001207
1208 for fieldname in self.dont_validate_fields:
1209 if fieldname in self._errors:
1210 del self._errors[fieldname]
1211
Scott Baker133c9212013-05-17 09:09:11 -07001212 return result
1213
1214 """ don't save anything """
1215 def is_valid(self):
1216 return False
1217
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001218class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001219 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001220 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001221 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001222 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001223 form = ReservationAddForm
1224
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001225 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1226
1227 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001228 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001229
Scott Baker133c9212013-05-17 09:09:11 -07001230 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001231 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001232 request._refresh = False
1233 request._slice = None
1234 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001235 # "refresh" will be set to "1" if the form was submitted due to
1236 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001237 if request.POST.get("refresh","1") == "1":
1238 request._refresh = True
1239 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001240
1241 # Keep track of the slice that was selected, so the
1242 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001243 request._slice = request.POST.get("slice",None)
1244 if (request._slice is not None):
1245 request._slice = Slice.objects.get(id=request._slice)
1246
1247 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1248 return result
1249
Scott Bakeracd45142013-05-19 16:19:16 -07001250 def changelist_view(self, request, extra_context = None):
1251 timezone.activate(request.user.timezone)
1252 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1253
Scott Baker133c9212013-05-17 09:09:11 -07001254 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001255 request._obj_ = obj
1256 if obj is not None:
1257 # For changes, set request._slice to the slice already set in the
1258 # object.
1259 request._slice = obj.slice
1260 self.form = ReservationChangeForm
1261 else:
1262 if getattr(request, "_refresh", False):
1263 self.form = ReservationAddRefreshForm
1264 else:
1265 self.form = ReservationAddForm
1266 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1267
Scott Baker133c9212013-05-17 09:09:11 -07001268 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001269 if (obj is not None):
1270 # Prevent slice from being changed after the reservation has been
1271 # created.
1272 return ['slice']
1273 else:
Scott Baker133c9212013-05-17 09:09:11 -07001274 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001275
Tony Mack5b061472014-02-04 07:57:10 -05001276 def queryset(self, request):
1277 return Reservation.select_by_user(request.user)
1278
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001279class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001280 list_display = ("backend_status_icon", "name", )
1281 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001282 user_readonly_fields = ['name']
1283 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001284
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001285class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001286 list_display = ("backend_status_icon", "name", )
1287 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001288 user_readonly_fields = ['name']
1289 user_readonly_inlines = []
1290
Scott Baker0165fac2014-01-13 11:49:26 -08001291class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001292 model = Router.networks.through
1293 extra = 0
1294 verbose_name_plural = "Routers"
1295 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001296 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001297
Scott Bakerb27b62c2014-08-15 16:29:16 -07001298class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001299 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001300 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001301 verbose_name_plural = "Parameters"
1302 verbose_name = "Parameter"
1303 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001304 fields = ['backend_status_icon', 'parameter', 'value']
1305 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001306
Scott Baker0165fac2014-01-13 11:49:26 -08001307class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001308 fields = ['backend_status_icon', 'network','sliver','ip']
1309 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001310 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001311 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001312 extra = 0
1313 verbose_name_plural = "Slivers"
1314 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001315 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001316
Scott Baker0165fac2014-01-13 11:49:26 -08001317class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001318 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001319 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001320 extra = 0
1321 verbose_name_plural = "Slices"
1322 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001323 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001324 fields = ['backend_status_icon', 'network','slice']
1325 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001326
Scott Baker8806cdf2014-10-17 16:27:23 -07001327class NetworkDeploymentsInline(PlStackTabularInline):
1328 model = NetworkDeployments
1329 extra = 0
1330 verbose_name_plural = "Network Deployments"
1331 verbose_name = "Network Deployment"
1332 suit_classes = 'suit-tab suit-tab-admin-only'
1333 fields = ['backend_status_icon', 'deployment','net_id','subnet_id']
1334 readonly_fields = ('backend_status_icon', )
1335
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001336class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001337 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1338 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001339 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001340
Scott Bakerd7d2a392013-08-06 08:57:30 -07001341 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Scott Baker8806cdf2014-10-17 16:27:23 -07001342 admin_inlines = [NetworkDeploymentsInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001343
Siobhan Tully2d95e482013-09-06 10:56:06 -04001344 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -07001345 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'], 'classes':['suit-tab suit-tab-general']}),]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001346
Scott Baker40c00762014-08-21 16:55:59 -07001347 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001348 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001349
Scott Baker8806cdf2014-10-17 16:27:23 -07001350 @property
1351 def suit_form_tabs(self):
1352 tabs=[('general','Network Details'),
1353 ('netparams', 'Parameters'),
1354 ('networkslivers','Slivers'),
1355 ('networkslices','Slices'),
1356 ('routers','Routers'),
1357 ]
1358
1359 request=getattr(_thread_locals, "request", None)
1360 if request and request.user.is_admin:
1361 tabs.append( ('admin-only', 'Admin-Only') )
1362
1363 return tabs
1364
1365
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001366class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001367 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1368 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001369 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1370 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001371
Scott Baker37b47902014-09-02 14:37:41 -07001372class FlavorAdmin(PlanetStackBaseAdmin):
1373 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1374 list_display_links = ("backend_status_icon", "name")
1375 user_readonly_fields = ("name", "flavor")
1376 fields = ("name", "description", "flavor", "order", "default")
1377
Tony Mack31c2b8f2013-04-26 20:01:42 -04001378# register a signal that caches the user's credentials when they log in
1379def cache_credentials(sender, user, request, **kwds):
1380 auth = {'username': request.POST['username'],
1381 'password': request.POST['password']}
1382 request.session['auth'] = auth
1383user_logged_in.connect(cache_credentials)
1384
Scott Baker15cddfa2013-12-09 13:45:19 -08001385def dollar_field(fieldName, short_description):
1386 def newFunc(self, obj):
1387 try:
1388 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1389 except:
1390 x=getattr(obj, fieldName, 0.0)
1391 return x
1392 newFunc.short_description = short_description
1393 return newFunc
1394
1395def right_dollar_field(fieldName, short_description):
1396 def newFunc(self, obj):
1397 try:
1398 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1399 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1400 except:
1401 x=getattr(obj, fieldName, 0.0)
1402 return x
1403 newFunc.short_description = short_description
1404 newFunc.allow_tags = True
1405 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001406
Scott Baker0165fac2014-01-13 11:49:26 -08001407class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001408 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001409 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001410 verbose_name_plural = "Charges"
1411 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001412 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001413 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1414 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1415 can_delete = False
1416 max_num = 0
1417
1418 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001419
1420class InvoiceAdmin(admin.ModelAdmin):
1421 list_display = ("date", "account")
1422
1423 inlines = [InvoiceChargeInline]
1424
Scott Baker9cb88a22013-12-09 18:56:00 -08001425 fields = ["date", "account", "dollar_amount"]
1426 readonly_fields = ["date", "account", "dollar_amount"]
1427
1428 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001429
Scott Baker0165fac2014-01-13 11:49:26 -08001430class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001431 model = Invoice
1432 extra = 0
1433 verbose_name_plural = "Invoices"
1434 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001435 fields = ["date", "dollar_amount"]
1436 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001437 suit_classes = 'suit-tab suit-tab-accountinvoice'
1438 can_delete=False
1439 max_num=0
1440
1441 dollar_amount = right_dollar_field("amount", "Amount")
1442
Scott Baker0165fac2014-01-13 11:49:26 -08001443class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001444 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001445 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001446 verbose_name_plural = "Charges"
1447 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001448 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001449 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1450 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001451 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001452 can_delete=False
1453 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001454
1455 def queryset(self, request):
1456 qs = super(PendingChargeInline, self).queryset(request)
1457 qs = qs.filter(state="pending")
1458 return qs
1459
Scott Baker15cddfa2013-12-09 13:45:19 -08001460 dollar_amount = right_dollar_field("amount", "Amount")
1461
Scott Baker0165fac2014-01-13 11:49:26 -08001462class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001463 model=Payment
1464 extra = 1
1465 verbose_name_plural = "Payments"
1466 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001467 fields = ["date", "dollar_amount"]
1468 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001469 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001470 can_delete=False
1471 max_num=0
1472
1473 dollar_amount = right_dollar_field("amount", "Amount")
1474
Scott Baker43105042013-12-06 23:23:36 -08001475class AccountAdmin(admin.ModelAdmin):
1476 list_display = ("site", "balance_due")
1477
1478 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1479
1480 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001481 (None, {'fields': ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments'],'classes':['suit-tab suit-tab-general']}),]
Scott Baker43105042013-12-06 23:23:36 -08001482
Scott Baker15cddfa2013-12-09 13:45:19 -08001483 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001484
1485 suit_form_tabs =(
1486 ('general','Account Details'),
1487 ('accountinvoice', 'Invoices'),
1488 ('accountpayments', 'Payments'),
1489 ('accountpendingcharges','Pending Charges'),
1490 )
1491
Scott Baker15cddfa2013-12-09 13:45:19 -08001492 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1493 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1494 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1495
Siobhan Tully53437282013-04-26 19:30:27 -04001496# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001497admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001498# ... and, since we're not using Django's builtin permissions,
1499# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001500#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001501
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001502#Do not show django evolution in the admin interface
1503from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001504#admin.site.unregister(Version)
1505#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001506
1507
1508# When debugging it is often easier to see all the classes, but for regular use
1509# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001510showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001511
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001512admin.site.register(Deployment, DeploymentAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001513admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001514admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001515admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001516admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001517admin.site.register(Network, NetworkAdmin)
1518admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001519admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001520admin.site.register(Account, AccountAdmin)
1521admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001522
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001523if True:
1524 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1525 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001526 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001527 admin.site.register(Tag, TagAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001528 admin.site.register(DeploymentRole)
1529 admin.site.register(SiteRole)
1530 admin.site.register(SliceRole)
1531 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001532 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001533 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1534 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001535 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001536 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001537 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001538 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001539