blob: b2ecafa53fa4b4de990e6744abdd4e5d2d78e0ac [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 Bakerb6b474d2015-02-10 18:24:20 -080029ICON_URLS = {"success": "/static/admin/img/icon_success.gif",
30 "clock": "/static/admin/img/icon_clock.gif",
31 "error": "/static/admin/img/icon_error.gif"}
32
33def backend_icon(obj):
34 (icon, tooltip) = obj.get_backend_icon()
35 icon_url = ICON_URLS.get(icon, "unknown")
36
37 if tooltip:
38 return '<span style="min-width:16px;" title="%s"><img src="%s"></span>' % (tooltip, icon_url)
Scott Baker40c00762014-08-21 16:55:59 -070039 else:
Scott Bakerb6b474d2015-02-10 18:24:20 -080040 return '<span style="min-width:16px;"><img src="%s"></span>' % icon_url
Scott Baker40c00762014-08-21 16:55:59 -070041
42def backend_text(obj):
Scott Bakerb6b474d2015-02-10 18:24:20 -080043 (icon, tooltip) = obj.get_backend_icon()
44 icon_url = ICON_URLS.get(icon, "unknown")
45
46 return '<img src="%s"> %s' % (icon_url, tooltip)
Scott Baker63d1a552014-08-21 15:19:07 -070047
Scott Baker9f6b8ed2014-11-17 23:44:03 -080048class UploadTextareaWidget(AdminTextareaWidget):
49 def render(self, name, value, attrs=None):
50 if value is None:
51 value = ''
52 final_attrs = self.build_attrs(attrs, name=name)
53 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
54 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
55 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
56 flatatt(final_attrs),
57 force_text(value))
58
Scott Baker36f50872014-08-21 13:01:25 -070059class PlainTextWidget(forms.HiddenInput):
60 input_type = 'hidden'
61
62 def render(self, name, value, attrs=None):
63 if value is None:
64 value = ''
65 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
66
Scott Baker86c83ab2014-10-03 13:10:47 -070067class PermissionCheckingAdminMixin(object):
Scott Bakercbfb6002014-10-03 00:32:37 -070068 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050069
70 def has_add_permission(self, request, obj=None):
71 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070072
Siobhan Tullycf04fb62014-01-11 11:25:57 -050073 def has_delete_permission(self, request, obj=None):
74 return (not self.__user_is_readonly(request))
75
76 def save_model(self, request, obj, form, change):
77 if self.__user_is_readonly(request):
Scott Bakercbfb6002014-10-03 00:32:37 -070078 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050079 raise PermissionDenied
Scott Bakercbfb6002014-10-03 00:32:37 -070080
81 obj.caller = request.user
82 # update openstack connection to use this site/tenant
83 obj.save_by_user(request.user)
84
85 def delete_model(self, request, obj):
86 obj.delete_by_user(request.user)
87
88 def save_formset(self, request, form, formset, change):
89 instances = formset.save(commit=False)
90 for instance in instances:
91 instance.save_by_user(request.user)
92
93 # BUG in django 1.7? Objects are not deleted by formset.save if
94 # commit is False. So let's delete them ourselves.
95 #
96 # code from forms/models.py save_existing_objects()
97 try:
98 forms_to_delete = formset.deleted_forms
99 except AttributeError:
100 forms_to_delete = []
101 if formset.initial_forms:
102 for form in formset.initial_forms:
103 obj = form.instance
104 if form in forms_to_delete:
105 if obj.pk is None:
106 continue
107 formset.deleted_objects.append(obj)
108 obj.delete()
109
110 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500111
112 def get_actions(self,request):
Scott Baker86c83ab2014-10-03 13:10:47 -0700113 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500114
115 if self.__user_is_readonly(request):
116 if 'delete_selected' in actions:
117 del actions['delete_selected']
118
119 return actions
120
121 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500122 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -0700123 if not hasattr(self, "readonly_save"):
124 # save the original readonly fields
125 self.readonly_save = self.readonly_fields
126 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -0700127 if hasattr(self, "user_readonly_fields"):
128 self.readonly_fields=self.user_readonly_fields
129 if hasattr(self, "user_readonly_inlines"):
130 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -0700131 else:
132 if hasattr(self, "readonly_save"):
133 # restore the original readonly fields
134 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -0700135 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700136 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500137
138 try:
Scott Baker86c83ab2014-10-03 13:10:47 -0700139 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500140 except PermissionDenied:
141 pass
142 if request.method == 'POST':
143 raise PermissionDenied
144 request.readonly = True
Scott Baker86c83ab2014-10-03 13:10:47 -0700145 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500146
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500147 def __user_is_readonly(self, request):
148 return request.user.isReadOnlyUser()
149
Scott Baker40c00762014-08-21 16:55:59 -0700150 def backend_status_text(self, obj):
151 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700152
Scott Baker63d1a552014-08-21 15:19:07 -0700153 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700154 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700155 backend_status_icon.short_description = ""
156
Scott Baker24ded6a2014-11-05 09:05:38 -0800157 def get_form(self, request, obj=None, **kwargs):
Scott Baker5c432692014-10-16 00:57:55 -0700158 # Save obj and request in thread-local storage, so suit_form_tabs can
159 # use it to determine whether we're in edit or add mode, and can
160 # determine whether the user is an admin.
161 _thread_locals.request = request
162 _thread_locals.obj = obj
Scott Baker24ded6a2014-11-05 09:05:38 -0800163 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker5c432692014-10-16 00:57:55 -0700164
165 def get_inline_instances(self, request, obj=None):
166 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
167
168 # inlines that should only be shown to an admin user
169 if request.user.is_admin:
170 for inline_class in getattr(self, "admin_inlines", []):
171 inlines.append(inline_class(self.model, self.admin_site))
172
173 return inlines
174
Scott Baker86c83ab2014-10-03 13:10:47 -0700175class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
176 # Note: Make sure PermissionCheckingAdminMixin is listed before
177 # admin.ModelAdmin in the class declaration.
178
Scott Bakercbfb6002014-10-03 00:32:37 -0700179 pass
180
181class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
182 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700183
Scott Bakere8859f92014-05-23 12:42:40 -0700184class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400185 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700186 if not super(SingletonAdmin, self).has_add_permission(request):
187 return False
188
Siobhan Tullyce652d02013-10-08 21:52:35 -0400189 num_objects = self.model.objects.count()
190 if num_objects >= 1:
191 return False
192 else:
193 return True
194
Siobhan Tullyd3515752013-06-21 16:34:53 -0400195class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800196 def __init__(self, *args, **kwargs):
197 super(PlStackTabularInline, self).__init__(*args, **kwargs)
198
199 # InlineModelAdmin as no get_fields() method, so in order to add
200 # the selflink field, we override __init__ to modify self.fields and
201 # self.readonly_fields.
202
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800203 self.setup_selflink()
204
Scott Baker874936e2014-01-13 18:15:34 -0800205 def get_change_url(self, model, id):
206 """ Get the URL to a change form in the admin for this model """
207 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800208 try:
Scott Baker874936e2014-01-13 18:15:34 -0800209 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800210 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800211 return None
212
213 return url
214
215 def setup_selflink(self):
216 if hasattr(self, "selflink_fieldname"):
217 """ self.selflink_model can be defined to punch through a relation
218 to its target object. For example, in SliceNetworkInline, set
219 selflink_model = "network", and the URL will lead to the Network
220 object instead of trying to bring up a change view of the
221 SliceNetwork object.
222 """
223 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
224 else:
225 self.selflink_model = self.model
226
227 url = self.get_change_url(self.selflink_model, 0)
228
229 # We don't have an admin for this object, so don't create the
230 # selflink.
231 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800232 return
233
Scott Baker874936e2014-01-13 18:15:34 -0800234 # Since we need to add "selflink" to the field list, we need to create
235 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800236 if (self.fields is None):
237 self.fields = []
238 for f in self.model._meta.fields:
239 if f.editable and f.name != "id":
240 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800241
Scott Baker874936e2014-01-13 18:15:34 -0800242 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800243
Scott Baker874936e2014-01-13 18:15:34 -0800244 if self.readonly_fields is None:
245 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800246
Scott Baker874936e2014-01-13 18:15:34 -0800247 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800248
249 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800250 if hasattr(self, "selflink_fieldname"):
251 obj = getattr(obj, self.selflink_fieldname)
252
Scott Baker86568322014-01-12 16:53:31 -0800253 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800254 url = self.get_change_url(self.selflink_model, obj.id)
255 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800256 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800257 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800258
259 selflink.allow_tags = True
260 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400261
Scott Bakerb27b62c2014-08-15 16:29:16 -0700262 def has_add_permission(self, request):
263 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500264
265 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700266 readonly_fields = list(self.readonly_fields)[:]
267 if request.user.isReadOnlyUser():
268 for field in self.fields:
269 if not field in readonly_fields:
270 readonly_fields.append(field)
271 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500272
Scott Baker40c00762014-08-21 16:55:59 -0700273 def backend_status_icon(self, obj):
274 return mark_safe(backend_icon(obj))
275 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700276
Scott Bakerb27b62c2014-08-15 16:29:16 -0700277class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500278 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700279 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500280
Scott Bakerb27b62c2014-08-15 16:29:16 -0700281 def get_readonly_fields(self, request, obj=None):
282 readonly_fields = list(self.readonly_fields)[:]
283 if request.user.isReadOnlyUser():
284 for field in self.fields:
285 if not field in readonly_fields:
286 readonly_fields.append(field)
287 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500288
Scott Baker40c00762014-08-21 16:55:59 -0700289 def backend_status_icon(self, obj):
290 return mark_safe(backend_icon(obj))
291 backend_status_icon.short_description = ""
292
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400293class ReservationInline(PlStackTabularInline):
294 model = Reservation
295 extra = 0
296 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700297
Tony Mack5b061472014-02-04 07:57:10 -0500298 def queryset(self, request):
299 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400300
Scott Bakerb27b62c2014-08-15 16:29:16 -0700301class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400302 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400303 extra = 0
304 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500305 fields = ['service', 'name', 'value']
306
307 def queryset(self, request):
308 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400309
Scott Baker74d8e622013-07-29 16:04:22 -0700310class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400311 """ This is a callable that looks up a network name in a sliver and returns
312 the ip address for that network.
313 """
314
Scott Baker434ca7e2014-08-15 12:29:20 -0700315 byNetworkName = {} # class variable
316
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400317 def __init__(self, name):
318 self.short_description = name
319 self.__name__ = name
320 self.network_name = name
321
322 def __call__(self, obj):
323 if obj is not None:
324 for nbs in obj.networksliver_set.all():
325 if (nbs.network.name == self.network_name):
326 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700327 return ""
328
329 def __str__(self):
330 return self.network_name
331
Scott Baker434ca7e2014-08-15 12:29:20 -0700332 @staticmethod
333 def get(network_name):
334 """ We want to make sure we alwars return the same NetworkLookerUpper
335 because sometimes django will cause them to be instantiated multiple
336 times (and we don't want different ones in form.fields vs
337 SliverInline.readonly_fields).
338 """
339 if network_name not in NetworkLookerUpper.byNetworkName:
340 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
341 return NetworkLookerUpper.byNetworkName[network_name]
342
Siobhan Tullyd3515752013-06-21 16:34:53 -0400343class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400344 model = Sliver
Scott Baker887d4a82015-01-19 11:32:20 -0800345 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400346 extra = 0
Scott Baker887d4a82015-01-19 11:32:20 -0800347 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400348 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700349
Tony Mack5b061472014-02-04 07:57:10 -0500350 def queryset(self, request):
351 return Sliver.select_by_user(request.user)
352
Scott Bakerb24cc932014-06-09 10:51:16 -0700353 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackbf6aa302014-12-26 13:38:02 -0500354 if db_field.name == 'deployment':
Scott Baker3b678742014-06-09 13:11:54 -0700355 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker7a61dc42014-09-02 17:08:20 -0700356 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Tony Mackbf6aa302014-12-26 13:38:02 -0500357 if db_field.name == 'flavor':
Scott Baker32481312014-09-08 12:14:14 -0700358 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700359
360 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700361
362 return field
363
Siobhan Tullyd3515752013-06-21 16:34:53 -0400364class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400365 model = Site
366 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400367 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400368
Tony Mack5b061472014-02-04 07:57:10 -0500369 def queryset(self, request):
370 return Site.select_by_user(request.user)
371
Siobhan Tullyd3515752013-06-21 16:34:53 -0400372class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400373 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700374 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
375 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400376 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400377 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400378
Tony Mack5b061472014-02-04 07:57:10 -0500379 def queryset(self, request):
380 return User.select_by_user(request.user)
381
Siobhan Tullyd3515752013-06-21 16:34:53 -0400382class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400383 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700384 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
385 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400386 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400387 suit_classes = 'suit-tab suit-tab-slices'
388
Tony Mack5b061472014-02-04 07:57:10 -0500389 def queryset(self, request):
390 return Slice.select_by_user(request.user)
391
Siobhan Tullyd3515752013-06-21 16:34:53 -0400392class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400393 model = Node
394 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400395 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack93d1b032014-12-08 16:43:02 -0500396 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700397 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400398
Tony Mack93d1b032014-12-08 16:43:02 -0500399class DeploymentPrivilegeInline(PlStackTabularInline):
400 model = DeploymentPrivilege
401 extra = 0
Tony Mack4ce14c42015-02-09 21:41:57 -0500402 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack93d1b032014-12-08 16:43:02 -0500403 fields = ['backend_status_icon', 'user','role','deployment']
404 readonly_fields = ('backend_status_icon', )
405
406 def queryset(self, request):
407 return DeploymentPrivilege.select_by_user(request.user)
408
Tony Macka7dbd422015-01-05 22:48:11 -0500409class ControllerSiteInline(PlStackTabularInline):
410 model = ControllerSite
411 extra = 0
412 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack8f30ebe2015-01-06 15:08:20 -0500413 fields = ['controller', 'site', 'tenant_id']
Tony Macka7dbd422015-01-05 22:48:11 -0500414
415
Siobhan Tullyd3515752013-06-21 16:34:53 -0400416class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400417 model = SitePrivilege
418 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400419 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700420 fields = ['backend_status_icon', 'user','site', 'role']
421 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400422
Tony Mackc2835a92013-05-28 09:18:49 -0400423 def formfield_for_foreignkey(self, db_field, request, **kwargs):
424 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500425 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400426
427 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500428 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400429 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
430
Tony Mack5b061472014-02-04 07:57:10 -0500431 def queryset(self, request):
432 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400433
Tony Macka7dbd422015-01-05 22:48:11 -0500434class SiteDeploymentInline(PlStackTabularInline):
435 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400436 extra = 0
Tony Mack10812252015-01-30 10:58:29 -0500437 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mack528d4222014-12-05 17:13:08 -0500438 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700439 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400440
441 def formfield_for_foreignkey(self, db_field, request, **kwargs):
442 if db_field.name == 'site':
443 kwargs['queryset'] = Site.select_by_user(request.user)
444
445 if db_field.name == 'deployment':
446 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mack528d4222014-12-05 17:13:08 -0500447
448 if db_field.name == 'controller':
449 kwargs['queryset'] = Controller.select_by_user(request.user)
450
Tony Macka7dbd422015-01-05 22:48:11 -0500451 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400452
453 def queryset(self, request):
Tony Macka7dbd422015-01-05 22:48:11 -0500454 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400455
456
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400457class SlicePrivilegeInline(PlStackTabularInline):
458 model = SlicePrivilege
459 suit_classes = 'suit-tab suit-tab-sliceprivileges'
460 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700461 fields = ('backend_status_icon', 'user', 'slice', 'role')
462 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400463
Tony Mackc2835a92013-05-28 09:18:49 -0400464 def formfield_for_foreignkey(self, db_field, request, **kwargs):
465 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700466 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400467 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700468 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400469
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400470 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400471
Tony Mack5b061472014-02-04 07:57:10 -0500472 def queryset(self, request):
473 return SlicePrivilege.select_by_user(request.user)
474
Scott Bakera0015eb2013-08-14 17:28:14 -0700475class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700476 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800477 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700478 extra = 0
479 verbose_name = "Network Connection"
480 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400481 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700482 fields = ['backend_status_icon', 'network']
483 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700484
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500485class ImageDeploymentsInline(PlStackTabularInline):
486 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700487 extra = 0
488 verbose_name = "Image Deployments"
489 verbose_name_plural = "Image Deployments"
490 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500491 fields = ['backend_status_icon', 'image', 'deployment']
492 readonly_fields = ['backend_status_icon']
493
494class ControllerImagesInline(PlStackTabularInline):
495 model = ControllerImages
496 extra = 0
497 verbose_name = "Controller Images"
498 verbose_name_plural = "Controller Images"
499 suit_classes = 'suit-tab suit-tab-admin-only'
500 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700501 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700502
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400503class SliceRoleAdmin(PlanetStackBaseAdmin):
504 model = SliceRole
505 pass
506
507class SiteRoleAdmin(PlanetStackBaseAdmin):
508 model = SiteRole
509 pass
510
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400511class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700512 images = forms.ModelMultipleChoiceField(
513 queryset=Image.objects.all(),
514 required=False,
515 help_text="Select which images should be deployed on this deployment",
516 widget=FilteredSelectMultiple(
517 verbose_name=('Images'), is_stacked=False
518 )
519 )
Scott Baker37b47902014-09-02 14:37:41 -0700520 flavors = forms.ModelMultipleChoiceField(
521 queryset=Flavor.objects.all(),
522 required=False,
523 help_text="Select which flavors should be usable on this deployment",
524 widget=FilteredSelectMultiple(
525 verbose_name=('Flavors'), is_stacked=False
526 )
527 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400528 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400529 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700530 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400531
Siobhan Tully320b4622014-01-17 15:11:14 -0500532 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700533 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500534 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
535
Scott Baker5380c522014-06-06 14:49:43 -0700536 self.fields['accessControl'].initial = "allow site " + request.user.site.name
537
Siobhan Tully320b4622014-01-17 15:11:14 -0500538 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800539 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
Tony Mack592aa952014-12-15 11:45:02 -0500590 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
591 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
592 # so well handle that manually here
593 for flavor in deployment.flavors.all():
594 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mack11f4d202014-12-15 12:37:59 -0500595 deployment.flavors.remove(flavor)
Tony Mack592aa952014-12-15 11:45:02 -0500596 for flavor in self.cleaned_data['flavors']:
597 if flavor not in deployment.flavors.all():
598 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700599
Scott Baker37b47902014-09-02 14:37:41 -0700600 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500601
602 return deployment
603
Scott Bakerff5e0f32014-05-22 14:40:27 -0700604class DeploymentAdminROForm(DeploymentAdminForm):
605 def save(self, commit=True):
606 raise PermissionDenied
607
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500608class SiteAssocInline(PlStackTabularInline):
609 model = Site.deployments.through
610 extra = 0
611 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400612
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400613class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500614 model = Deployment
Scott Bakerae233f42015-02-10 08:40:34 -0800615 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
616 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack93d1b032014-12-08 16:43:02 -0500617 # node no longer directly connected to deployment
618 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mack10812252015-01-30 10:58:29 -0500619 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700620 list_display = ['backend_status_icon', 'name']
621 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700622 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500623
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500624 user_readonly_fields = ['name']
625
Tony Mack93d1b032014-12-08 16:43:02 -0500626 # nodes no longer direclty connected to deployments
Scott Bakerae233f42015-02-10 08:40:34 -0800627 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500628
Scott Bakerff5e0f32014-05-22 14:40:27 -0700629 def get_form(self, request, obj=None, **kwargs):
Tony Mackd893dfb2015-02-05 06:13:04 -0500630 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700631 kwargs["form"] = DeploymentAdminROForm
632 else:
633 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700634 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
635
636 # from stackexchange: pass the request object into the form
637
638 class AdminFormMetaClass(adminForm):
639 def __new__(cls, *args, **kwargs):
640 kwargs['request'] = request
641 return adminForm(*args, **kwargs)
642
643 return AdminFormMetaClass
644
Tony Mack528d4222014-12-05 17:13:08 -0500645class ControllerAdmin(PlanetStackBaseAdmin):
Scott Bakerae233f42015-02-10 08:40:34 -0800646 model = Controller
Tony Mack4cea77e2015-02-10 12:43:07 -0500647 fieldList = ['name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
Scott Bakerae233f42015-02-10 08:40:34 -0800648 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Macka7dbd422015-01-05 22:48:11 -0500649 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500650 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
651 list_display_links = ('backend_status_icon', 'name', )
652 readonly_fields = ('backend_status_text',)
653
654 user_readonly_fields = []
655
Tony Mackc36cafb2015-01-13 17:33:08 -0500656 def save_model(self, request, obj, form, change):
657 # update openstack connection to use this site/tenant
658 obj.save_by_user(request.user)
659
660 def delete_model(self, request, obj):
Scott Bakerae233f42015-02-10 08:40:34 -0800661 obj.delete_by_user(request.user)
662
663 @property
664 def suit_form_tabs(self):
665 tabs = [('general', 'Controller Details'),
666 ]
667
668 request=getattr(_thread_locals, "request", None)
669 if request and request.user.is_admin:
670 tabs.append( ('admin-only', 'Admin-Only') )
671
672 return tabs
Tony Mackc36cafb2015-01-13 17:33:08 -0500673
Siobhan Tullyce652d02013-10-08 21:52:35 -0400674class ServiceAttrAsTabInline(PlStackTabularInline):
675 model = ServiceAttribute
676 fields = ['name','value']
677 extra = 0
678 suit_classes = 'suit-tab suit-tab-serviceattrs'
679
Siobhan Tullyce652d02013-10-08 21:52:35 -0400680class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700681 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
682 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700683 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500684 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
685 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700686 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500687
688 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500689
690 suit_form_tabs =(('general', 'Service Details'),
691 ('slices','Slices'),
692 ('serviceattrs','Additional Attributes'),
693 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400694
Tony Mack82df1d02015-01-14 20:58:38 -0500695class SiteNodeInline(PlStackTabularInline):
696 model = Node
697 fields = ['name', 'site_deployment']
698 extra = 0
699 suit_classes = 'suit-tab suit-tab-nodes'
700
Tony Mack0553f282013-06-10 22:54:50 -0400701class SiteAdmin(PlanetStackBaseAdmin):
Tony Mack598eaf22015-01-25 12:35:29 -0500702 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
703 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400704 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500705 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400706 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400707 ]
Tony Mack598eaf22015-01-25 12:35:29 -0500708 #readonly_fields = ['backend_status_text', 'accountLink']
709 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500710
Tony Mack598eaf22015-01-25 12:35:29 -0500711 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
712 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500713
Scott Baker63d1a552014-08-21 15:19:07 -0700714 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
715 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400716 filter_horizontal = ('deployments',)
Tony Mack10812252015-01-30 10:58:29 -0500717 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mack10328a12015-01-14 12:11:05 -0500718 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400719 search_fields = ['name']
720
Tony Mack3c01ff92015-01-10 23:08:10 -0500721 @property
722 def suit_form_tabs(self):
723 tabs = [('general', 'Site Details'),
724 ('users','Users'),
725 ('siteprivileges','Privileges'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500726 ('slices','Slices'),
Tony Mack82df1d02015-01-14 20:58:38 -0500727 ('nodes','Nodes'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500728 ]
729
730 request=getattr(_thread_locals, "request", None)
731 if request and request.user.is_admin:
732 tabs.append( ('admin-only', 'Admin-Only') )
733
734 return tabs
735
Tony Mack04062832013-05-10 08:22:44 -0400736 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500737 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400738
Tony Mack5cd13202013-05-01 21:48:38 -0400739 def get_formsets(self, request, obj=None):
740 for inline in self.get_inline_instances(request, obj):
741 # hide MyInline in the add view
742 if obj is None:
743 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400744 if isinstance(inline, SliverInline):
745 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400746 yield inline.get_formset(request, obj)
747
Scott Baker545db2a2013-12-09 18:44:43 -0800748 def accountLink(self, obj):
749 link_obj = obj.accounts.all()
750 if link_obj:
751 reverse_path = "admin:core_account_change"
752 url = reverse(reverse_path, args =(link_obj[0].id,))
753 return "<a href='%s'>%s</a>" % (url, "view billing details")
754 else:
755 return "no billing data for this site"
756 accountLink.allow_tags = True
757 accountLink.short_description = "Billing"
758
Tony Mack332ee1d2014-02-04 15:33:45 -0500759 def save_model(self, request, obj, form, change):
760 # update openstack connection to use this site/tenant
761 obj.save_by_user(request.user)
762
763 def delete_model(self, request, obj):
764 obj.delete_by_user(request.user)
765
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500766
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400767class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700768 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400769 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500770 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400771 ]
Scott Baker40c00762014-08-21 16:55:59 -0700772 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700773 list_display = ('backend_status_icon', 'user', 'site', 'role')
774 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500775 user_readonly_fields = fieldList
776 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400777
Tony Mackc2835a92013-05-28 09:18:49 -0400778 def formfield_for_foreignkey(self, db_field, request, **kwargs):
779 if db_field.name == 'site':
780 if not request.user.is_admin:
781 # only show sites where user is an admin or pi
782 sites = set()
783 for site_privilege in SitePrivilege.objects.filer(user=request.user):
784 if site_privilege.role.role_type in ['admin', 'pi']:
785 sites.add(site_privilege.site)
786 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
787
788 if db_field.name == 'user':
789 if not request.user.is_admin:
790 # only show users from sites where caller has admin or pi role
791 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
792 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
793 sites = [site_privilege.site for site_privilege in site_privileges]
794 site_privileges = SitePrivilege.objects.filter(site__in=sites)
795 emails = [site_privilege.user.email for site_privilege in site_privileges]
796 users = User.objects.filter(email__in=emails)
797 kwargs['queryset'] = users
798
799 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
800
Tony Mack04062832013-05-10 08:22:44 -0400801 def queryset(self, request):
802 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400803 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400804 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500805 #if not request.user.is_admin:
806 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
807 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
808 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
809 # sites = Site.objects.filter(login_base__in=login_bases)
810 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400811 return qs
812
Siobhan Tullyce652d02013-10-08 21:52:35 -0400813class SliceForm(forms.ModelForm):
814 class Meta:
815 model = Slice
816 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700817 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400818 }
819
Tony Mack2cbd3802014-09-29 16:10:52 -0400820 def clean(self):
821 cleaned_data = super(SliceForm, self).clean()
822 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -0700823 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -0400824 slice_id = self.instance.id
825 if not site and slice_id:
826 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -0700827 if (not isinstance(site,Site)):
828 # previous code indicates 'site' could be a site_id and not a site?
829 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -0400830 if not name.startswith(site.login_base):
831 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
832 return cleaned_data
833
Tony Macka7dbd422015-01-05 22:48:11 -0500834class ControllerSliceInline(PlStackTabularInline):
835 model = ControllerSlice
Scott Bakerc4efdc72014-10-15 16:54:04 -0700836 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500837 verbose_name = "Controller Slices"
838 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -0700839 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -0500840 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack3c01ff92015-01-10 23:08:10 -0500841 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerc4efdc72014-10-15 16:54:04 -0700842
Tony Mack2bd5b412013-06-11 21:05:06 -0400843class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400844 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400845 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500846 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700847 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400848 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
849 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400850 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Macka7dbd422015-01-05 22:48:11 -0500851 admin_inlines = [ControllerSliceInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400852
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500853 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400854
Scott Bakerc4efdc72014-10-15 16:54:04 -0700855 @property
856 def suit_form_tabs(self):
857 tabs =[('general', 'Slice Details'),
858 ('slicenetworks','Networks'),
859 ('sliceprivileges','Privileges'),
860 ('slivers','Slivers'),
Tony Mack598eaf22015-01-25 12:35:29 -0500861 #('reservations','Reservations'),
Tony Mackb34553e2015-01-15 14:44:06 -0500862 ('tags','Tags'),
Scott Bakerc4efdc72014-10-15 16:54:04 -0700863 ]
864
865 request=getattr(_thread_locals, "request", None)
866 if request and request.user.is_admin:
867 tabs.append( ('admin-only', 'Admin-Only') )
868
869 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -0400870
871 def add_view(self, request, form_url='', extra_context=None):
872 # revert to default read-only fields
873 self.readonly_fields = ('backend_status_text',)
874 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
875
876 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -0400877 # cannot change the site of an existing slice so make the site field read only
878 if object_id:
879 self.readonly_fields = ('backend_status_text','site')
880 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400881
Scott Baker510fdbb2014-08-05 17:19:24 -0700882 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700883 deployment_nodes = []
884 for node in Node.objects.all():
Scott Baker39293d72015-01-21 16:24:07 -0800885 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -0700886
Scott Baker7a61dc42014-09-02 17:08:20 -0700887 deployment_flavors = []
888 for flavor in Flavor.objects.all():
889 for deployment in flavor.deployments.all():
890 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
891
Tony Mack93d1b032014-12-08 16:43:02 -0500892 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -0700893 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -0500894 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -0800895 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -0700896
Tony Mackec23b992014-09-02 21:18:45 -0400897 site_login_bases = []
898 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -0700899 site_login_bases.append((site.id, site.login_base))
900
Scott Baker510fdbb2014-08-05 17:19:24 -0700901 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700902 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -0700903 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400904 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700905 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
906
Tony Mackc2835a92013-05-28 09:18:49 -0400907 def formfield_for_foreignkey(self, db_field, request, **kwargs):
908 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500909 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400910 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 -0700911
Tony Mackc2835a92013-05-28 09:18:49 -0400912 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
913
Tony Mack04062832013-05-10 08:22:44 -0400914 def queryset(self, request):
915 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500916 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400917
Tony Mack79748612013-05-01 14:52:03 -0400918 def get_formsets(self, request, obj=None):
919 for inline in self.get_inline_instances(request, obj):
920 # hide MyInline in the add view
921 if obj is None:
922 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400923 if isinstance(inline, SliverInline):
924 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400925 yield inline.get_formset(request, obj)
926
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400927class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400928 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700929 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400930 ]
Scott Baker40c00762014-08-21 16:55:59 -0700931 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700932 list_display = ('backend_status_icon', 'user', 'slice', 'role')
933 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400934
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500935 user_readonly_fields = ['user', 'slice', 'role']
936 user_readonly_inlines = []
937
Tony Mackc2835a92013-05-28 09:18:49 -0400938 def formfield_for_foreignkey(self, db_field, request, **kwargs):
939 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500940 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400941
942 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500943 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400944
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400945 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400946
Tony Mack04062832013-05-10 08:22:44 -0400947 def queryset(self, request):
948 # admins can see all memberships. Users can only see memberships of
949 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500950 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400951
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400952 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400953 # update openstack connection to use this site/tenant
954 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400955 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400956 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400957 obj.save()
958
959 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400960 # update openstack connection to use this site/tenant
961 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400962 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400963 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400964 obj.delete()
965
Siobhan Tully567e3e62013-06-21 18:03:16 -0400966
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400967class ImageAdmin(PlanetStackBaseAdmin):
968
Scott Baker36f50872014-08-21 13:01:25 -0700969 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -0700970 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400971 'classes': ['suit-tab suit-tab-general']})
972 ]
Scott Baker40c00762014-08-21 16:55:59 -0700973 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400974
Tony Mack336e0f92014-11-30 15:53:08 -0500975 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400976
Tony Mack336e0f92014-11-30 15:53:08 -0500977 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700978
Tony Mack32e1ce32014-05-07 13:29:41 -0400979 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -0700980
Scott Baker63d1a552014-08-21 15:19:07 -0700981 list_display = ['backend_status_icon', 'name']
982 list_display_links = ('backend_status_icon', 'name', )
983
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400984class NodeForm(forms.ModelForm):
985 class Meta:
986 widgets = {
987 'site': LinkedSelect,
988 'deployment': LinkedSelect
989 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400990
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500991class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400992 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -0500993 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -0700994 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -0500995 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500996
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400997 inlines = [TagInline,SliverInline]
Tony Mack93d1b032014-12-08 16:43:02 -0500998 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -0700999 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001000
Tony Mack93d1b032014-12-08 16:43:02 -05001001 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001002 user_readonly_inlines = [TagInline,SliverInline]
1003
Tony Mackab6538f2015-01-12 21:40:09 -05001004 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001005
Siobhan Tully567e3e62013-06-21 18:03:16 -04001006
Tony Mackd90cdbf2013-04-16 22:48:40 -04001007class SliverForm(forms.ModelForm):
1008 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001009 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001010 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001011 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001012 widgets = {
1013 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001014 'instance_name': PlainTextWidget(),
Scott Baker887d4a82015-01-19 11:32:20 -08001015 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001016 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001017 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001018 'node': LinkedSelect,
1019 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001020 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001021
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001022class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001023 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1024 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001025 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1026 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001027
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001028class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001029 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001030 fieldsets = [
Tony Mackd8336582015-01-30 12:52:46 -05001031 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'flavor', 'image', 'ssh_command'], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001032 ]
Tony Mack7d61efb2015-01-30 17:20:46 -05001033 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mackd8336582015-01-30 12:52:46 -05001034 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
1035 list_display_links = ('backend_status_icon', 'all_ips_string',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001036
Scott Baker1eace6f2015-01-19 08:24:08 -08001037 suit_form_tabs =(('general', 'Sliver Details'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001038
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001039 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001040
Tony Mackbf6aa302014-12-26 13:38:02 -05001041 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001042
Scott Baker970314b2015-01-25 22:16:13 -08001043 def ssh_command(self, obj):
1044 ssh_command = obj.get_ssh_command()
1045 if ssh_command:
1046 return ssh_command
1047 else:
1048 return "(not available)"
1049
Tony Mackc2835a92013-05-28 09:18:49 -04001050 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1051 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001052 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001053
1054 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1055
Tony Mack04062832013-05-10 08:22:44 -04001056 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001057 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001058 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001059 return Sliver.select_by_user(request.user)
1060
Tony Mack04062832013-05-10 08:22:44 -04001061
Tony Mack1d6b85f2013-05-07 18:49:14 -04001062 def get_formsets(self, request, obj=None):
1063 # make some fields read only if we are updating an existing record
1064 if obj == None:
Tony Mack26dbd8d2015-01-30 17:42:10 -05001065 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001066 else:
Tony Mack26dbd8d2015-01-30 17:42:10 -05001067 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'slice', 'flavor', 'image', 'node')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001068
1069 for inline in self.get_inline_instances(request, obj):
1070 # hide MyInline in the add view
1071 if obj is None:
1072 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001073 if isinstance(inline, SliverInline):
1074 inline.model.caller = request.user
1075 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001076
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001077 #def save_model(self, request, obj, form, change):
1078 # # update openstack connection to use this site/tenant
1079 # auth = request.session.get('auth', {})
1080 # auth['tenant'] = obj.slice.name
1081 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1082 # obj.creator = request.user
1083 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001084
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001085 #def delete_model(self, request, obj):
1086 # # update openstack connection to use this site/tenant
1087 # auth = request.session.get('auth', {})
1088 # auth['tenant'] = obj.slice.name
1089 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1090 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001091
Siobhan Tully53437282013-04-26 19:30:27 -04001092class UserCreationForm(forms.ModelForm):
1093 """A form for creating new users. Includes all the required
1094 fields, plus a repeated password."""
1095 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1096 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1097
1098 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001099 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001100 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001101
1102 def clean_password2(self):
1103 # Check that the two password entries match
1104 password1 = self.cleaned_data.get("password1")
1105 password2 = self.cleaned_data.get("password2")
1106 if password1 and password2 and password1 != password2:
1107 raise forms.ValidationError("Passwords don't match")
1108 return password2
1109
1110 def save(self, commit=True):
1111 # Save the provided password in hashed format
1112 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001113 user.password = self.cleaned_data["password1"]
1114 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001115 if commit:
1116 user.save()
1117 return user
1118
Siobhan Tully567e3e62013-06-21 18:03:16 -04001119
Siobhan Tully53437282013-04-26 19:30:27 -04001120class UserChangeForm(forms.ModelForm):
1121 """A form for updating users. Includes all the fields on
1122 the user, but replaces the password field with admin's
1123 password hash display field.
1124 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001125 password = ReadOnlyPasswordHashField(label='Password',
1126 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001127
1128 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001129 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001130 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001131
1132 def clean_password(self):
1133 # Regardless of what the user provides, return the initial value.
1134 # This is done here, rather than on the field, because the
1135 # field does not have access to the initial value
1136 return self.initial["password"]
1137
Scott Baker2c3cb642014-05-19 17:55:56 -07001138class UserDashboardViewInline(PlStackTabularInline):
1139 model = UserDashboardView
1140 extra = 0
1141 suit_classes = 'suit-tab suit-tab-dashboards'
1142 fields = ['user', 'dashboardView', 'order']
1143
Tony Mack3c01ff92015-01-10 23:08:10 -05001144class ControllerUserInline(PlStackTabularInline):
1145 model = ControllerUser
1146 extra = 0
1147 suit_classes = 'suit-tab suit-tab-admin-only'
1148 fields = ['controller', 'user', 'kuser_id']
1149 readonly_fields=['controller']
1150
1151
Scott Baker86c83ab2014-10-03 13:10:47 -07001152class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1153 # Note: Make sure PermissionCheckingAdminMixin is listed before
1154 # admin.ModelAdmin in the class declaration.
1155
Siobhan Tully53437282013-04-26 19:30:27 -04001156 class Meta:
1157 app_label = "core"
1158
1159 # The forms to add and change user instances
1160 form = UserChangeForm
1161 add_form = UserCreationForm
1162
1163 # The fields to be used in displaying the User model.
1164 # These override the definitions on the base UserAdmin
1165 # that reference specific fields on auth.User.
Scott Bakerf587f442015-01-24 13:33:26 -08001166 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1167 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001168 list_filter = ('site',)
Scott Baker6e9027f2015-01-29 10:55:53 -08001169 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack3c01ff92015-01-10 23:08:10 -05001170 admin_inlines = [ControllerUserInline]
Scott Bakercbfb6002014-10-03 00:32:37 -07001171 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001172 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1173
Siobhan Tully53437282013-04-26 19:30:27 -04001174 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001175 ('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 -04001176 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001177 #('Important dates', {'fields': ('last_login',)}),
1178 )
1179 add_fieldsets = (
1180 (None, {
1181 'classes': ('wide',),
Tony Mackb195b5f2015-01-28 12:03:15 -05001182 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001183 ),
1184 )
Scott Baker40c00762014-08-21 16:55:59 -07001185 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001186 search_fields = ('email',)
1187 ordering = ('email',)
1188 filter_horizontal = ()
1189
Scott Baker3ca51f62014-05-23 12:05:11 -07001190 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001191
Scott Baker0a5633b2014-10-06 17:51:20 -07001192 @property
1193 def suit_form_tabs(self):
1194 if getattr(_thread_locals, "obj", None) is None:
1195 return []
1196 else:
Tony Mack3c01ff92015-01-10 23:08:10 -05001197 tabs = [('general','Login Details'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001198 ('contact','Contact Information'),
1199 ('sliceprivileges','Slice Privileges'),
Scott Baker6e9027f2015-01-29 10:55:53 -08001200 ('siteprivileges','Site Privileges')]
Tony Mack3c01ff92015-01-10 23:08:10 -05001201
1202 request=getattr(_thread_locals, "request", None)
1203 if request and request.user.is_admin:
1204 tabs.append( ('admin-only', 'Admin-Only') )
1205
1206 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001207
Tony Mackc2835a92013-05-28 09:18:49 -04001208 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1209 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001210 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001211
1212 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1213
Tony Mack5b061472014-02-04 07:57:10 -05001214 def queryset(self, request):
1215 return User.select_by_user(request.user)
1216
Tony Mack6235fc82015-01-25 21:58:30 -05001217 def get_form(self, request, obj=None, **kwargs):
Tony Mackb2c407f2015-01-28 12:37:12 -05001218 # copy login details list
1219 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack92b12052015-01-28 12:49:58 -05001220 if not request.user.is_admin:
Scott Baker6e9027f2015-01-29 10:55:53 -08001221 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mackb2c407f2015-01-28 12:37:12 -05001222 if 'is_admin' in login_details_fields:
1223 login_details_fields.remove('is_admin')
1224 if 'is_readonly' in login_details_fields:
1225 login_details_fields.remove('is_readonly')
Tony Mack92b12052015-01-28 12:49:58 -05001226 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Tony Mackb2c407f2015-01-28 12:37:12 -05001227 # only admins and pis can change a user's site
Tony Mack92b12052015-01-28 12:49:58 -05001228 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mackb2c407f2015-01-28 12:37:12 -05001229 self.fieldsets = (
1230 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1231 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1232 )
Tony Mack6235fc82015-01-25 21:58:30 -05001233 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1234
Scott Bakera6a0c772014-12-22 17:35:34 -08001235class ControllerDashboardViewInline(PlStackTabularInline):
1236 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001237 extra = 0
1238 fields = ["controller", "url"]
1239 suit_classes = 'suit-tab suit-tab-controllers'
1240
Scott Baker2c3cb642014-05-19 17:55:56 -07001241class DashboardViewAdmin(PlanetStackBaseAdmin):
1242 fieldsets = [('Dashboard View Details',
Scott Bakerb8f3cab2015-01-18 16:33:30 -08001243 {'fields': ['backend_status_text', 'name', 'url', 'enabled'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001244 'classes': ['suit-tab suit-tab-general']})
1245 ]
Scott Baker9daf19c2015-01-18 16:46:26 -08001246 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001247 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001248 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001249
Scott Bakereef5a6b2014-12-19 16:41:12 -08001250 suit_form_tabs =(('general','Dashboard View Details'),
1251 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001252
Scott Baker0165fac2014-01-13 11:49:26 -08001253class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001254 model = ServiceResource
1255 extra = 0
1256
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001257class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001258 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1259 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001260 inlines = [ServiceResourceInline]
1261
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001262 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1263 user_readonly_inlines = []
1264
Scott Baker0165fac2014-01-13 11:49:26 -08001265class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001266 model = ReservedResource
1267 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001268 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001269
1270 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1271 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1272
1273 if db_field.name == 'resource':
1274 # restrict resources to those that the slice's service class allows
1275 if request._slice is not None:
1276 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1277 if len(field.queryset) > 0:
1278 field.initial = field.queryset.all()[0]
1279 else:
1280 field.queryset = field.queryset.none()
1281 elif db_field.name == 'sliver':
1282 # restrict slivers to those that belong to the slice
1283 if request._slice is not None:
1284 field.queryset = field.queryset.filter(slice = request._slice)
1285 else:
1286 field.queryset = field.queryset.none()
1287
1288 return field
1289
Tony Mack5b061472014-02-04 07:57:10 -05001290 def queryset(self, request):
1291 return ReservedResource.select_by_user(request.user)
1292
Scott Baker133c9212013-05-17 09:09:11 -07001293class ReservationChangeForm(forms.ModelForm):
1294 class Meta:
1295 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001296 widgets = {
1297 'slice' : LinkedSelect
1298 }
Scott Baker133c9212013-05-17 09:09:11 -07001299
1300class ReservationAddForm(forms.ModelForm):
1301 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1302 refresh = forms.CharField(widget=forms.HiddenInput())
1303
1304 class Media:
1305 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1306
1307 def clean_slice(self):
1308 slice = self.cleaned_data.get("slice")
1309 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1310 if len(x) == 0:
1311 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1312 return slice
1313
1314 class Meta:
1315 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001316 widgets = {
1317 'slice' : LinkedSelect
1318 }
1319
Scott Baker133c9212013-05-17 09:09:11 -07001320
1321class ReservationAddRefreshForm(ReservationAddForm):
1322 """ This form is displayed when the Reservation Form receives an update
1323 from the Slice dropdown onChange handler. It doesn't validate the
1324 data and doesn't save the data. This will cause the form to be
1325 redrawn.
1326 """
1327
Scott Baker8737e5f2013-05-17 09:35:32 -07001328 """ don't validate anything other than slice """
1329 dont_validate_fields = ("startTime", "duration")
1330
Scott Baker133c9212013-05-17 09:09:11 -07001331 def full_clean(self):
1332 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001333
1334 for fieldname in self.dont_validate_fields:
1335 if fieldname in self._errors:
1336 del self._errors[fieldname]
1337
Scott Baker133c9212013-05-17 09:09:11 -07001338 return result
1339
1340 """ don't save anything """
1341 def is_valid(self):
1342 return False
1343
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001344class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001345 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001346 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001347 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001348 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001349 form = ReservationAddForm
1350
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001351 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1352
1353 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001354 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001355
Scott Baker133c9212013-05-17 09:09:11 -07001356 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001357 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001358 request._refresh = False
1359 request._slice = None
1360 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001361 # "refresh" will be set to "1" if the form was submitted due to
1362 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001363 if request.POST.get("refresh","1") == "1":
1364 request._refresh = True
1365 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001366
1367 # Keep track of the slice that was selected, so the
1368 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001369 request._slice = request.POST.get("slice",None)
1370 if (request._slice is not None):
1371 request._slice = Slice.objects.get(id=request._slice)
1372
1373 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1374 return result
1375
Scott Bakeracd45142013-05-19 16:19:16 -07001376 def changelist_view(self, request, extra_context = None):
1377 timezone.activate(request.user.timezone)
1378 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1379
Scott Baker133c9212013-05-17 09:09:11 -07001380 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001381 request._obj_ = obj
1382 if obj is not None:
1383 # For changes, set request._slice to the slice already set in the
1384 # object.
1385 request._slice = obj.slice
1386 self.form = ReservationChangeForm
1387 else:
1388 if getattr(request, "_refresh", False):
1389 self.form = ReservationAddRefreshForm
1390 else:
1391 self.form = ReservationAddForm
1392 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1393
Scott Baker133c9212013-05-17 09:09:11 -07001394 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001395 if (obj is not None):
1396 # Prevent slice from being changed after the reservation has been
1397 # created.
1398 return ['slice']
1399 else:
Scott Baker133c9212013-05-17 09:09:11 -07001400 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001401
Tony Mack5b061472014-02-04 07:57:10 -05001402 def queryset(self, request):
1403 return Reservation.select_by_user(request.user)
1404
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001405class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001406 list_display = ("backend_status_icon", "name", )
1407 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001408 user_readonly_fields = ['name']
1409 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001410
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001411class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001412 list_display = ("backend_status_icon", "name", )
1413 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001414 user_readonly_fields = ['name']
1415 user_readonly_inlines = []
1416
Scott Baker0165fac2014-01-13 11:49:26 -08001417class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001418 model = Router.networks.through
1419 extra = 0
1420 verbose_name_plural = "Routers"
1421 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001422 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001423
Scott Bakerb27b62c2014-08-15 16:29:16 -07001424class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001425 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001426 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001427 verbose_name_plural = "Parameters"
1428 verbose_name = "Parameter"
1429 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001430 fields = ['backend_status_icon', 'parameter', 'value']
1431 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001432
Scott Baker0165fac2014-01-13 11:49:26 -08001433class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001434 fields = ['backend_status_icon', 'network','sliver','ip']
1435 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001436 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001437 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001438 extra = 0
1439 verbose_name_plural = "Slivers"
1440 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001441 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001442
Scott Baker0165fac2014-01-13 11:49:26 -08001443class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001444 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001445 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001446 extra = 0
1447 verbose_name_plural = "Slices"
1448 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001449 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001450 fields = ['backend_status_icon', 'network','slice']
1451 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001452
Tony Macka7dbd422015-01-05 22:48:11 -05001453class ControllerNetworkInline(PlStackTabularInline):
1454 model = ControllerNetwork
Scott Bakerfbb45862014-10-17 16:27:23 -07001455 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001456 verbose_name_plural = "Controller Networks"
1457 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001458 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001459 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001460 readonly_fields = ('backend_status_icon', )
1461
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001462class NetworkForm(forms.ModelForm):
1463 class Meta:
1464 model = Network
1465 widgets = {
1466 'topologyParameters': UploadTextareaWidget,
1467 'controllerParameters': UploadTextareaWidget,
1468 }
1469
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001470class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001471 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1472 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001473 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001474
Scott Bakerd7d2a392013-08-06 08:57:30 -07001475 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Macka7dbd422015-01-05 22:48:11 -05001476 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001477
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001478 form=NetworkForm
1479
Siobhan Tully2d95e482013-09-06 10:56:06 -04001480 fieldsets = [
Scott Baker0451fb62015-01-03 12:29:29 -08001481 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet'],
Scott Baker40248712014-11-17 16:04:45 -08001482 'classes':['suit-tab suit-tab-general']}),
Scott Baker0451fb62015-01-03 12:29:29 -08001483 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker40248712014-11-17 16:04:45 -08001484 'classes':['suit-tab suit-tab-sdn']}),
1485 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001486
Scott Baker40c00762014-08-21 16:55:59 -07001487 readonly_fields = ('backend_status_text', )
Scott Baker0451fb62015-01-03 12:29:29 -08001488 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001489
Scott Bakerfbb45862014-10-17 16:27:23 -07001490 @property
1491 def suit_form_tabs(self):
1492 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001493 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001494 ('netparams', 'Parameters'),
1495 ('networkslivers','Slivers'),
1496 ('networkslices','Slices'),
1497 ('routers','Routers'),
1498 ]
1499
1500 request=getattr(_thread_locals, "request", None)
1501 if request and request.user.is_admin:
1502 tabs.append( ('admin-only', 'Admin-Only') )
1503
1504 return tabs
1505
1506
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001507class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001508 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001509 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001510 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001511 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001512 fieldsets = [
Scott Baker81fa17f2015-01-03 12:03:38 -08001513 (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
Scott Baker40248712014-11-17 16:04:45 -08001514 'classes':['suit-tab suit-tab-general']}),]
1515 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001516
Scott Baker37b47902014-09-02 14:37:41 -07001517class FlavorAdmin(PlanetStackBaseAdmin):
1518 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1519 list_display_links = ("backend_status_icon", "name")
1520 user_readonly_fields = ("name", "flavor")
1521 fields = ("name", "description", "flavor", "order", "default")
1522
Tony Mack31c2b8f2013-04-26 20:01:42 -04001523# register a signal that caches the user's credentials when they log in
1524def cache_credentials(sender, user, request, **kwds):
1525 auth = {'username': request.POST['username'],
1526 'password': request.POST['password']}
1527 request.session['auth'] = auth
1528user_logged_in.connect(cache_credentials)
1529
Scott Baker15cddfa2013-12-09 13:45:19 -08001530def dollar_field(fieldName, short_description):
1531 def newFunc(self, obj):
1532 try:
1533 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1534 except:
1535 x=getattr(obj, fieldName, 0.0)
1536 return x
1537 newFunc.short_description = short_description
1538 return newFunc
1539
1540def right_dollar_field(fieldName, short_description):
1541 def newFunc(self, obj):
1542 try:
1543 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1544 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1545 except:
1546 x=getattr(obj, fieldName, 0.0)
1547 return x
1548 newFunc.short_description = short_description
1549 newFunc.allow_tags = True
1550 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001551
Scott Baker0165fac2014-01-13 11:49:26 -08001552class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001553 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001554 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001555 verbose_name_plural = "Charges"
1556 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001557 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001558 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1559 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1560 can_delete = False
1561 max_num = 0
1562
1563 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001564
1565class InvoiceAdmin(admin.ModelAdmin):
1566 list_display = ("date", "account")
1567
1568 inlines = [InvoiceChargeInline]
1569
Scott Baker9cb88a22013-12-09 18:56:00 -08001570 fields = ["date", "account", "dollar_amount"]
1571 readonly_fields = ["date", "account", "dollar_amount"]
1572
1573 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001574
Scott Baker0165fac2014-01-13 11:49:26 -08001575class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001576 model = Invoice
1577 extra = 0
1578 verbose_name_plural = "Invoices"
1579 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001580 fields = ["date", "dollar_amount"]
1581 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001582 suit_classes = 'suit-tab suit-tab-accountinvoice'
1583 can_delete=False
1584 max_num=0
1585
1586 dollar_amount = right_dollar_field("amount", "Amount")
1587
Scott Baker0165fac2014-01-13 11:49:26 -08001588class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001589 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001590 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001591 verbose_name_plural = "Charges"
1592 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001593 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001594 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1595 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001596 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001597 can_delete=False
1598 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001599
1600 def queryset(self, request):
1601 qs = super(PendingChargeInline, self).queryset(request)
1602 qs = qs.filter(state="pending")
1603 return qs
1604
Scott Baker15cddfa2013-12-09 13:45:19 -08001605 dollar_amount = right_dollar_field("amount", "Amount")
1606
Scott Baker0165fac2014-01-13 11:49:26 -08001607class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001608 model=Payment
1609 extra = 1
1610 verbose_name_plural = "Payments"
1611 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001612 fields = ["date", "dollar_amount"]
1613 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001614 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001615 can_delete=False
1616 max_num=0
1617
1618 dollar_amount = right_dollar_field("amount", "Amount")
1619
Scott Baker43105042013-12-06 23:23:36 -08001620class AccountAdmin(admin.ModelAdmin):
1621 list_display = ("site", "balance_due")
1622
1623 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1624
1625 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001626 (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 -08001627
Scott Baker15cddfa2013-12-09 13:45:19 -08001628 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001629
1630 suit_form_tabs =(
1631 ('general','Account Details'),
1632 ('accountinvoice', 'Invoices'),
1633 ('accountpayments', 'Payments'),
1634 ('accountpendingcharges','Pending Charges'),
1635 )
1636
Scott Baker15cddfa2013-12-09 13:45:19 -08001637 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1638 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1639 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1640
Siobhan Tully53437282013-04-26 19:30:27 -04001641# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001642admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001643# ... and, since we're not using Django's builtin permissions,
1644# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001645#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001646
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001647#Do not show django evolution in the admin interface
1648from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001649#admin.site.unregister(Version)
1650#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001651
1652
1653# When debugging it is often easier to see all the classes, but for regular use
1654# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001655showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001656
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001657admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001658admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001659admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001660admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001661admin.site.register(Service, ServiceAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05001662#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001663admin.site.register(Network, NetworkAdmin)
1664admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001665admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Tony Mack598eaf22015-01-25 12:35:29 -05001666#admin.site.register(Account, AccountAdmin)
1667#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001668
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001669if True:
1670 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1671 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001672 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001673 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001674 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001675 admin.site.register(SiteRole)
1676 admin.site.register(SliceRole)
1677 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001678 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001679 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1680 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001681 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001682 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001683 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001684 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001685