blob: d1e4cd0184ca2e82bc93487097fae325a3194cdf [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'
Tony Mack528d4222014-12-05 17:13:08 -0500429 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700430 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)
Tony Mack528d4222014-12-05 17:13:08 -0500438
439 if db_field.name == 'controller':
440 kwargs['queryset'] = Controller.select_by_user(request.user)
441
Sapan Bhatia39e85582014-11-19 15:07:36 -0500442 return super(SiteDeploymentsInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400443
444 def queryset(self, request):
Sapan Bhatia39e85582014-11-19 15:07:36 -0500445 return SiteDeployments.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400446
447
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400448class SlicePrivilegeInline(PlStackTabularInline):
449 model = SlicePrivilege
450 suit_classes = 'suit-tab suit-tab-sliceprivileges'
451 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700452 fields = ('backend_status_icon', 'user', 'slice', 'role')
453 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400454
Tony Mackc2835a92013-05-28 09:18:49 -0400455 def formfield_for_foreignkey(self, db_field, request, **kwargs):
456 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700457 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400458 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700459 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400460
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400461 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400462
Tony Mack5b061472014-02-04 07:57:10 -0500463 def queryset(self, request):
464 return SlicePrivilege.select_by_user(request.user)
465
Scott Bakera0015eb2013-08-14 17:28:14 -0700466class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700467 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800468 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700469 extra = 0
470 verbose_name = "Network Connection"
471 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400472 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700473 fields = ['backend_status_icon', 'network']
474 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700475
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500476class ImageDeploymentsInline(PlStackTabularInline):
477 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700478 extra = 0
479 verbose_name = "Image Deployments"
480 verbose_name_plural = "Image Deployments"
481 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500482 fields = ['backend_status_icon', 'image', 'deployment']
483 readonly_fields = ['backend_status_icon']
484
485class ControllerImagesInline(PlStackTabularInline):
486 model = ControllerImages
487 extra = 0
488 verbose_name = "Controller Images"
489 verbose_name_plural = "Controller Images"
490 suit_classes = 'suit-tab suit-tab-admin-only'
491 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700492 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700493
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400494class SliceRoleAdmin(PlanetStackBaseAdmin):
495 model = SliceRole
496 pass
497
498class SiteRoleAdmin(PlanetStackBaseAdmin):
499 model = SiteRole
500 pass
501
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400502class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400503 sites = forms.ModelMultipleChoiceField(
504 queryset=Site.objects.all(),
505 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700506 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400507 widget=FilteredSelectMultiple(
508 verbose_name=('Sites'), is_stacked=False
509 )
510 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700511 images = forms.ModelMultipleChoiceField(
512 queryset=Image.objects.all(),
513 required=False,
514 help_text="Select which images should be deployed on this deployment",
515 widget=FilteredSelectMultiple(
516 verbose_name=('Images'), is_stacked=False
517 )
518 )
Scott Baker37b47902014-09-02 14:37:41 -0700519 flavors = forms.ModelMultipleChoiceField(
520 queryset=Flavor.objects.all(),
521 required=False,
522 help_text="Select which flavors should be usable on this deployment",
523 widget=FilteredSelectMultiple(
524 verbose_name=('Flavors'), is_stacked=False
525 )
526 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400527 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400528 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700529 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400530
Siobhan Tully320b4622014-01-17 15:11:14 -0500531 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700532 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500533 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
534
Scott Baker5380c522014-06-06 14:49:43 -0700535 self.fields['accessControl'].initial = "allow site " + request.user.site.name
536
Siobhan Tully320b4622014-01-17 15:11:14 -0500537 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800538 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments.all()]
539 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700540 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700541
542 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
543 """ helper function for handling m2m relations from the MultipleChoiceField
544
545 this_obj: the source object we want to link from
546
547 selected_objs: a list of destination objects we want to link to
548
549 all_relations: the full set of relations involving this_obj, including ones we don't want
550
551 relation_class: the class that implements the relation from source to dest
552
553 local_attrname: field name representing this_obj in relation_class
554
555 foreign_attrname: field name representing selected_objs in relation_class
556
557 This function will remove all newobjclass relations from this_obj
558 that are not contained in selected_objs, and add any relations that
559 are in selected_objs but don't exist in the data model yet.
560 """
561
562 existing_dest_objs = []
563 for relation in list(all_relations):
564 if getattr(relation, foreign_attrname) not in selected_objs:
565 #print "deleting site", sdp.site
566 relation.delete()
567 else:
568 existing_dest_objs.append(getattr(relation, foreign_attrname))
569
570 for dest_obj in selected_objs:
571 if dest_obj not in existing_dest_objs:
572 #print "adding site", site
573 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
574 relation = relation_class(**kwargs)
575 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500576
577 def save(self, commit=True):
578 deployment = super(DeploymentAdminForm, self).save(commit=False)
579
580 if commit:
581 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700582 # this has to be done after save() if/when a deployment is first created
583 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500584
585 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700586 # save_m2m() doesn't seem to work with 'through' relations. So we
587 # create/destroy the through models ourselves. There has to be
588 # a better way...
589
Sapan Bhatia39e85582014-11-19 15:07:36 -0500590 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments.all(), SiteDeployments, "deployment", "site")
Tony Mack336e0f92014-11-30 15:53:08 -0500591 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ControllerImages, "deployment", "image")
Scott Bakerc9b14f72014-05-22 13:44:20 -0700592
Scott Baker37b47902014-09-02 14:37:41 -0700593 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500594
595 return deployment
596
Scott Bakerff5e0f32014-05-22 14:40:27 -0700597class DeploymentAdminROForm(DeploymentAdminForm):
598 def save(self, commit=True):
599 raise PermissionDenied
600
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500601class SiteAssocInline(PlStackTabularInline):
602 model = Site.deployments.through
603 extra = 0
604 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400605
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400606class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500607 model = Deployment
Tony Mack2cbd3802014-09-29 16:10:52 -0400608 fieldList = ['backend_status_text', 'name', 'availability_zone', 'sites', 'images', 'flavors', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500609 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Tony Mack336e0f92014-11-30 15:53:08 -0500610 inlines = [ControllerPrivilegeInline,NodeInline,TagInline] # ,ControllerImagesInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700611 list_display = ['backend_status_icon', 'name']
612 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700613 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500614
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500615 user_readonly_fields = ['name']
616
Scott Bakerde0f4412014-06-11 15:40:26 -0700617 suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags')) # ,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500618
Scott Bakerff5e0f32014-05-22 14:40:27 -0700619 def get_form(self, request, obj=None, **kwargs):
620 if request.user.isReadOnlyUser():
621 kwargs["form"] = DeploymentAdminROForm
622 else:
623 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700624 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
625
626 # from stackexchange: pass the request object into the form
627
628 class AdminFormMetaClass(adminForm):
629 def __new__(cls, *args, **kwargs):
630 kwargs['request'] = request
631 return adminForm(*args, **kwargs)
632
633 return AdminFormMetaClass
634
Tony Mack528d4222014-12-05 17:13:08 -0500635class ControllerAdminForm(forms.ModelForm):
636 site_deployments = forms.ModelMultipleChoiceField(
637 queryset=SiteDeployment.objects.all(),
638 required=False,
639 help_text="Select which sites deployments are managed by this controller",
640 widget=FilteredSelectMultiple(
641 verbose_name=('Site Deployments'), is_stacked=False
642 )
643 )
644
645 class Meta:
646 model = Controller
647
648 def __init__(self, *args, **kwds):
649 request = kwargs.pop('request', None)
650 super(ControllerAdminForm, self).__init__(*args, **kwargs)
651
652 if self.instance and self.instance.pk:
653 self.fields['site_deployments'].initial = [x.site_deployment for x in self.instance.controllersitedeployments.all()]
654
655 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
656 """ helper function for handling m2m relations from the MultipleChoiceField
657 this_obj: the source object we want to link from
658 selected_objs: a list of destination objects we want to link to
659 all_relations: the full set of relations involving this_obj, including ones we don't want
660 relation_class: the class that implements the relation from source to dest
661 local_attrname: field name representing this_obj in relation_class
662 foreign_attrname: field name representing selected_objs in relation_class
663 This function will remove all newobjclass relations from this_obj
664 that are not contained in selected_objs, and add any relations that
665 are in selected_objs but don't exist in the data model yet.
666 """
667 existing_dest_objs = []
668 for relation in list(all_relations):
669 if getattr(relation, foreign_attrname) not in selected_objs:
670 #print "deleting site", sdp.site
671 relation.delete()
672 else:
673 existing_dest_objs.append(getattr(relation, foreign_attrname))
674
675 for dest_obj in selected_objs:
676 if dest_obj not in existing_dest_objs:
677 #print "adding site", site
678 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
679 relation = relation_class(**kwargs)
680 relation.save()
681
682 def save(self, commit=True):
683 controller = super(ControllerAdminForm, self).save(commit=False)
684
685 if commit:
686 controller.save()
687
688 if controller.pk:
689 # save_m2m() doesn't seem to work with 'through' relations. So we
690 # create/destroy the through models ourselves. There has to be
691 # a better way...
692 self.manipulate_m2m_objs(controller, self.cleaned_data['site_deployments'], controller.controllersitedeployments.all(), ControllerSiteDeployments, "controller", "site_deployment")
693
694 self.save_m2m()
695
696 return controller
697
698class ControllerAdmin(PlanetStackBaseAdmin):
699 model = Controller
700 fieldList = ['name', 'version', 'backend_type', 'auth_url', 'admin_user', 'admin_tenant',]
701 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-admin-only']})]
702 inlines = [ControllerPrivilegeInline, ContrllerSiteInline] # ,ControllerImagesInline]
703 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
704 list_display_links = ('backend_status_icon', 'name', )
705 readonly_fields = ('backend_status_text',)
706
707 user_readonly_fields = []
708
709 def get_form(self, request, obj=None, **kwargs):
710 if request.user.isReadOnlyUser():
711 kwargs["form"] = ControllerAdminROForm
712 else:
713 kwargs["form"] = ControllerAdminForm
714 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
715
716 # from stackexchange: pass the request object into the form
717
718 class AdminFormMetaClass(adminForm):
719 def __new__(cls, *args, **kwargs):
720 kwargs['request'] = request
721 return adminForm(*args, **kwargs)
722
723 return AdminFormMetaClass
724
Siobhan Tullyce652d02013-10-08 21:52:35 -0400725class ServiceAttrAsTabInline(PlStackTabularInline):
726 model = ServiceAttribute
727 fields = ['name','value']
728 extra = 0
729 suit_classes = 'suit-tab suit-tab-serviceattrs'
730
Siobhan Tullyce652d02013-10-08 21:52:35 -0400731class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700732 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
733 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700734 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500735 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
736 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700737 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500738
739 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500740
741 suit_form_tabs =(('general', 'Service Details'),
742 ('slices','Slices'),
743 ('serviceattrs','Additional Attributes'),
744 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400745
Tony Mack0553f282013-06-10 22:54:50 -0400746class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700747 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400748 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500749 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400750 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400751 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400752 suit_form_tabs =(('general', 'Site Details'),
753 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400754 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400755 ('deployments','Deployments'),
756 ('slices','Slices'),
Scott Baker36f50872014-08-21 13:01:25 -0700757 ('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400758 ('tags','Tags'),
759 )
Scott Baker40c00762014-08-21 16:55:59 -0700760 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500761
762 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500763
Scott Baker63d1a552014-08-21 15:19:07 -0700764 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
765 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400766 filter_horizontal = ('deployments',)
Sapan Bhatia39e85582014-11-19 15:07:36 -0500767 inlines = [SliceInline,UserInline,TagInline, NodeInline, SitePrivilegeInline, SiteDeploymentsInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400768 search_fields = ['name']
769
Tony Mack04062832013-05-10 08:22:44 -0400770 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500771 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400772
Tony Mack5cd13202013-05-01 21:48:38 -0400773 def get_formsets(self, request, obj=None):
774 for inline in self.get_inline_instances(request, obj):
775 # hide MyInline in the add view
776 if obj is None:
777 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400778 if isinstance(inline, SliverInline):
779 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400780 yield inline.get_formset(request, obj)
781
Scott Baker545db2a2013-12-09 18:44:43 -0800782 def accountLink(self, obj):
783 link_obj = obj.accounts.all()
784 if link_obj:
785 reverse_path = "admin:core_account_change"
786 url = reverse(reverse_path, args =(link_obj[0].id,))
787 return "<a href='%s'>%s</a>" % (url, "view billing details")
788 else:
789 return "no billing data for this site"
790 accountLink.allow_tags = True
791 accountLink.short_description = "Billing"
792
Tony Mack332ee1d2014-02-04 15:33:45 -0500793 def save_model(self, request, obj, form, change):
794 # update openstack connection to use this site/tenant
795 obj.save_by_user(request.user)
796
797 def delete_model(self, request, obj):
798 obj.delete_by_user(request.user)
799
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500800
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400801class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700802 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400803 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500804 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400805 ]
Scott Baker40c00762014-08-21 16:55:59 -0700806 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700807 list_display = ('backend_status_icon', 'user', 'site', 'role')
808 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500809 user_readonly_fields = fieldList
810 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400811
Tony Mackc2835a92013-05-28 09:18:49 -0400812 def formfield_for_foreignkey(self, db_field, request, **kwargs):
813 if db_field.name == 'site':
814 if not request.user.is_admin:
815 # only show sites where user is an admin or pi
816 sites = set()
817 for site_privilege in SitePrivilege.objects.filer(user=request.user):
818 if site_privilege.role.role_type in ['admin', 'pi']:
819 sites.add(site_privilege.site)
820 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
821
822 if db_field.name == 'user':
823 if not request.user.is_admin:
824 # only show users from sites where caller has admin or pi role
825 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
826 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
827 sites = [site_privilege.site for site_privilege in site_privileges]
828 site_privileges = SitePrivilege.objects.filter(site__in=sites)
829 emails = [site_privilege.user.email for site_privilege in site_privileges]
830 users = User.objects.filter(email__in=emails)
831 kwargs['queryset'] = users
832
833 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
834
Tony Mack04062832013-05-10 08:22:44 -0400835 def queryset(self, request):
836 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400837 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400838 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500839 #if not request.user.is_admin:
840 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
841 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
842 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
843 # sites = Site.objects.filter(login_base__in=login_bases)
844 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400845 return qs
846
Siobhan Tullyce652d02013-10-08 21:52:35 -0400847class SliceForm(forms.ModelForm):
848 class Meta:
849 model = Slice
850 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700851 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400852 }
853
Tony Mack2cbd3802014-09-29 16:10:52 -0400854 def clean(self):
855 cleaned_data = super(SliceForm, self).clean()
856 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -0700857 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -0400858 slice_id = self.instance.id
859 if not site and slice_id:
860 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -0700861 if (not isinstance(site,Site)):
862 # previous code indicates 'site' could be a site_id and not a site?
863 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -0400864 if not name.startswith(site.login_base):
865 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
866 return cleaned_data
867
Tony Mack336e0f92014-11-30 15:53:08 -0500868class ControllerSlicesInline(PlStackTabularInline):
869 model = ControllerSlices
Scott Bakerc4efdc72014-10-15 16:54:04 -0700870 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500871 verbose_name = "Controller Slices"
872 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -0700873 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -0500874 fields = ['backend_status_icon', 'controller', 'tenant_id']
Scott Bakerc4efdc72014-10-15 16:54:04 -0700875 readonly_fields = ('backend_status_icon', )
876
Tony Mack2bd5b412013-06-11 21:05:06 -0400877class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400878 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400879 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500880 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700881 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400882 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
883 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400884 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Mack336e0f92014-11-30 15:53:08 -0500885 admin_inlines = [ControllerSlicesInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400886
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500887 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400888
Scott Bakerc4efdc72014-10-15 16:54:04 -0700889 @property
890 def suit_form_tabs(self):
891 tabs =[('general', 'Slice Details'),
892 ('slicenetworks','Networks'),
893 ('sliceprivileges','Privileges'),
894 ('slivers','Slivers'),
895 ('tags','Tags'),
896 ('reservations','Reservations'),
897 ]
898
899 request=getattr(_thread_locals, "request", None)
900 if request and request.user.is_admin:
901 tabs.append( ('admin-only', 'Admin-Only') )
902
903 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -0400904
905 def add_view(self, request, form_url='', extra_context=None):
906 # revert to default read-only fields
907 self.readonly_fields = ('backend_status_text',)
908 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
909
910 def change_view(self, request, object_id, form_url='', extra_context=None):
911 print object_id
912 # cannot change the site of an existing slice so make the site field read only
913 if object_id:
914 self.readonly_fields = ('backend_status_text','site')
915 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400916
Scott Baker510fdbb2014-08-05 17:19:24 -0700917 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700918 deployment_nodes = []
919 for node in Node.objects.all():
920 deployment_nodes.append( (node.deployment.id, node.id, node.name) )
921
Scott Baker7a61dc42014-09-02 17:08:20 -0700922 deployment_flavors = []
923 for flavor in Flavor.objects.all():
924 for deployment in flavor.deployments.all():
925 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
926
Tony Mack336e0f92014-11-30 15:53:08 -0500927 controller_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -0700928 for image in Image.objects.all():
Tony Mack336e0f92014-11-30 15:53:08 -0500929 for controller_image in image.controllerimages.all():
930 controller_images.append( (controller_image.controller.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -0700931
Tony Mackec23b992014-09-02 21:18:45 -0400932 site_login_bases = []
933 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -0700934 site_login_bases.append((site.id, site.login_base))
935
Scott Baker510fdbb2014-08-05 17:19:24 -0700936 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700937 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -0700938 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400939 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700940 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
941
Tony Mackc2835a92013-05-28 09:18:49 -0400942 def formfield_for_foreignkey(self, db_field, request, **kwargs):
943 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500944 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400945 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 -0700946
Tony Mackc2835a92013-05-28 09:18:49 -0400947 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
948
Tony Mack04062832013-05-10 08:22:44 -0400949 def queryset(self, request):
950 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500951 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400952
Tony Mack79748612013-05-01 14:52:03 -0400953 def get_formsets(self, request, obj=None):
954 for inline in self.get_inline_instances(request, obj):
955 # hide MyInline in the add view
956 if obj is None:
957 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400958 if isinstance(inline, SliverInline):
959 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400960 yield inline.get_formset(request, obj)
961
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400962class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400963 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700964 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400965 ]
Scott Baker40c00762014-08-21 16:55:59 -0700966 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700967 list_display = ('backend_status_icon', 'user', 'slice', 'role')
968 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400969
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500970 user_readonly_fields = ['user', 'slice', 'role']
971 user_readonly_inlines = []
972
Tony Mackc2835a92013-05-28 09:18:49 -0400973 def formfield_for_foreignkey(self, db_field, request, **kwargs):
974 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500975 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400976
977 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500978 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400979
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400980 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400981
Tony Mack04062832013-05-10 08:22:44 -0400982 def queryset(self, request):
983 # admins can see all memberships. Users can only see memberships of
984 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500985 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400986
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400987 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400988 # update openstack connection to use this site/tenant
989 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400990 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400991 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400992 obj.save()
993
994 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400995 # update openstack connection to use this site/tenant
996 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400997 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400998 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400999 obj.delete()
1000
Siobhan Tully567e3e62013-06-21 18:03:16 -04001001
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001002class ImageAdmin(PlanetStackBaseAdmin):
1003
Scott Baker36f50872014-08-21 13:01:25 -07001004 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001005 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001006 'classes': ['suit-tab suit-tab-general']})
1007 ]
Scott Baker40c00762014-08-21 16:55:59 -07001008 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001009
Tony Mack336e0f92014-11-30 15:53:08 -05001010 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001011
Tony Mack336e0f92014-11-30 15:53:08 -05001012 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001013
Tony Mack32e1ce32014-05-07 13:29:41 -04001014 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001015
Scott Baker63d1a552014-08-21 15:19:07 -07001016 list_display = ['backend_status_icon', 'name']
1017 list_display_links = ('backend_status_icon', 'name', )
1018
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001019class NodeForm(forms.ModelForm):
1020 class Meta:
1021 widgets = {
1022 'site': LinkedSelect,
1023 'deployment': LinkedSelect
1024 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001025
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001026class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001027 form = NodeForm
Scott Baker63d1a552014-08-21 15:19:07 -07001028 list_display = ('backend_status_icon', 'name', 'site', 'deployment')
1029 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001030 list_filter = ('deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001031
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001032 inlines = [TagInline,SliverInline]
Scott Baker40c00762014-08-21 16:55:59 -07001033 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site','deployment'], 'classes':['suit-tab suit-tab-details']})]
1034 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001035
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001036 user_readonly_fields = ['name','site','deployment']
1037 user_readonly_inlines = [TagInline,SliverInline]
1038
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001039 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001040
Siobhan Tully567e3e62013-06-21 18:03:16 -04001041
Tony Mackd90cdbf2013-04-16 22:48:40 -04001042class SliverForm(forms.ModelForm):
1043 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001044 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001045 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001046 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001047 widgets = {
1048 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001049 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001050 'slice': LinkedSelect,
1051 'deploymentNetwork': LinkedSelect,
1052 'node': LinkedSelect,
1053 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001054 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001055
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001056class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001057 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1058 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001059 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1060 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001061
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001062class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001063 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001064 fieldsets = [
Scott Baker7a61dc42014-09-02 17:08:20 -07001065 ('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 -04001066 ]
Scott Baker40c00762014-08-21 16:55:59 -07001067 readonly_fields = ('backend_status_text', )
Scott Baker7a61dc42014-09-02 17:08:20 -07001068 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deploymentNetwork']
Scott Baker63d1a552014-08-21 15:19:07 -07001069 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001070
1071 suit_form_tabs =(('general', 'Sliver Details'),
1072 ('tags','Tags'),
1073 )
1074
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001075 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001076
Scott Baker7a61dc42014-09-02 17:08:20 -07001077 user_readonly_fields = ['slice', 'deploymentNetwork', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001078
Tony Mackc2835a92013-05-28 09:18:49 -04001079 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1080 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001081 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001082
1083 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1084
Tony Mack04062832013-05-10 08:22:44 -04001085 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001086 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001087 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001088 return Sliver.select_by_user(request.user)
1089
Tony Mack04062832013-05-10 08:22:44 -04001090
Tony Mack1d6b85f2013-05-07 18:49:14 -04001091 def get_formsets(self, request, obj=None):
1092 # make some fields read only if we are updating an existing record
1093 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -07001094 #self.readonly_fields = ('ip', 'instance_name')
Scott Bakerfbb45862014-10-17 16:27:23 -07001095 self.readonly_fields = ('backend_status_text',)
Tony Mack1d6b85f2013-05-07 18:49:14 -04001096 else:
Scott Bakerfbb45862014-10-17 16:27:23 -07001097 self.readonly_fields = ('backend_status_text',)
Scott Baker36f50872014-08-21 13:01:25 -07001098 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001099
1100 for inline in self.get_inline_instances(request, obj):
1101 # hide MyInline in the add view
1102 if obj is None:
1103 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001104 if isinstance(inline, SliverInline):
1105 inline.model.caller = request.user
1106 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001107
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001108 #def save_model(self, request, obj, form, change):
1109 # # update openstack connection to use this site/tenant
1110 # auth = request.session.get('auth', {})
1111 # auth['tenant'] = obj.slice.name
1112 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1113 # obj.creator = request.user
1114 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001115
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001116 #def delete_model(self, request, obj):
1117 # # update openstack connection to use this site/tenant
1118 # auth = request.session.get('auth', {})
1119 # auth['tenant'] = obj.slice.name
1120 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1121 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001122
Siobhan Tully53437282013-04-26 19:30:27 -04001123class UserCreationForm(forms.ModelForm):
1124 """A form for creating new users. Includes all the required
1125 fields, plus a repeated password."""
1126 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1127 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1128
1129 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001130 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001131 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001132
1133 def clean_password2(self):
1134 # Check that the two password entries match
1135 password1 = self.cleaned_data.get("password1")
1136 password2 = self.cleaned_data.get("password2")
1137 if password1 and password2 and password1 != password2:
1138 raise forms.ValidationError("Passwords don't match")
1139 return password2
1140
1141 def save(self, commit=True):
1142 # Save the provided password in hashed format
1143 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001144 user.password = self.cleaned_data["password1"]
1145 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001146 if commit:
1147 user.save()
1148 return user
1149
Siobhan Tully567e3e62013-06-21 18:03:16 -04001150
Siobhan Tully53437282013-04-26 19:30:27 -04001151class UserChangeForm(forms.ModelForm):
1152 """A form for updating users. Includes all the fields on
1153 the user, but replaces the password field with admin's
1154 password hash display field.
1155 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001156 password = ReadOnlyPasswordHashField(label='Password',
1157 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001158
1159 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001160 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001161 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001162
1163 def clean_password(self):
1164 # Regardless of what the user provides, return the initial value.
1165 # This is done here, rather than on the field, because the
1166 # field does not have access to the initial value
1167 return self.initial["password"]
1168
Scott Baker2c3cb642014-05-19 17:55:56 -07001169class UserDashboardViewInline(PlStackTabularInline):
1170 model = UserDashboardView
1171 extra = 0
1172 suit_classes = 'suit-tab suit-tab-dashboards'
1173 fields = ['user', 'dashboardView', 'order']
1174
Scott Baker86c83ab2014-10-03 13:10:47 -07001175class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1176 # Note: Make sure PermissionCheckingAdminMixin is listed before
1177 # admin.ModelAdmin in the class declaration.
1178
Siobhan Tully53437282013-04-26 19:30:27 -04001179 class Meta:
1180 app_label = "core"
1181
1182 # The forms to add and change user instances
1183 form = UserChangeForm
1184 add_form = UserCreationForm
1185
1186 # The fields to be used in displaying the User model.
1187 # These override the definitions on the base UserAdmin
1188 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001189 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001190 list_filter = ('site',)
Tony Mack336e0f92014-11-30 15:53:08 -05001191 inlines = [SlicePrivilegeInline,SitePrivilegeInline,ControllerPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001192
Scott Bakercbfb6002014-10-03 00:32:37 -07001193 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001194 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1195
Siobhan Tully53437282013-04-26 19:30:27 -04001196 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001197 ('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 -04001198 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001199 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001200 #('Important dates', {'fields': ('last_login',)}),
1201 )
1202 add_fieldsets = (
1203 (None, {
1204 'classes': ('wide',),
Scott Baker0a5633b2014-10-06 17:51:20 -07001205 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001206 ),
1207 )
Scott Baker40c00762014-08-21 16:55:59 -07001208 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001209 search_fields = ('email',)
1210 ordering = ('email',)
1211 filter_horizontal = ()
1212
Scott Baker3ca51f62014-05-23 12:05:11 -07001213 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001214
Scott Baker0a5633b2014-10-06 17:51:20 -07001215 @property
1216 def suit_form_tabs(self):
1217 if getattr(_thread_locals, "obj", None) is None:
1218 return []
1219 else:
1220 return (('general','Login Details'),
1221 ('contact','Contact Information'),
1222 ('sliceprivileges','Slice Privileges'),
1223 ('siteprivileges','Site Privileges'),
Tony Mack336e0f92014-11-30 15:53:08 -05001224 ('controllerprivileges','Controller Privileges'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001225 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001226
Tony Mackc2835a92013-05-28 09:18:49 -04001227 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1228 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001229 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001230
1231 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1232
Tony Mack5b061472014-02-04 07:57:10 -05001233 def queryset(self, request):
1234 return User.select_by_user(request.user)
1235
Scott Baker2c3cb642014-05-19 17:55:56 -07001236class DashboardViewAdmin(PlanetStackBaseAdmin):
1237 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001238 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001239 'classes': ['suit-tab suit-tab-general']})
1240 ]
Scott Baker40c00762014-08-21 16:55:59 -07001241 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001242
Scott Baker2c3cb642014-05-19 17:55:56 -07001243 suit_form_tabs =(('general','Dashboard View Details'),)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001244
Scott Baker0165fac2014-01-13 11:49:26 -08001245class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001246 model = ServiceResource
1247 extra = 0
1248
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001249class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001250 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1251 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001252 inlines = [ServiceResourceInline]
1253
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001254 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1255 user_readonly_inlines = []
1256
Scott Baker0165fac2014-01-13 11:49:26 -08001257class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001258 model = ReservedResource
1259 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001260 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001261
1262 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1263 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1264
1265 if db_field.name == 'resource':
1266 # restrict resources to those that the slice's service class allows
1267 if request._slice is not None:
1268 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1269 if len(field.queryset) > 0:
1270 field.initial = field.queryset.all()[0]
1271 else:
1272 field.queryset = field.queryset.none()
1273 elif db_field.name == 'sliver':
1274 # restrict slivers to those that belong to the slice
1275 if request._slice is not None:
1276 field.queryset = field.queryset.filter(slice = request._slice)
1277 else:
1278 field.queryset = field.queryset.none()
1279
1280 return field
1281
Tony Mack5b061472014-02-04 07:57:10 -05001282 def queryset(self, request):
1283 return ReservedResource.select_by_user(request.user)
1284
Scott Baker133c9212013-05-17 09:09:11 -07001285class ReservationChangeForm(forms.ModelForm):
1286 class Meta:
1287 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001288 widgets = {
1289 'slice' : LinkedSelect
1290 }
Scott Baker133c9212013-05-17 09:09:11 -07001291
1292class ReservationAddForm(forms.ModelForm):
1293 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1294 refresh = forms.CharField(widget=forms.HiddenInput())
1295
1296 class Media:
1297 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1298
1299 def clean_slice(self):
1300 slice = self.cleaned_data.get("slice")
1301 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1302 if len(x) == 0:
1303 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1304 return slice
1305
1306 class Meta:
1307 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001308 widgets = {
1309 'slice' : LinkedSelect
1310 }
1311
Scott Baker133c9212013-05-17 09:09:11 -07001312
1313class ReservationAddRefreshForm(ReservationAddForm):
1314 """ This form is displayed when the Reservation Form receives an update
1315 from the Slice dropdown onChange handler. It doesn't validate the
1316 data and doesn't save the data. This will cause the form to be
1317 redrawn.
1318 """
1319
Scott Baker8737e5f2013-05-17 09:35:32 -07001320 """ don't validate anything other than slice """
1321 dont_validate_fields = ("startTime", "duration")
1322
Scott Baker133c9212013-05-17 09:09:11 -07001323 def full_clean(self):
1324 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001325
1326 for fieldname in self.dont_validate_fields:
1327 if fieldname in self._errors:
1328 del self._errors[fieldname]
1329
Scott Baker133c9212013-05-17 09:09:11 -07001330 return result
1331
1332 """ don't save anything """
1333 def is_valid(self):
1334 return False
1335
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001336class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001337 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001338 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001339 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001340 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001341 form = ReservationAddForm
1342
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001343 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1344
1345 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001346 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001347
Scott Baker133c9212013-05-17 09:09:11 -07001348 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001349 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001350 request._refresh = False
1351 request._slice = None
1352 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001353 # "refresh" will be set to "1" if the form was submitted due to
1354 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001355 if request.POST.get("refresh","1") == "1":
1356 request._refresh = True
1357 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001358
1359 # Keep track of the slice that was selected, so the
1360 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001361 request._slice = request.POST.get("slice",None)
1362 if (request._slice is not None):
1363 request._slice = Slice.objects.get(id=request._slice)
1364
1365 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1366 return result
1367
Scott Bakeracd45142013-05-19 16:19:16 -07001368 def changelist_view(self, request, extra_context = None):
1369 timezone.activate(request.user.timezone)
1370 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1371
Scott Baker133c9212013-05-17 09:09:11 -07001372 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001373 request._obj_ = obj
1374 if obj is not None:
1375 # For changes, set request._slice to the slice already set in the
1376 # object.
1377 request._slice = obj.slice
1378 self.form = ReservationChangeForm
1379 else:
1380 if getattr(request, "_refresh", False):
1381 self.form = ReservationAddRefreshForm
1382 else:
1383 self.form = ReservationAddForm
1384 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1385
Scott Baker133c9212013-05-17 09:09:11 -07001386 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001387 if (obj is not None):
1388 # Prevent slice from being changed after the reservation has been
1389 # created.
1390 return ['slice']
1391 else:
Scott Baker133c9212013-05-17 09:09:11 -07001392 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001393
Tony Mack5b061472014-02-04 07:57:10 -05001394 def queryset(self, request):
1395 return Reservation.select_by_user(request.user)
1396
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001397class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001398 list_display = ("backend_status_icon", "name", )
1399 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001400 user_readonly_fields = ['name']
1401 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001402
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001403class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001404 list_display = ("backend_status_icon", "name", )
1405 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001406 user_readonly_fields = ['name']
1407 user_readonly_inlines = []
1408
Scott Baker0165fac2014-01-13 11:49:26 -08001409class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001410 model = Router.networks.through
1411 extra = 0
1412 verbose_name_plural = "Routers"
1413 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001414 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001415
Scott Bakerb27b62c2014-08-15 16:29:16 -07001416class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001417 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001418 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001419 verbose_name_plural = "Parameters"
1420 verbose_name = "Parameter"
1421 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001422 fields = ['backend_status_icon', 'parameter', 'value']
1423 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001424
Scott Baker0165fac2014-01-13 11:49:26 -08001425class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001426 fields = ['backend_status_icon', 'network','sliver','ip']
1427 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001428 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001429 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001430 extra = 0
1431 verbose_name_plural = "Slivers"
1432 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001433 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001434
Scott Baker0165fac2014-01-13 11:49:26 -08001435class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001436 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001437 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001438 extra = 0
1439 verbose_name_plural = "Slices"
1440 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001441 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001442 fields = ['backend_status_icon', 'network','slice']
1443 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001444
Tony Mack336e0f92014-11-30 15:53:08 -05001445class ControllerNetworksInline(PlStackTabularInline):
1446 model = ControllerNetworks
Scott Bakerfbb45862014-10-17 16:27:23 -07001447 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001448 verbose_name_plural = "Controller Networks"
1449 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001450 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001451 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001452 readonly_fields = ('backend_status_icon', )
1453
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001454class NetworkForm(forms.ModelForm):
1455 class Meta:
1456 model = Network
1457 widgets = {
1458 'topologyParameters': UploadTextareaWidget,
1459 'controllerParameters': UploadTextareaWidget,
1460 }
1461
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001462class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001463 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1464 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001465 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001466
Scott Bakerd7d2a392013-08-06 08:57:30 -07001467 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Mack336e0f92014-11-30 15:53:08 -05001468 admin_inlines = [ControllerNetworksInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001469
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001470 form=NetworkForm
1471
Siobhan Tully2d95e482013-09-06 10:56:06 -04001472 fieldsets = [
Scott Baker40248712014-11-17 16:04:45 -08001473 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'],
1474 'classes':['suit-tab suit-tab-general']}),
Scott Baker7b6722e2014-11-17 16:18:00 -08001475 (None, {'fields': ['topologyParameters', 'controllerUrl', 'controllerParameters'],
Scott Baker40248712014-11-17 16:04:45 -08001476 'classes':['suit-tab suit-tab-sdn']}),
1477 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001478
Scott Baker40c00762014-08-21 16:55:59 -07001479 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001480 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 -04001481
Scott Bakerfbb45862014-10-17 16:27:23 -07001482 @property
1483 def suit_form_tabs(self):
1484 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001485 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001486 ('netparams', 'Parameters'),
1487 ('networkslivers','Slivers'),
1488 ('networkslices','Slices'),
1489 ('routers','Routers'),
1490 ]
1491
1492 request=getattr(_thread_locals, "request", None)
1493 if request and request.user.is_admin:
1494 tabs.append( ('admin-only', 'Admin-Only') )
1495
1496 return tabs
1497
1498
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001499class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001500 list_display = ("backend_status_icon", "name", "guaranteedBandwidth", "visibility")
1501 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001502 user_readonly_fields = ["name", "guaranteedBandwidth", "visibility"]
1503 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001504 fieldsets = [
1505 (None, {'fields': ['name', 'description', 'guaranteedBandwidth', 'visibility', 'translation', 'sharedNetworkName', 'sharedNetworkId', 'topologyKind', 'controllerKind'],
1506 'classes':['suit-tab suit-tab-general']}),]
1507 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001508
Scott Baker37b47902014-09-02 14:37:41 -07001509class FlavorAdmin(PlanetStackBaseAdmin):
1510 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1511 list_display_links = ("backend_status_icon", "name")
1512 user_readonly_fields = ("name", "flavor")
1513 fields = ("name", "description", "flavor", "order", "default")
1514
Tony Mack31c2b8f2013-04-26 20:01:42 -04001515# register a signal that caches the user's credentials when they log in
1516def cache_credentials(sender, user, request, **kwds):
1517 auth = {'username': request.POST['username'],
1518 'password': request.POST['password']}
1519 request.session['auth'] = auth
1520user_logged_in.connect(cache_credentials)
1521
Scott Baker15cddfa2013-12-09 13:45:19 -08001522def dollar_field(fieldName, short_description):
1523 def newFunc(self, obj):
1524 try:
1525 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1526 except:
1527 x=getattr(obj, fieldName, 0.0)
1528 return x
1529 newFunc.short_description = short_description
1530 return newFunc
1531
1532def right_dollar_field(fieldName, short_description):
1533 def newFunc(self, obj):
1534 try:
1535 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1536 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1537 except:
1538 x=getattr(obj, fieldName, 0.0)
1539 return x
1540 newFunc.short_description = short_description
1541 newFunc.allow_tags = True
1542 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001543
Scott Baker0165fac2014-01-13 11:49:26 -08001544class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001545 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001546 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001547 verbose_name_plural = "Charges"
1548 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001549 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001550 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1551 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1552 can_delete = False
1553 max_num = 0
1554
1555 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001556
1557class InvoiceAdmin(admin.ModelAdmin):
1558 list_display = ("date", "account")
1559
1560 inlines = [InvoiceChargeInline]
1561
Scott Baker9cb88a22013-12-09 18:56:00 -08001562 fields = ["date", "account", "dollar_amount"]
1563 readonly_fields = ["date", "account", "dollar_amount"]
1564
1565 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001566
Scott Baker0165fac2014-01-13 11:49:26 -08001567class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001568 model = Invoice
1569 extra = 0
1570 verbose_name_plural = "Invoices"
1571 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001572 fields = ["date", "dollar_amount"]
1573 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001574 suit_classes = 'suit-tab suit-tab-accountinvoice'
1575 can_delete=False
1576 max_num=0
1577
1578 dollar_amount = right_dollar_field("amount", "Amount")
1579
Scott Baker0165fac2014-01-13 11:49:26 -08001580class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001581 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001582 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001583 verbose_name_plural = "Charges"
1584 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001585 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001586 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1587 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001588 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001589 can_delete=False
1590 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001591
1592 def queryset(self, request):
1593 qs = super(PendingChargeInline, self).queryset(request)
1594 qs = qs.filter(state="pending")
1595 return qs
1596
Scott Baker15cddfa2013-12-09 13:45:19 -08001597 dollar_amount = right_dollar_field("amount", "Amount")
1598
Scott Baker0165fac2014-01-13 11:49:26 -08001599class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001600 model=Payment
1601 extra = 1
1602 verbose_name_plural = "Payments"
1603 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001604 fields = ["date", "dollar_amount"]
1605 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001606 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001607 can_delete=False
1608 max_num=0
1609
1610 dollar_amount = right_dollar_field("amount", "Amount")
1611
Scott Baker43105042013-12-06 23:23:36 -08001612class AccountAdmin(admin.ModelAdmin):
1613 list_display = ("site", "balance_due")
1614
1615 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1616
1617 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001618 (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 -08001619
Scott Baker15cddfa2013-12-09 13:45:19 -08001620 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001621
1622 suit_form_tabs =(
1623 ('general','Account Details'),
1624 ('accountinvoice', 'Invoices'),
1625 ('accountpayments', 'Payments'),
1626 ('accountpendingcharges','Pending Charges'),
1627 )
1628
Scott Baker15cddfa2013-12-09 13:45:19 -08001629 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1630 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1631 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1632
Siobhan Tully53437282013-04-26 19:30:27 -04001633# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001634admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001635# ... and, since we're not using Django's builtin permissions,
1636# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001637#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001638
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001639#Do not show django evolution in the admin interface
1640from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001641#admin.site.unregister(Version)
1642#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001643
1644
1645# When debugging it is often easier to see all the classes, but for regular use
1646# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001647showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001648
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001649admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001650admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001651admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001652admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001653admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001654admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001655admin.site.register(Network, NetworkAdmin)
1656admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001657admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001658admin.site.register(Account, AccountAdmin)
1659admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001660
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001661if True:
1662 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1663 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001664 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001665 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001666 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001667 admin.site.register(SiteRole)
1668 admin.site.register(SliceRole)
1669 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001670 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001671 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1672 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001673 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001674 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001675 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001676 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001677