blob: ffa21d9de3f56e06f9e007eb1bdcb96a73b97882 [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
Scott Baker9f6b8ed2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Bakercbfb6002014-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 Baker9f6b8ed2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
20from django.forms.utils import flatatt, to_current_timezone
Scott Baker3cde7372014-10-21 21:03:08 -070021from cgi import escape as html_escape
Tony Mack7130ac32013-03-22 21:58:00 -040022
Scott Baker36f50872014-08-21 13:01:25 -070023import django_evolution
Scott Baker0a5633b2014-10-06 17:51:20 -070024import threading
25
26# thread locals necessary to work around a django-suit issue
27_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070028
Scott Baker40c00762014-08-21 16:55:59 -070029def backend_icon(obj): # backend_status, enacted, updated):
30 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
31 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Bakerfbf06642014-09-09 10:38:15 -070032 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
Scott Baker40c00762014-08-21 16:55:59 -070033 else:
34 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
Scott Bakerfbf06642014-09-09 10:38:15 -070035 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 -070036 else:
Scott Baker3cde7372014-10-21 21:03:08 -070037 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 -070038
39def backend_text(obj):
40 icon = backend_icon(obj)
41 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Baker3cde7372014-10-21 21:03:08 -070042 return "%s %s" % (icon, "successfully enacted")
Scott Baker40c00762014-08-21 16:55:59 -070043 else:
Scott Baker3cde7372014-10-21 21:03:08 -070044 return "%s %s" % (icon, html_escape(obj.backend_status, quote=True))
Scott Baker63d1a552014-08-21 15:19:07 -070045
Scott Baker9f6b8ed2014-11-17 23:44:03 -080046class UploadTextareaWidget(AdminTextareaWidget):
47 def render(self, name, value, attrs=None):
48 if value is None:
49 value = ''
50 final_attrs = self.build_attrs(attrs, name=name)
51 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
52 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
53 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
54 flatatt(final_attrs),
55 force_text(value))
56
Scott Baker36f50872014-08-21 13:01:25 -070057class PlainTextWidget(forms.HiddenInput):
58 input_type = 'hidden'
59
60 def render(self, name, value, attrs=None):
61 if value is None:
62 value = ''
63 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
64
Scott Baker86c83ab2014-10-03 13:10:47 -070065class PermissionCheckingAdminMixin(object):
Scott Bakercbfb6002014-10-03 00:32:37 -070066 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050067
68 def has_add_permission(self, request, obj=None):
69 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070070
Siobhan Tullycf04fb62014-01-11 11:25:57 -050071 def has_delete_permission(self, request, obj=None):
72 return (not self.__user_is_readonly(request))
73
74 def save_model(self, request, obj, form, change):
75 if self.__user_is_readonly(request):
Scott Bakercbfb6002014-10-03 00:32:37 -070076 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050077 raise PermissionDenied
Scott Bakercbfb6002014-10-03 00:32:37 -070078
79 obj.caller = request.user
80 # update openstack connection to use this site/tenant
81 obj.save_by_user(request.user)
82
83 def delete_model(self, request, obj):
84 obj.delete_by_user(request.user)
85
86 def save_formset(self, request, form, formset, change):
87 instances = formset.save(commit=False)
88 for instance in instances:
89 instance.save_by_user(request.user)
90
91 # BUG in django 1.7? Objects are not deleted by formset.save if
92 # commit is False. So let's delete them ourselves.
93 #
94 # code from forms/models.py save_existing_objects()
95 try:
96 forms_to_delete = formset.deleted_forms
97 except AttributeError:
98 forms_to_delete = []
99 if formset.initial_forms:
100 for form in formset.initial_forms:
101 obj = form.instance
102 if form in forms_to_delete:
103 if obj.pk is None:
104 continue
105 formset.deleted_objects.append(obj)
106 obj.delete()
107
108 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500109
110 def get_actions(self,request):
Scott Baker86c83ab2014-10-03 13:10:47 -0700111 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500112
113 if self.__user_is_readonly(request):
114 if 'delete_selected' in actions:
115 del actions['delete_selected']
116
117 return actions
118
119 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500120 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -0700121 if not hasattr(self, "readonly_save"):
122 # save the original readonly fields
123 self.readonly_save = self.readonly_fields
124 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -0700125 if hasattr(self, "user_readonly_fields"):
126 self.readonly_fields=self.user_readonly_fields
127 if hasattr(self, "user_readonly_inlines"):
128 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -0700129 else:
130 if hasattr(self, "readonly_save"):
131 # restore the original readonly fields
132 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -0700133 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700134 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500135
136 try:
Scott Baker86c83ab2014-10-03 13:10:47 -0700137 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500138 except PermissionDenied:
139 pass
140 if request.method == 'POST':
141 raise PermissionDenied
142 request.readonly = True
Scott Baker86c83ab2014-10-03 13:10:47 -0700143 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500144
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500145 def __user_is_readonly(self, request):
146 return request.user.isReadOnlyUser()
147
Scott Baker40c00762014-08-21 16:55:59 -0700148 def backend_status_text(self, obj):
149 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700150
Scott Baker63d1a552014-08-21 15:19:07 -0700151 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700152 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700153 backend_status_icon.short_description = ""
154
Scott Baker24ded6a2014-11-05 09:05:38 -0800155 def get_form(self, request, obj=None, **kwargs):
Scott Baker5c432692014-10-16 00:57:55 -0700156 # Save obj and request in thread-local storage, so suit_form_tabs can
157 # use it to determine whether we're in edit or add mode, and can
158 # determine whether the user is an admin.
159 _thread_locals.request = request
160 _thread_locals.obj = obj
Scott Baker24ded6a2014-11-05 09:05:38 -0800161 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker5c432692014-10-16 00:57:55 -0700162
163 def get_inline_instances(self, request, obj=None):
164 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
165
166 # inlines that should only be shown to an admin user
167 if request.user.is_admin:
168 for inline_class in getattr(self, "admin_inlines", []):
169 inlines.append(inline_class(self.model, self.admin_site))
170
171 return inlines
172
Scott Baker86c83ab2014-10-03 13:10:47 -0700173class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
174 # Note: Make sure PermissionCheckingAdminMixin is listed before
175 # admin.ModelAdmin in the class declaration.
176
Scott Bakercbfb6002014-10-03 00:32:37 -0700177 pass
178
179class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
180 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700181
Scott Bakere8859f92014-05-23 12:42:40 -0700182class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400183 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700184 if not super(SingletonAdmin, self).has_add_permission(request):
185 return False
186
Siobhan Tullyce652d02013-10-08 21:52:35 -0400187 num_objects = self.model.objects.count()
188 if num_objects >= 1:
189 return False
190 else:
191 return True
192
Siobhan Tullyd3515752013-06-21 16:34:53 -0400193class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800194 def __init__(self, *args, **kwargs):
195 super(PlStackTabularInline, self).__init__(*args, **kwargs)
196
197 # InlineModelAdmin as no get_fields() method, so in order to add
198 # the selflink field, we override __init__ to modify self.fields and
199 # self.readonly_fields.
200
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800201 self.setup_selflink()
202
Scott Baker874936e2014-01-13 18:15:34 -0800203 def get_change_url(self, model, id):
204 """ Get the URL to a change form in the admin for this model """
205 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800206 try:
Scott Baker874936e2014-01-13 18:15:34 -0800207 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800208 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800209 return None
210
211 return url
212
213 def setup_selflink(self):
214 if hasattr(self, "selflink_fieldname"):
215 """ self.selflink_model can be defined to punch through a relation
216 to its target object. For example, in SliceNetworkInline, set
217 selflink_model = "network", and the URL will lead to the Network
218 object instead of trying to bring up a change view of the
219 SliceNetwork object.
220 """
221 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
222 else:
223 self.selflink_model = self.model
224
225 url = self.get_change_url(self.selflink_model, 0)
226
227 # We don't have an admin for this object, so don't create the
228 # selflink.
229 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800230 return
231
Scott Baker874936e2014-01-13 18:15:34 -0800232 # Since we need to add "selflink" to the field list, we need to create
233 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800234 if (self.fields is None):
235 self.fields = []
236 for f in self.model._meta.fields:
237 if f.editable and f.name != "id":
238 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800239
Scott Baker874936e2014-01-13 18:15:34 -0800240 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800241
Scott Baker874936e2014-01-13 18:15:34 -0800242 if self.readonly_fields is None:
243 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800244
Scott Baker874936e2014-01-13 18:15:34 -0800245 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800246
247 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800248 if hasattr(self, "selflink_fieldname"):
249 obj = getattr(obj, self.selflink_fieldname)
250
Scott Baker86568322014-01-12 16:53:31 -0800251 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800252 url = self.get_change_url(self.selflink_model, obj.id)
253 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800254 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800255 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800256
257 selflink.allow_tags = True
258 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400259
Scott Bakerb27b62c2014-08-15 16:29:16 -0700260 def has_add_permission(self, request):
261 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500262
263 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700264 readonly_fields = list(self.readonly_fields)[:]
265 if request.user.isReadOnlyUser():
266 for field in self.fields:
267 if not field in readonly_fields:
268 readonly_fields.append(field)
269 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500270
Scott Baker40c00762014-08-21 16:55:59 -0700271 def backend_status_icon(self, obj):
272 return mark_safe(backend_icon(obj))
273 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700274
Scott Bakerb27b62c2014-08-15 16:29:16 -0700275class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500276 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700277 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500278
Scott Bakerb27b62c2014-08-15 16:29:16 -0700279 def get_readonly_fields(self, request, obj=None):
280 readonly_fields = list(self.readonly_fields)[:]
281 if request.user.isReadOnlyUser():
282 for field in self.fields:
283 if not field in readonly_fields:
284 readonly_fields.append(field)
285 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500286
Scott Baker40c00762014-08-21 16:55:59 -0700287 def backend_status_icon(self, obj):
288 return mark_safe(backend_icon(obj))
289 backend_status_icon.short_description = ""
290
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400291class ReservationInline(PlStackTabularInline):
292 model = Reservation
293 extra = 0
294 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700295
Tony Mack5b061472014-02-04 07:57:10 -0500296 def queryset(self, request):
297 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400298
Scott Bakerb27b62c2014-08-15 16:29:16 -0700299class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400300 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400301 extra = 0
302 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500303 fields = ['service', 'name', 'value']
304
305 def queryset(self, request):
306 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400307
Scott Baker74d8e622013-07-29 16:04:22 -0700308class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400309 """ This is a callable that looks up a network name in a sliver and returns
310 the ip address for that network.
311 """
312
Scott Baker434ca7e2014-08-15 12:29:20 -0700313 byNetworkName = {} # class variable
314
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400315 def __init__(self, name):
316 self.short_description = name
317 self.__name__ = name
318 self.network_name = name
319
320 def __call__(self, obj):
321 if obj is not None:
322 for nbs in obj.networksliver_set.all():
323 if (nbs.network.name == self.network_name):
324 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700325 return ""
326
327 def __str__(self):
328 return self.network_name
329
Scott Baker434ca7e2014-08-15 12:29:20 -0700330 @staticmethod
331 def get(network_name):
332 """ We want to make sure we alwars return the same NetworkLookerUpper
333 because sometimes django will cause them to be instantiated multiple
334 times (and we don't want different ones in form.fields vs
335 SliverInline.readonly_fields).
336 """
337 if network_name not in NetworkLookerUpper.byNetworkName:
338 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
339 return NetworkLookerUpper.byNetworkName[network_name]
340
Siobhan Tullyd3515752013-06-21 16:34:53 -0400341class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400342 model = Sliver
Scott Baker7a61dc42014-09-02 17:08:20 -0700343 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deploymentNetwork', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400344 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700345 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400346 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700347
Tony Mack5b061472014-02-04 07:57:10 -0500348 def queryset(self, request):
349 return Sliver.select_by_user(request.user)
350
Scott Bakerb24cc932014-06-09 10:51:16 -0700351 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Scott Bakerb24cc932014-06-09 10:51:16 -0700352 if db_field.name == 'deploymentNetwork':
Scott Baker3b678742014-06-09 13:11:54 -0700353 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker7a61dc42014-09-02 17:08:20 -0700354 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Scott Baker32481312014-09-08 12:14:14 -0700355 elif db_field.name == 'flavor':
356 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700357
358 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700359
360 return field
361
Siobhan Tullyd3515752013-06-21 16:34:53 -0400362class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400363 model = Site
364 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400365 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400366
Tony Mack5b061472014-02-04 07:57:10 -0500367 def queryset(self, request):
368 return Site.select_by_user(request.user)
369
Siobhan Tullyd3515752013-06-21 16:34:53 -0400370class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400371 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700372 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
373 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400374 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400375 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400376
Tony Mack5b061472014-02-04 07:57:10 -0500377 def queryset(self, request):
378 return User.select_by_user(request.user)
379
Siobhan Tullyd3515752013-06-21 16:34:53 -0400380class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400381 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700382 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
383 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400384 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400385 suit_classes = 'suit-tab suit-tab-slices'
386
Tony Mack5b061472014-02-04 07:57:10 -0500387 def queryset(self, request):
388 return Slice.select_by_user(request.user)
389
Siobhan Tullyd3515752013-06-21 16:34:53 -0400390class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400391 model = Node
392 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400393 suit_classes = 'suit-tab suit-tab-nodes'
Scott Baker40c00762014-08-21 16:55:59 -0700394 fields = ['backend_status_icon', 'name','deployment','site']
395 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400396
Tony Mack336e0f92014-11-30 15:53:08 -0500397class ControllerPrivilegeInline(PlStackTabularInline):
398 model = ControllerPrivilege
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400399 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500400 suit_classes = 'suit-tab suit-tab-admin-only'
401 fields = ['backend_status_icon', 'user','role','controller']
Scott Baker40c00762014-08-21 16:55:59 -0700402 readonly_fields = ('backend_status_icon', )
Tony Mack5b061472014-02-04 07:57:10 -0500403
404 def queryset(self, request):
Tony Mack336e0f92014-11-30 15:53:08 -0500405 return ControllerPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400406
Siobhan Tullyd3515752013-06-21 16:34:53 -0400407class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400408 model = SitePrivilege
409 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400410 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700411 fields = ['backend_status_icon', 'user','site', 'role']
412 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400413
Tony Mackc2835a92013-05-28 09:18:49 -0400414 def formfield_for_foreignkey(self, db_field, request, **kwargs):
415 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500416 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400417
418 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500419 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400420 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
421
Tony Mack5b061472014-02-04 07:57:10 -0500422 def queryset(self, request):
423 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400424
Sapan Bhatia39e85582014-11-19 15:07:36 -0500425class SiteDeploymentsInline(PlStackTabularInline):
426 model = SiteDeployments
Tony Macke4be32f2014-03-11 20:45:25 -0400427 extra = 0
428 suit_classes = 'suit-tab suit-tab-deployments'
Scott Baker40c00762014-08-21 16:55:59 -0700429 fields = ['backend_status_icon', 'deployment','site']
430 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400431
432 def formfield_for_foreignkey(self, db_field, request, **kwargs):
433 if db_field.name == 'site':
434 kwargs['queryset'] = Site.select_by_user(request.user)
435
436 if db_field.name == 'deployment':
437 kwargs['queryset'] = Deployment.select_by_user(request.user)
Sapan Bhatia39e85582014-11-19 15:07:36 -0500438 return super(SiteDeploymentsInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400439
440 def queryset(self, request):
Sapan Bhatia39e85582014-11-19 15:07:36 -0500441 return SiteDeployments.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400442
Tony Mack336e0f92014-11-30 15:53:08 -0500443class ControllerSitesInline(PlStackTabularInline):
444 model = ControllerSites
445 extra = 0
446 suit_classes = 'suit-tab suit-tab-admin-only'
447 fields = ['backend_status_icon', 'controller','site']
448 readonly_fields = ('backend_status_icon', )
449
450 def formfield_for_foreignkey(self, db_field, request, **kwargs):
451 if db_field.name == 'site':
452 kwargs['queryset'] = Site.select_by_user(request.user)
453
454 if db_field.name == 'controller':
455 kwargs['queryset'] = Controller.select_by_user(request.user)
456 return super(ControllerSitesInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
457
458 def queryset(self, request):
459 return ControllerSites.select_by_user(request.user)
460
Tony Macke4be32f2014-03-11 20:45:25 -0400461
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400462class SlicePrivilegeInline(PlStackTabularInline):
463 model = SlicePrivilege
464 suit_classes = 'suit-tab suit-tab-sliceprivileges'
465 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700466 fields = ('backend_status_icon', 'user', 'slice', 'role')
467 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400468
Tony Mackc2835a92013-05-28 09:18:49 -0400469 def formfield_for_foreignkey(self, db_field, request, **kwargs):
470 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700471 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400472 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700473 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400474
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400475 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400476
Tony Mack5b061472014-02-04 07:57:10 -0500477 def queryset(self, request):
478 return SlicePrivilege.select_by_user(request.user)
479
Scott Bakera0015eb2013-08-14 17:28:14 -0700480class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700481 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800482 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700483 extra = 0
484 verbose_name = "Network Connection"
485 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400486 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700487 fields = ['backend_status_icon', 'network']
488 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700489
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500490class ImageDeploymentsInline(PlStackTabularInline):
491 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700492 extra = 0
493 verbose_name = "Image Deployments"
494 verbose_name_plural = "Image Deployments"
495 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500496 fields = ['backend_status_icon', 'image', 'deployment']
497 readonly_fields = ['backend_status_icon']
498
499class ControllerImagesInline(PlStackTabularInline):
500 model = ControllerImages
501 extra = 0
502 verbose_name = "Controller Images"
503 verbose_name_plural = "Controller Images"
504 suit_classes = 'suit-tab suit-tab-admin-only'
505 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700506 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700507
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400508class SliceRoleAdmin(PlanetStackBaseAdmin):
509 model = SliceRole
510 pass
511
512class SiteRoleAdmin(PlanetStackBaseAdmin):
513 model = SiteRole
514 pass
515
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400516class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400517 sites = forms.ModelMultipleChoiceField(
518 queryset=Site.objects.all(),
519 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700520 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400521 widget=FilteredSelectMultiple(
522 verbose_name=('Sites'), is_stacked=False
523 )
524 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700525 images = forms.ModelMultipleChoiceField(
526 queryset=Image.objects.all(),
527 required=False,
528 help_text="Select which images should be deployed on this deployment",
529 widget=FilteredSelectMultiple(
530 verbose_name=('Images'), is_stacked=False
531 )
532 )
Scott Baker37b47902014-09-02 14:37:41 -0700533 flavors = forms.ModelMultipleChoiceField(
534 queryset=Flavor.objects.all(),
535 required=False,
536 help_text="Select which flavors should be usable on this deployment",
537 widget=FilteredSelectMultiple(
538 verbose_name=('Flavors'), is_stacked=False
539 )
540 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400541 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400542 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700543 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400544
Siobhan Tully320b4622014-01-17 15:11:14 -0500545 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700546 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500547 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
548
Scott Baker5380c522014-06-06 14:49:43 -0700549 self.fields['accessControl'].initial = "allow site " + request.user.site.name
550
Siobhan Tully320b4622014-01-17 15:11:14 -0500551 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800552 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments.all()]
553 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700554 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700555
556 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
557 """ helper function for handling m2m relations from the MultipleChoiceField
558
559 this_obj: the source object we want to link from
560
561 selected_objs: a list of destination objects we want to link to
562
563 all_relations: the full set of relations involving this_obj, including ones we don't want
564
565 relation_class: the class that implements the relation from source to dest
566
567 local_attrname: field name representing this_obj in relation_class
568
569 foreign_attrname: field name representing selected_objs in relation_class
570
571 This function will remove all newobjclass relations from this_obj
572 that are not contained in selected_objs, and add any relations that
573 are in selected_objs but don't exist in the data model yet.
574 """
575
576 existing_dest_objs = []
577 for relation in list(all_relations):
578 if getattr(relation, foreign_attrname) not in selected_objs:
579 #print "deleting site", sdp.site
580 relation.delete()
581 else:
582 existing_dest_objs.append(getattr(relation, foreign_attrname))
583
584 for dest_obj in selected_objs:
585 if dest_obj not in existing_dest_objs:
586 #print "adding site", site
587 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
588 relation = relation_class(**kwargs)
589 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500590
591 def save(self, commit=True):
592 deployment = super(DeploymentAdminForm, self).save(commit=False)
593
594 if commit:
595 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700596 # this has to be done after save() if/when a deployment is first created
597 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500598
599 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700600 # save_m2m() doesn't seem to work with 'through' relations. So we
601 # create/destroy the through models ourselves. There has to be
602 # a better way...
603
Sapan Bhatia39e85582014-11-19 15:07:36 -0500604 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments.all(), SiteDeployments, "deployment", "site")
Tony Mack336e0f92014-11-30 15:53:08 -0500605 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ControllerImages, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700606
Scott Baker37b47902014-09-02 14:37:41 -0700607 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500608
609 return deployment
610
Scott Bakerff5e0f32014-05-22 14:40:27 -0700611class DeploymentAdminROForm(DeploymentAdminForm):
612 def save(self, commit=True):
613 raise PermissionDenied
614
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500615class SiteAssocInline(PlStackTabularInline):
616 model = Site.deployments.through
617 extra = 0
618 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400619
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400620class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500621 model = Deployment
Tony Mack2cbd3802014-09-29 16:10:52 -0400622 fieldList = ['backend_status_text', 'name', 'availability_zone', 'sites', 'images', 'flavors', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500623 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Tony Mack336e0f92014-11-30 15:53:08 -0500624 inlines = [ControllerPrivilegeInline,NodeInline,TagInline] # ,ControllerImagesInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700625 list_display = ['backend_status_icon', 'name']
626 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700627 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500628
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500629 user_readonly_fields = ['name']
630
Scott Bakerde0f4412014-06-11 15:40:26 -0700631 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500632
Scott Bakerff5e0f32014-05-22 14:40:27 -0700633 def get_form(self, request, obj=None, **kwargs):
634 if request.user.isReadOnlyUser():
635 kwargs["form"] = DeploymentAdminROForm
636 else:
637 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700638 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
639
640 # from stackexchange: pass the request object into the form
641
642 class AdminFormMetaClass(adminForm):
643 def __new__(cls, *args, **kwargs):
644 kwargs['request'] = request
645 return adminForm(*args, **kwargs)
646
647 return AdminFormMetaClass
648
Siobhan Tullyce652d02013-10-08 21:52:35 -0400649class ServiceAttrAsTabInline(PlStackTabularInline):
650 model = ServiceAttribute
651 fields = ['name','value']
652 extra = 0
653 suit_classes = 'suit-tab suit-tab-serviceattrs'
654
Siobhan Tullyce652d02013-10-08 21:52:35 -0400655class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700656 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
657 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700658 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500659 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
660 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700661 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500662
663 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500664
665 suit_form_tabs =(('general', 'Service Details'),
666 ('slices','Slices'),
667 ('serviceattrs','Additional Attributes'),
668 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400669
Tony Mack0553f282013-06-10 22:54:50 -0400670class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700671 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400672 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500673 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400674 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400675 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400676 suit_form_tabs =(('general', 'Site Details'),
677 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400678 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400679 ('deployments','Deployments'),
680 ('slices','Slices'),
Scott Baker36f50872014-08-21 13:01:25 -0700681 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400682 ('tags','Tags'),
683 )
Scott Baker40c00762014-08-21 16:55:59 -0700684 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500685
686 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500687
Scott Baker63d1a552014-08-21 15:19:07 -0700688 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
689 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400690 filter_horizontal = ('deployments',)
Sapan Bhatia39e85582014-11-19 15:07:36 -0500691 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentsInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400692 search_fields = ['name']
693
Tony Mack04062832013-05-10 08:22:44 -0400694 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500695 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400696
Tony Mack5cd13202013-05-01 21:48:38 -0400697 def get_formsets(self, request, obj=None):
698 for inline in self.get_inline_instances(request, obj):
699 # hide MyInline in the add view
700 if obj is None:
701 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400702 if isinstance(inline, SliverInline):
703 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400704 yield inline.get_formset(request, obj)
705
Scott Baker545db2a2013-12-09 18:44:43 -0800706 def accountLink(self, obj):
707 link_obj = obj.accounts.all()
708 if link_obj:
709 reverse_path = "admin:core_account_change"
710 url = reverse(reverse_path, args =(link_obj[0].id,))
711 return "<a href='%s'>%s</a>" % (url, "view billing details")
712 else:
713 return "no billing data for this site"
714 accountLink.allow_tags = True
715 accountLink.short_description = "Billing"
716
Tony Mack332ee1d2014-02-04 15:33:45 -0500717 def save_model(self, request, obj, form, change):
718 # update openstack connection to use this site/tenant
719 obj.save_by_user(request.user)
720
721 def delete_model(self, request, obj):
722 obj.delete_by_user(request.user)
723
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500724
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400725class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700726 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400727 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500728 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400729 ]
Scott Baker40c00762014-08-21 16:55:59 -0700730 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700731 list_display = ('backend_status_icon', 'user', 'site', 'role')
732 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500733 user_readonly_fields = fieldList
734 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400735
Tony Mackc2835a92013-05-28 09:18:49 -0400736 def formfield_for_foreignkey(self, db_field, request, **kwargs):
737 if db_field.name == 'site':
738 if not request.user.is_admin:
739 # only show sites where user is an admin or pi
740 sites = set()
741 for site_privilege in SitePrivilege.objects.filer(user=request.user):
742 if site_privilege.role.role_type in ['admin', 'pi']:
743 sites.add(site_privilege.site)
744 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
745
746 if db_field.name == 'user':
747 if not request.user.is_admin:
748 # only show users from sites where caller has admin or pi role
749 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
750 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
751 sites = [site_privilege.site for site_privilege in site_privileges]
752 site_privileges = SitePrivilege.objects.filter(site__in=sites)
753 emails = [site_privilege.user.email for site_privilege in site_privileges]
754 users = User.objects.filter(email__in=emails)
755 kwargs['queryset'] = users
756
757 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
758
Tony Mack04062832013-05-10 08:22:44 -0400759 def queryset(self, request):
760 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400761 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400762 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500763 #if not request.user.is_admin:
764 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
765 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
766 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
767 # sites = Site.objects.filter(login_base__in=login_bases)
768 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400769 return qs
770
Siobhan Tullyce652d02013-10-08 21:52:35 -0400771class SliceForm(forms.ModelForm):
772 class Meta:
773 model = Slice
774 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700775 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400776 }
777
Tony Mack2cbd3802014-09-29 16:10:52 -0400778 def clean(self):
779 cleaned_data = super(SliceForm, self).clean()
780 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -0700781 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -0400782 slice_id = self.instance.id
783 if not site and slice_id:
784 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -0700785 if (not isinstance(site,Site)):
786 # previous code indicates 'site' could be a site_id and not a site?
787 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -0400788 if not name.startswith(site.login_base):
789 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
790 return cleaned_data
791
Tony Mack336e0f92014-11-30 15:53:08 -0500792class ControllerSlicesInline(PlStackTabularInline):
793 model = ControllerSlices
Scott Bakerc4efdc72014-10-15 16:54:04 -0700794 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500795 verbose_name = "Controller Slices"
796 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -0700797 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -0500798 fields = ['backend_status_icon', 'controller', 'tenant_id']
Scott Bakerc4efdc72014-10-15 16:54:04 -0700799 readonly_fields = ('backend_status_icon', )
800
Tony Mack2bd5b412013-06-11 21:05:06 -0400801class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400802 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400803 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500804 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700805 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400806 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
807 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400808 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Mack336e0f92014-11-30 15:53:08 -0500809 admin_inlines = [ControllerSlicesInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400810
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500811 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400812
Scott Bakerc4efdc72014-10-15 16:54:04 -0700813 @property
814 def suit_form_tabs(self):
815 tabs =[('general', 'Slice Details'),
816 ('slicenetworks','Networks'),
817 ('sliceprivileges','Privileges'),
818 ('slivers','Slivers'),
819 ('tags','Tags'),
820 ('reservations','Reservations'),
821 ]
822
823 request=getattr(_thread_locals, "request", None)
824 if request and request.user.is_admin:
825 tabs.append( ('admin-only', 'Admin-Only') )
826
827 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -0400828
829 def add_view(self, request, form_url='', extra_context=None):
830 # revert to default read-only fields
831 self.readonly_fields = ('backend_status_text',)
832 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
833
834 def change_view(self, request, object_id, form_url='', extra_context=None):
835 print object_id
836 # cannot change the site of an existing slice so make the site field read only
837 if object_id:
838 self.readonly_fields = ('backend_status_text','site')
839 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400840
Scott Baker510fdbb2014-08-05 17:19:24 -0700841 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700842 deployment_nodes = []
843 for node in Node.objects.all():
844 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
845
Scott Baker7a61dc42014-09-02 17:08:20 -0700846 deployment_flavors = []
847 for flavor in Flavor.objects.all():
848 for deployment in flavor.deployments.all():
849 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
850
Tony Mack336e0f92014-11-30 15:53:08 -0500851 controller_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -0700852 for image in Image.objects.all():
Tony Mack336e0f92014-11-30 15:53:08 -0500853 for controller_image in image.controllerimages.all():
854 controller_images.append( (controller_image.controller.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -0700855
Tony Mackec23b992014-09-02 21:18:45 -0400856 site_login_bases = []
857 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -0700858 site_login_bases.append((site.id, site.login_base))
859
Scott Baker510fdbb2014-08-05 17:19:24 -0700860 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700861 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -0700862 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400863 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700864 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
865
Tony Mackc2835a92013-05-28 09:18:49 -0400866 def formfield_for_foreignkey(self, db_field, request, **kwargs):
867 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500868 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400869 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 -0700870
Tony Mackc2835a92013-05-28 09:18:49 -0400871 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
872
Tony Mack04062832013-05-10 08:22:44 -0400873 def queryset(self, request):
874 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500875 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400876
Tony Mack79748612013-05-01 14:52:03 -0400877 def get_formsets(self, request, obj=None):
878 for inline in self.get_inline_instances(request, obj):
879 # hide MyInline in the add view
880 if obj is None:
881 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400882 if isinstance(inline, SliverInline):
883 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400884 yield inline.get_formset(request, obj)
885
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400886class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400887 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700888 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400889 ]
Scott Baker40c00762014-08-21 16:55:59 -0700890 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700891 list_display = ('backend_status_icon', 'user', 'slice', 'role')
892 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400893
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500894 user_readonly_fields = ['user', 'slice', 'role']
895 user_readonly_inlines = []
896
Tony Mackc2835a92013-05-28 09:18:49 -0400897 def formfield_for_foreignkey(self, db_field, request, **kwargs):
898 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500899 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400900
901 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500902 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400903
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400904 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400905
Tony Mack04062832013-05-10 08:22:44 -0400906 def queryset(self, request):
907 # admins can see all memberships. Users can only see memberships of
908 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500909 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400910
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400911 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400912 # update openstack connection to use this site/tenant
913 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400914 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400915 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400916 obj.save()
917
918 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400919 # update openstack connection to use this site/tenant
920 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400921 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400922 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400923 obj.delete()
924
Siobhan Tully567e3e62013-06-21 18:03:16 -0400925
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400926class ImageAdmin(PlanetStackBaseAdmin):
927
Scott Baker36f50872014-08-21 13:01:25 -0700928 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -0700929 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400930 'classes': ['suit-tab suit-tab-general']})
931 ]
Scott Baker40c00762014-08-21 16:55:59 -0700932 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400933
Tony Mack336e0f92014-11-30 15:53:08 -0500934 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400935
Tony Mack336e0f92014-11-30 15:53:08 -0500936 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700937
Tony Mack32e1ce32014-05-07 13:29:41 -0400938 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -0700939
Scott Baker63d1a552014-08-21 15:19:07 -0700940 list_display = ['backend_status_icon', 'name']
941 list_display_links = ('backend_status_icon', 'name', )
942
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400943class NodeForm(forms.ModelForm):
944 class Meta:
945 widgets = {
946 'site': LinkedSelect,
947 'deployment': LinkedSelect
948 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400949
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500950class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400951 form = NodeForm
Scott Baker63d1a552014-08-21 15:19:07 -0700952 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
953 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400954 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500955
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400956 inlines = [TagInline,SliverInline]
Scott Baker40c00762014-08-21 16:55:59 -0700957 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
958 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400959
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500960 user_readonly_fields = ['name','site','deployment']
961 user_readonly_inlines = [TagInline,SliverInline]
962
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400963 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400964
Siobhan Tully567e3e62013-06-21 18:03:16 -0400965
Tony Mackd90cdbf2013-04-16 22:48:40 -0400966class SliverForm(forms.ModelForm):
967 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -0400968 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -0400969 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -0400970 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -0400971 widgets = {
972 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -0400973 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400974 'slice': LinkedSelect,
975 'deploymentNetwork': LinkedSelect,
976 'node': LinkedSelect,
977 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -0400978 }
Tony Mackd90cdbf2013-04-16 22:48:40 -0400979
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500980class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700981 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
982 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500983 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
984 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -0400985
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400986class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -0400987 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -0400988 fieldsets = [
Scott Baker7a61dc42014-09-02 17:08:20 -0700989 ('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 -0400990 ]
Scott Baker40c00762014-08-21 16:55:59 -0700991 readonly_fields = ('backend_status_text', )
Scott Baker7a61dc42014-09-02 17:08:20 -0700992 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
Scott Baker63d1a552014-08-21 15:19:07 -0700993 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400994
995 suit_form_tabs =(('general', 'Sliver Details'),
996 ('tags','Tags'),
997 )
998
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400999 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001000
Scott Baker7a61dc42014-09-02 17:08:20 -07001001 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001002
Tony Mackc2835a92013-05-28 09:18:49 -04001003 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1004 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001005 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001006
1007 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1008
Tony Mack04062832013-05-10 08:22:44 -04001009 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001010 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001011 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001012 return Sliver.select_by_user(request.user)
1013
Tony Mack04062832013-05-10 08:22:44 -04001014
Tony Mack1d6b85f2013-05-07 18:49:14 -04001015 def get_formsets(self, request, obj=None):
1016 # make some fields read only if we are updating an existing record
1017 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -07001018 #self.readonly_fields = ('ip', 'instance_name')
Scott Bakerfbb45862014-10-17 16:27:23 -07001019 self.readonly_fields = ('backend_status_text',)
Tony Mack1d6b85f2013-05-07 18:49:14 -04001020 else:
Scott Bakerfbb45862014-10-17 16:27:23 -07001021 self.readonly_fields = ('backend_status_text',)
Scott Baker36f50872014-08-21 13:01:25 -07001022 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001023
1024 for inline in self.get_inline_instances(request, obj):
1025 # hide MyInline in the add view
1026 if obj is None:
1027 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001028 if isinstance(inline, SliverInline):
1029 inline.model.caller = request.user
1030 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001031
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001032 #def save_model(self, request, obj, form, change):
1033 # # update openstack connection to use this site/tenant
1034 # auth = request.session.get('auth', {})
1035 # auth['tenant'] = obj.slice.name
1036 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1037 # obj.creator = request.user
1038 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001039
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001040 #def delete_model(self, request, obj):
1041 # # update openstack connection to use this site/tenant
1042 # auth = request.session.get('auth', {})
1043 # auth['tenant'] = obj.slice.name
1044 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1045 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001046
Siobhan Tully53437282013-04-26 19:30:27 -04001047class UserCreationForm(forms.ModelForm):
1048 """A form for creating new users. Includes all the required
1049 fields, plus a repeated password."""
1050 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1051 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1052
1053 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001054 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001055 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001056
1057 def clean_password2(self):
1058 # Check that the two password entries match
1059 password1 = self.cleaned_data.get("password1")
1060 password2 = self.cleaned_data.get("password2")
1061 if password1 and password2 and password1 != password2:
1062 raise forms.ValidationError("Passwords don't match")
1063 return password2
1064
1065 def save(self, commit=True):
1066 # Save the provided password in hashed format
1067 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001068 user.password = self.cleaned_data["password1"]
1069 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001070 if commit:
1071 user.save()
1072 return user
1073
Siobhan Tully567e3e62013-06-21 18:03:16 -04001074
Siobhan Tully53437282013-04-26 19:30:27 -04001075class UserChangeForm(forms.ModelForm):
1076 """A form for updating users. Includes all the fields on
1077 the user, but replaces the password field with admin's
1078 password hash display field.
1079 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001080 password = ReadOnlyPasswordHashField(label='Password',
1081 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001082
1083 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001084 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001085 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001086
1087 def clean_password(self):
1088 # Regardless of what the user provides, return the initial value.
1089 # This is done here, rather than on the field, because the
1090 # field does not have access to the initial value
1091 return self.initial["password"]
1092
Scott Baker2c3cb642014-05-19 17:55:56 -07001093class UserDashboardViewInline(PlStackTabularInline):
1094 model = UserDashboardView
1095 extra = 0
1096 suit_classes = 'suit-tab suit-tab-dashboards'
1097 fields = ['user', 'dashboardView', 'order']
1098
Scott Baker86c83ab2014-10-03 13:10:47 -07001099class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1100 # Note: Make sure PermissionCheckingAdminMixin is listed before
1101 # admin.ModelAdmin in the class declaration.
1102
Siobhan Tully53437282013-04-26 19:30:27 -04001103 class Meta:
1104 app_label = "core"
1105
1106 # The forms to add and change user instances
1107 form = UserChangeForm
1108 add_form = UserCreationForm
1109
1110 # The fields to be used in displaying the User model.
1111 # These override the definitions on the base UserAdmin
1112 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001113 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001114 list_filter = ('site',)
Tony Mack336e0f92014-11-30 15:53:08 -05001115 inlines = [SlicePrivilegeInline,SitePrivilegeInline,ControllerPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001116
Scott Bakercbfb6002014-10-03 00:32:37 -07001117 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001118 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1119
Siobhan Tully53437282013-04-26 19:30:27 -04001120 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001121 ('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 -04001122 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001123 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001124 #('Important dates', {'fields': ('last_login',)}),
1125 )
1126 add_fieldsets = (
1127 (None, {
1128 'classes': ('wide',),
Scott Baker0a5633b2014-10-06 17:51:20 -07001129 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001130 ),
1131 )
Scott Baker40c00762014-08-21 16:55:59 -07001132 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001133 search_fields = ('email',)
1134 ordering = ('email',)
1135 filter_horizontal = ()
1136
Scott Baker3ca51f62014-05-23 12:05:11 -07001137 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001138
Scott Baker0a5633b2014-10-06 17:51:20 -07001139 @property
1140 def suit_form_tabs(self):
1141 if getattr(_thread_locals, "obj", None) is None:
1142 return []
1143 else:
1144 return (('general','Login Details'),
1145 ('contact','Contact Information'),
1146 ('sliceprivileges','Slice Privileges'),
1147 ('siteprivileges','Site Privileges'),
Tony Mack336e0f92014-11-30 15:53:08 -05001148 ('controllerprivileges','Controller Privileges'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001149 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001150
Tony Mackc2835a92013-05-28 09:18:49 -04001151 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1152 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001153 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001154
1155 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1156
Tony Mack5b061472014-02-04 07:57:10 -05001157 def queryset(self, request):
1158 return User.select_by_user(request.user)
1159
Scott Baker2c3cb642014-05-19 17:55:56 -07001160class DashboardViewAdmin(PlanetStackBaseAdmin):
1161 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001162 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001163 'classes': ['suit-tab suit-tab-general']})
1164 ]
Scott Baker40c00762014-08-21 16:55:59 -07001165 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001166
Scott Baker2c3cb642014-05-19 17:55:56 -07001167 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001168
Scott Baker0165fac2014-01-13 11:49:26 -08001169class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001170 model = ServiceResource
1171 extra = 0
1172
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001173class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001174 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1175 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001176 inlines = [ServiceResourceInline]
1177
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001178 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1179 user_readonly_inlines = []
1180
Scott Baker0165fac2014-01-13 11:49:26 -08001181class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001182 model = ReservedResource
1183 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001184 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001185
1186 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1187 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1188
1189 if db_field.name == 'resource':
1190 # restrict resources to those that the slice's service class allows
1191 if request._slice is not None:
1192 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1193 if len(field.queryset) > 0:
1194 field.initial = field.queryset.all()[0]
1195 else:
1196 field.queryset = field.queryset.none()
1197 elif db_field.name == 'sliver':
1198 # restrict slivers to those that belong to the slice
1199 if request._slice is not None:
1200 field.queryset = field.queryset.filter(slice = request._slice)
1201 else:
1202 field.queryset = field.queryset.none()
1203
1204 return field
1205
Tony Mack5b061472014-02-04 07:57:10 -05001206 def queryset(self, request):
1207 return ReservedResource.select_by_user(request.user)
1208
Scott Baker133c9212013-05-17 09:09:11 -07001209class ReservationChangeForm(forms.ModelForm):
1210 class Meta:
1211 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001212 widgets = {
1213 'slice' : LinkedSelect
1214 }
Scott Baker133c9212013-05-17 09:09:11 -07001215
1216class ReservationAddForm(forms.ModelForm):
1217 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1218 refresh = forms.CharField(widget=forms.HiddenInput())
1219
1220 class Media:
1221 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1222
1223 def clean_slice(self):
1224 slice = self.cleaned_data.get("slice")
1225 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1226 if len(x) == 0:
1227 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1228 return slice
1229
1230 class Meta:
1231 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001232 widgets = {
1233 'slice' : LinkedSelect
1234 }
1235
Scott Baker133c9212013-05-17 09:09:11 -07001236
1237class ReservationAddRefreshForm(ReservationAddForm):
1238 """ This form is displayed when the Reservation Form receives an update
1239 from the Slice dropdown onChange handler. It doesn't validate the
1240 data and doesn't save the data. This will cause the form to be
1241 redrawn.
1242 """
1243
Scott Baker8737e5f2013-05-17 09:35:32 -07001244 """ don't validate anything other than slice """
1245 dont_validate_fields = ("startTime", "duration")
1246
Scott Baker133c9212013-05-17 09:09:11 -07001247 def full_clean(self):
1248 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001249
1250 for fieldname in self.dont_validate_fields:
1251 if fieldname in self._errors:
1252 del self._errors[fieldname]
1253
Scott Baker133c9212013-05-17 09:09:11 -07001254 return result
1255
1256 """ don't save anything """
1257 def is_valid(self):
1258 return False
1259
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001260class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001261 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001262 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001263 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001264 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001265 form = ReservationAddForm
1266
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001267 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1268
1269 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001270 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001271
Scott Baker133c9212013-05-17 09:09:11 -07001272 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001273 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001274 request._refresh = False
1275 request._slice = None
1276 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001277 # "refresh" will be set to "1" if the form was submitted due to
1278 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001279 if request.POST.get("refresh","1") == "1":
1280 request._refresh = True
1281 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001282
1283 # Keep track of the slice that was selected, so the
1284 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001285 request._slice = request.POST.get("slice",None)
1286 if (request._slice is not None):
1287 request._slice = Slice.objects.get(id=request._slice)
1288
1289 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1290 return result
1291
Scott Bakeracd45142013-05-19 16:19:16 -07001292 def changelist_view(self, request, extra_context = None):
1293 timezone.activate(request.user.timezone)
1294 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1295
Scott Baker133c9212013-05-17 09:09:11 -07001296 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001297 request._obj_ = obj
1298 if obj is not None:
1299 # For changes, set request._slice to the slice already set in the
1300 # object.
1301 request._slice = obj.slice
1302 self.form = ReservationChangeForm
1303 else:
1304 if getattr(request, "_refresh", False):
1305 self.form = ReservationAddRefreshForm
1306 else:
1307 self.form = ReservationAddForm
1308 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1309
Scott Baker133c9212013-05-17 09:09:11 -07001310 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001311 if (obj is not None):
1312 # Prevent slice from being changed after the reservation has been
1313 # created.
1314 return ['slice']
1315 else:
Scott Baker133c9212013-05-17 09:09:11 -07001316 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001317
Tony Mack5b061472014-02-04 07:57:10 -05001318 def queryset(self, request):
1319 return Reservation.select_by_user(request.user)
1320
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001321class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001322 list_display = ("backend_status_icon", "name", )
1323 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001324 user_readonly_fields = ['name']
1325 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001326
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001327class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001328 list_display = ("backend_status_icon", "name", )
1329 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001330 user_readonly_fields = ['name']
1331 user_readonly_inlines = []
1332
Scott Baker0165fac2014-01-13 11:49:26 -08001333class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001334 model = Router.networks.through
1335 extra = 0
1336 verbose_name_plural = "Routers"
1337 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001338 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001339
Scott Bakerb27b62c2014-08-15 16:29:16 -07001340class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001341 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001342 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001343 verbose_name_plural = "Parameters"
1344 verbose_name = "Parameter"
1345 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001346 fields = ['backend_status_icon', 'parameter', 'value']
1347 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001348
Scott Baker0165fac2014-01-13 11:49:26 -08001349class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001350 fields = ['backend_status_icon', 'network','sliver','ip']
1351 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001352 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001353 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001354 extra = 0
1355 verbose_name_plural = "Slivers"
1356 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001357 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001358
Scott Baker0165fac2014-01-13 11:49:26 -08001359class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001360 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001361 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001362 extra = 0
1363 verbose_name_plural = "Slices"
1364 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001365 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001366 fields = ['backend_status_icon', 'network','slice']
1367 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001368
Tony Mack336e0f92014-11-30 15:53:08 -05001369class ControllerNetworksInline(PlStackTabularInline):
1370 model = ControllerNetworks
Scott Bakerfbb45862014-10-17 16:27:23 -07001371 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001372 verbose_name_plural = "Controller Networks"
1373 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001374 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001375 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001376 readonly_fields = ('backend_status_icon', )
1377
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001378class NetworkForm(forms.ModelForm):
1379 class Meta:
1380 model = Network
1381 widgets = {
1382 'topologyParameters': UploadTextareaWidget,
1383 'controllerParameters': UploadTextareaWidget,
1384 }
1385
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001386class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001387 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1388 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001389 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001390
Scott Bakerd7d2a392013-08-06 08:57:30 -07001391 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Mack336e0f92014-11-30 15:53:08 -05001392 admin_inlines = [ControllerNetworksInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001393
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001394 form=NetworkForm
1395
Siobhan Tully2d95e482013-09-06 10:56:06 -04001396 fieldsets = [
Scott Baker40248712014-11-17 16:04:45 -08001397 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'],
1398 'classes':['suit-tab suit-tab-general']}),
Scott Baker7b6722e2014-11-17 16:18:00 -08001399 (None, {'fields': ['topologyParameters', 'controllerUrl', 'controllerParameters'],
Scott Baker40248712014-11-17 16:04:45 -08001400 'classes':['suit-tab suit-tab-sdn']}),
1401 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001402
Scott Baker40c00762014-08-21 16:55:59 -07001403 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001404 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 -04001405
Scott Bakerfbb45862014-10-17 16:27:23 -07001406 @property
1407 def suit_form_tabs(self):
1408 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001409 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001410 ('netparams', 'Parameters'),
1411 ('networkslivers','Slivers'),
1412 ('networkslices','Slices'),
1413 ('routers','Routers'),
1414 ]
1415
1416 request=getattr(_thread_locals, "request", None)
1417 if request and request.user.is_admin:
1418 tabs.append( ('admin-only', 'Admin-Only') )
1419
1420 return tabs
1421
1422
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001423class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001424 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1425 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001426 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1427 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001428 fieldsets = [
1429 (None, {'fields': ['name', 'description', 'guaranteedBandwidth', 'visibility', 'translation', 'sharedNetworkName', 'sharedNetworkId', 'topologyKind', 'controllerKind'],
1430 'classes':['suit-tab suit-tab-general']}),]
1431 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001432
Scott Baker37b47902014-09-02 14:37:41 -07001433class FlavorAdmin(PlanetStackBaseAdmin):
1434 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1435 list_display_links = ("backend_status_icon", "name")
1436 user_readonly_fields = ("name", "flavor")
1437 fields = ("name", "description", "flavor", "order", "default")
1438
Tony Mack31c2b8f2013-04-26 20:01:42 -04001439# register a signal that caches the user's credentials when they log in
1440def cache_credentials(sender, user, request, **kwds):
1441 auth = {'username': request.POST['username'],
1442 'password': request.POST['password']}
1443 request.session['auth'] = auth
1444user_logged_in.connect(cache_credentials)
1445
Scott Baker15cddfa2013-12-09 13:45:19 -08001446def dollar_field(fieldName, short_description):
1447 def newFunc(self, obj):
1448 try:
1449 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1450 except:
1451 x=getattr(obj, fieldName, 0.0)
1452 return x
1453 newFunc.short_description = short_description
1454 return newFunc
1455
1456def right_dollar_field(fieldName, short_description):
1457 def newFunc(self, obj):
1458 try:
1459 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1460 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1461 except:
1462 x=getattr(obj, fieldName, 0.0)
1463 return x
1464 newFunc.short_description = short_description
1465 newFunc.allow_tags = True
1466 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001467
Scott Baker0165fac2014-01-13 11:49:26 -08001468class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001469 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001470 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001471 verbose_name_plural = "Charges"
1472 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001473 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001474 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1475 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1476 can_delete = False
1477 max_num = 0
1478
1479 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001480
1481class InvoiceAdmin(admin.ModelAdmin):
1482 list_display = ("date", "account")
1483
1484 inlines = [InvoiceChargeInline]
1485
Scott Baker9cb88a22013-12-09 18:56:00 -08001486 fields = ["date", "account", "dollar_amount"]
1487 readonly_fields = ["date", "account", "dollar_amount"]
1488
1489 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001490
Scott Baker0165fac2014-01-13 11:49:26 -08001491class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001492 model = Invoice
1493 extra = 0
1494 verbose_name_plural = "Invoices"
1495 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001496 fields = ["date", "dollar_amount"]
1497 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001498 suit_classes = 'suit-tab suit-tab-accountinvoice'
1499 can_delete=False
1500 max_num=0
1501
1502 dollar_amount = right_dollar_field("amount", "Amount")
1503
Scott Baker0165fac2014-01-13 11:49:26 -08001504class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001505 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001506 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001507 verbose_name_plural = "Charges"
1508 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001509 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001510 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1511 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001512 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001513 can_delete=False
1514 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001515
1516 def queryset(self, request):
1517 qs = super(PendingChargeInline, self).queryset(request)
1518 qs = qs.filter(state="pending")
1519 return qs
1520
Scott Baker15cddfa2013-12-09 13:45:19 -08001521 dollar_amount = right_dollar_field("amount", "Amount")
1522
Scott Baker0165fac2014-01-13 11:49:26 -08001523class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001524 model=Payment
1525 extra = 1
1526 verbose_name_plural = "Payments"
1527 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001528 fields = ["date", "dollar_amount"]
1529 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001530 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001531 can_delete=False
1532 max_num=0
1533
1534 dollar_amount = right_dollar_field("amount", "Amount")
1535
Scott Baker43105042013-12-06 23:23:36 -08001536class AccountAdmin(admin.ModelAdmin):
1537 list_display = ("site", "balance_due")
1538
1539 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1540
1541 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001542 (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 -08001543
Scott Baker15cddfa2013-12-09 13:45:19 -08001544 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001545
1546 suit_form_tabs =(
1547 ('general','Account Details'),
1548 ('accountinvoice', 'Invoices'),
1549 ('accountpayments', 'Payments'),
1550 ('accountpendingcharges','Pending Charges'),
1551 )
1552
Scott Baker15cddfa2013-12-09 13:45:19 -08001553 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1554 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1555 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1556
Siobhan Tully53437282013-04-26 19:30:27 -04001557# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001558admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001559# ... and, since we're not using Django's builtin permissions,
1560# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001561#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001562
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001563#Do not show django evolution in the admin interface
1564from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001565#admin.site.unregister(Version)
1566#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001567
1568
1569# When debugging it is often easier to see all the classes, but for regular use
1570# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001571showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001572
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001573admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001574admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001575admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001576admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001577admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001578admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001579admin.site.register(Network, NetworkAdmin)
1580admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001581admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001582admin.site.register(Account, AccountAdmin)
1583admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001584
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001585if True:
1586 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1587 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001588 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001589 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001590 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001591 admin.site.register(SiteRole)
1592 admin.site.register(SliceRole)
1593 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001594 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001595 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1596 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001597 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001598 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001599 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001600 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001601