blob: 4846a2816840d64040a4d7bbe947330c7807793b [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Scott Baker9f6b8ed2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Bakercbfb6002014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Scott Bakere2bbf7e2014-01-13 12:09:31 -080017from django.core.urlresolvers import reverse, NoReverseMatch
Scott Baker9f6b8ed2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
20from django.forms.utils import flatatt, to_current_timezone
Scott Baker3cde7372014-10-21 21:03:08 -070021from cgi import escape as html_escape
Tony Mack7130ac32013-03-22 21:58:00 -040022
Scott Baker36f50872014-08-21 13:01:25 -070023import django_evolution
Scott Baker0a5633b2014-10-06 17:51:20 -070024import threading
25
26# thread locals necessary to work around a django-suit issue
27_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070028
Scott Baker40c00762014-08-21 16:55:59 -070029def backend_icon(obj): # backend_status, enacted, updated):
30 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
31 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Bakerfbf06642014-09-09 10:38:15 -070032 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
Scott Baker40c00762014-08-21 16:55:59 -070033 else:
34 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
Scott Bakerfbf06642014-09-09 10:38:15 -070035 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
Scott Baker63d1a552014-08-21 15:19:07 -070036 else:
Scott Baker3cde7372014-10-21 21:03:08 -070037 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % html_escape(obj.backend_status, quote=True)
Scott Baker40c00762014-08-21 16:55:59 -070038
39def backend_text(obj):
40 icon = backend_icon(obj)
41 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Baker3cde7372014-10-21 21:03:08 -070042 return "%s %s" % (icon, "successfully enacted")
Scott Baker40c00762014-08-21 16:55:59 -070043 else:
Scott Baker3cde7372014-10-21 21:03:08 -070044 return "%s %s" % (icon, html_escape(obj.backend_status, quote=True))
Scott Baker63d1a552014-08-21 15:19:07 -070045
Scott Baker9f6b8ed2014-11-17 23:44:03 -080046class UploadTextareaWidget(AdminTextareaWidget):
47 def render(self, name, value, attrs=None):
48 if value is None:
49 value = ''
50 final_attrs = self.build_attrs(attrs, name=name)
51 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
52 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
53 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
54 flatatt(final_attrs),
55 force_text(value))
56
Scott Baker36f50872014-08-21 13:01:25 -070057class PlainTextWidget(forms.HiddenInput):
58 input_type = 'hidden'
59
60 def render(self, name, value, attrs=None):
61 if value is None:
62 value = ''
63 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
64
Scott Baker86c83ab2014-10-03 13:10:47 -070065class PermissionCheckingAdminMixin(object):
Scott Bakercbfb6002014-10-03 00:32:37 -070066 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050067
68 def has_add_permission(self, request, obj=None):
69 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070070
Siobhan Tullycf04fb62014-01-11 11:25:57 -050071 def has_delete_permission(self, request, obj=None):
72 return (not self.__user_is_readonly(request))
73
74 def save_model(self, request, obj, form, change):
75 if self.__user_is_readonly(request):
Scott Bakercbfb6002014-10-03 00:32:37 -070076 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050077 raise PermissionDenied
Scott Bakercbfb6002014-10-03 00:32:37 -070078
79 obj.caller = request.user
80 # update openstack connection to use this site/tenant
81 obj.save_by_user(request.user)
82
83 def delete_model(self, request, obj):
84 obj.delete_by_user(request.user)
85
86 def save_formset(self, request, form, formset, change):
87 instances = formset.save(commit=False)
88 for instance in instances:
89 instance.save_by_user(request.user)
90
91 # BUG in django 1.7? Objects are not deleted by formset.save if
92 # commit is False. So let's delete them ourselves.
93 #
94 # code from forms/models.py save_existing_objects()
95 try:
96 forms_to_delete = formset.deleted_forms
97 except AttributeError:
98 forms_to_delete = []
99 if formset.initial_forms:
100 for form in formset.initial_forms:
101 obj = form.instance
102 if form in forms_to_delete:
103 if obj.pk is None:
104 continue
105 formset.deleted_objects.append(obj)
106 obj.delete()
107
108 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500109
110 def get_actions(self,request):
Scott Baker86c83ab2014-10-03 13:10:47 -0700111 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500112
113 if self.__user_is_readonly(request):
114 if 'delete_selected' in actions:
115 del actions['delete_selected']
116
117 return actions
118
119 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500120 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -0700121 if not hasattr(self, "readonly_save"):
122 # save the original readonly fields
123 self.readonly_save = self.readonly_fields
124 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -0700125 if hasattr(self, "user_readonly_fields"):
126 self.readonly_fields=self.user_readonly_fields
127 if hasattr(self, "user_readonly_inlines"):
128 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -0700129 else:
130 if hasattr(self, "readonly_save"):
131 # restore the original readonly fields
132 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -0700133 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700134 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500135
136 try:
Scott Baker86c83ab2014-10-03 13:10:47 -0700137 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500138 except PermissionDenied:
139 pass
140 if request.method == 'POST':
141 raise PermissionDenied
142 request.readonly = True
Scott Baker86c83ab2014-10-03 13:10:47 -0700143 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500144
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500145 def __user_is_readonly(self, request):
146 return request.user.isReadOnlyUser()
147
Scott Baker40c00762014-08-21 16:55:59 -0700148 def backend_status_text(self, obj):
149 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700150
Scott Baker63d1a552014-08-21 15:19:07 -0700151 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700152 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700153 backend_status_icon.short_description = ""
154
Scott Baker24ded6a2014-11-05 09:05:38 -0800155 def get_form(self, request, obj=None, **kwargs):
Scott Baker5c432692014-10-16 00:57:55 -0700156 # Save obj and request in thread-local storage, so suit_form_tabs can
157 # use it to determine whether we're in edit or add mode, and can
158 # determine whether the user is an admin.
159 _thread_locals.request = request
160 _thread_locals.obj = obj
Scott Baker24ded6a2014-11-05 09:05:38 -0800161 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker5c432692014-10-16 00:57:55 -0700162
163 def get_inline_instances(self, request, obj=None):
164 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
165
166 # inlines that should only be shown to an admin user
167 if request.user.is_admin:
168 for inline_class in getattr(self, "admin_inlines", []):
169 inlines.append(inline_class(self.model, self.admin_site))
170
171 return inlines
172
Scott Baker86c83ab2014-10-03 13:10:47 -0700173class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
174 # Note: Make sure PermissionCheckingAdminMixin is listed before
175 # admin.ModelAdmin in the class declaration.
176
Scott Bakercbfb6002014-10-03 00:32:37 -0700177 pass
178
179class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
180 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700181
Scott Bakere8859f92014-05-23 12:42:40 -0700182class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400183 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700184 if not super(SingletonAdmin, self).has_add_permission(request):
185 return False
186
Siobhan Tullyce652d02013-10-08 21:52:35 -0400187 num_objects = self.model.objects.count()
188 if num_objects >= 1:
189 return False
190 else:
191 return True
192
Siobhan Tullyd3515752013-06-21 16:34:53 -0400193class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800194 def __init__(self, *args, **kwargs):
195 super(PlStackTabularInline, self).__init__(*args, **kwargs)
196
197 # InlineModelAdmin as no get_fields() method, so in order to add
198 # the selflink field, we override __init__ to modify self.fields and
199 # self.readonly_fields.
200
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800201 self.setup_selflink()
202
Scott Baker874936e2014-01-13 18:15:34 -0800203 def get_change_url(self, model, id):
204 """ Get the URL to a change form in the admin for this model """
205 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800206 try:
Scott Baker874936e2014-01-13 18:15:34 -0800207 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800208 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800209 return None
210
211 return url
212
213 def setup_selflink(self):
214 if hasattr(self, "selflink_fieldname"):
215 """ self.selflink_model can be defined to punch through a relation
216 to its target object. For example, in SliceNetworkInline, set
217 selflink_model = "network", and the URL will lead to the Network
218 object instead of trying to bring up a change view of the
219 SliceNetwork object.
220 """
221 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
222 else:
223 self.selflink_model = self.model
224
225 url = self.get_change_url(self.selflink_model, 0)
226
227 # We don't have an admin for this object, so don't create the
228 # selflink.
229 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800230 return
231
Scott Baker874936e2014-01-13 18:15:34 -0800232 # Since we need to add "selflink" to the field list, we need to create
233 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800234 if (self.fields is None):
235 self.fields = []
236 for f in self.model._meta.fields:
237 if f.editable and f.name != "id":
238 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800239
Scott Baker874936e2014-01-13 18:15:34 -0800240 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800241
Scott Baker874936e2014-01-13 18:15:34 -0800242 if self.readonly_fields is None:
243 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800244
Scott Baker874936e2014-01-13 18:15:34 -0800245 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800246
247 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800248 if hasattr(self, "selflink_fieldname"):
249 obj = getattr(obj, self.selflink_fieldname)
250
Scott Baker86568322014-01-12 16:53:31 -0800251 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800252 url = self.get_change_url(self.selflink_model, obj.id)
253 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800254 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800255 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800256
257 selflink.allow_tags = True
258 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400259
Scott Bakerb27b62c2014-08-15 16:29:16 -0700260 def has_add_permission(self, request):
261 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500262
263 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700264 readonly_fields = list(self.readonly_fields)[:]
265 if request.user.isReadOnlyUser():
266 for field in self.fields:
267 if not field in readonly_fields:
268 readonly_fields.append(field)
269 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500270
Scott Baker40c00762014-08-21 16:55:59 -0700271 def backend_status_icon(self, obj):
272 return mark_safe(backend_icon(obj))
273 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700274
Scott Bakerb27b62c2014-08-15 16:29:16 -0700275class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500276 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700277 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500278
Scott Bakerb27b62c2014-08-15 16:29:16 -0700279 def get_readonly_fields(self, request, obj=None):
280 readonly_fields = list(self.readonly_fields)[:]
281 if request.user.isReadOnlyUser():
282 for field in self.fields:
283 if not field in readonly_fields:
284 readonly_fields.append(field)
285 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500286
Scott Baker40c00762014-08-21 16:55:59 -0700287 def backend_status_icon(self, obj):
288 return mark_safe(backend_icon(obj))
289 backend_status_icon.short_description = ""
290
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400291class ReservationInline(PlStackTabularInline):
292 model = Reservation
293 extra = 0
294 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700295
Tony Mack5b061472014-02-04 07:57:10 -0500296 def queryset(self, request):
297 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400298
Scott Bakerb27b62c2014-08-15 16:29:16 -0700299class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400300 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400301 extra = 0
302 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500303 fields = ['service', 'name', 'value']
304
305 def queryset(self, request):
306 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400307
Scott Baker74d8e622013-07-29 16:04:22 -0700308class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400309 """ This is a callable that looks up a network name in a sliver and returns
310 the ip address for that network.
311 """
312
Scott Baker434ca7e2014-08-15 12:29:20 -0700313 byNetworkName = {} # class variable
314
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400315 def __init__(self, name):
316 self.short_description = name
317 self.__name__ = name
318 self.network_name = name
319
320 def __call__(self, obj):
321 if obj is not None:
322 for nbs in obj.networksliver_set.all():
323 if (nbs.network.name == self.network_name):
324 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700325 return ""
326
327 def __str__(self):
328 return self.network_name
329
Scott Baker434ca7e2014-08-15 12:29:20 -0700330 @staticmethod
331 def get(network_name):
332 """ We want to make sure we alwars return the same NetworkLookerUpper
333 because sometimes django will cause them to be instantiated multiple
334 times (and we don't want different ones in form.fields vs
335 SliverInline.readonly_fields).
336 """
337 if network_name not in NetworkLookerUpper.byNetworkName:
338 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
339 return NetworkLookerUpper.byNetworkName[network_name]
340
Siobhan Tullyd3515752013-06-21 16:34:53 -0400341class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400342 model = Sliver
Tony Mackbf6aa302014-12-26 13:38:02 -0500343 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400344 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700345 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400346 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700347
Tony Mack5b061472014-02-04 07:57:10 -0500348 def queryset(self, request):
349 return Sliver.select_by_user(request.user)
350
Scott Bakerb24cc932014-06-09 10:51:16 -0700351 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackbf6aa302014-12-26 13:38:02 -0500352 if db_field.name == 'deployment':
Scott Baker3b678742014-06-09 13:11:54 -0700353 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker7a61dc42014-09-02 17:08:20 -0700354 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Tony Mackbf6aa302014-12-26 13:38:02 -0500355 if db_field.name == 'flavor':
Scott Baker32481312014-09-08 12:14:14 -0700356 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700357
358 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700359
360 return field
361
Siobhan Tullyd3515752013-06-21 16:34:53 -0400362class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400363 model = Site
364 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400365 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400366
Tony Mack5b061472014-02-04 07:57:10 -0500367 def queryset(self, request):
368 return Site.select_by_user(request.user)
369
Siobhan Tullyd3515752013-06-21 16:34:53 -0400370class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400371 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700372 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
373 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400374 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400375 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400376
Tony Mack5b061472014-02-04 07:57:10 -0500377 def queryset(self, request):
378 return User.select_by_user(request.user)
379
Siobhan Tullyd3515752013-06-21 16:34:53 -0400380class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400381 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700382 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
383 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400384 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400385 suit_classes = 'suit-tab suit-tab-slices'
386
Tony Mack5b061472014-02-04 07:57:10 -0500387 def queryset(self, request):
388 return Slice.select_by_user(request.user)
389
Siobhan Tullyd3515752013-06-21 16:34:53 -0400390class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400391 model = Node
392 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400393 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack93d1b032014-12-08 16:43:02 -0500394 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700395 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400396
Tony Mack93d1b032014-12-08 16:43:02 -0500397class DeploymentPrivilegeInline(PlStackTabularInline):
398 model = DeploymentPrivilege
399 extra = 0
400 suit_classes = 'suit-tab suit-tab-admin-only'
401 fields = ['backend_status_icon', 'user','role','deployment']
402 readonly_fields = ('backend_status_icon', )
403
404 def queryset(self, request):
405 return DeploymentPrivilege.select_by_user(request.user)
406
Tony Mack336e0f92014-11-30 15:53:08 -0500407class ControllerPrivilegeInline(PlStackTabularInline):
408 model = ControllerPrivilege
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400409 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500410 suit_classes = 'suit-tab suit-tab-admin-only'
411 fields = ['backend_status_icon', 'user','role','controller']
Scott Baker40c00762014-08-21 16:55:59 -0700412 readonly_fields = ('backend_status_icon', )
Tony Mack5b061472014-02-04 07:57:10 -0500413
414 def queryset(self, request):
Tony Mack336e0f92014-11-30 15:53:08 -0500415 return ControllerPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400416
Tony Macka7dbd422015-01-05 22:48:11 -0500417class ControllerSiteInline(PlStackTabularInline):
418 model = ControllerSite
419 extra = 0
420 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack8f30ebe2015-01-06 15:08:20 -0500421 fields = ['controller', 'site', 'tenant_id']
Tony Macka7dbd422015-01-05 22:48:11 -0500422
423
Siobhan Tullyd3515752013-06-21 16:34:53 -0400424class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400425 model = SitePrivilege
426 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400427 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700428 fields = ['backend_status_icon', 'user','site', 'role']
429 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400430
Tony Mackc2835a92013-05-28 09:18:49 -0400431 def formfield_for_foreignkey(self, db_field, request, **kwargs):
432 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500433 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400434
435 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500436 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400437 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
438
Tony Mack5b061472014-02-04 07:57:10 -0500439 def queryset(self, request):
440 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400441
Tony Macka7dbd422015-01-05 22:48:11 -0500442class SiteDeploymentInline(PlStackTabularInline):
443 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400444 extra = 0
445 suit_classes = 'suit-tab suit-tab-deployments'
Tony Mack528d4222014-12-05 17:13:08 -0500446 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700447 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400448
449 def formfield_for_foreignkey(self, db_field, request, **kwargs):
450 if db_field.name == 'site':
451 kwargs['queryset'] = Site.select_by_user(request.user)
452
453 if db_field.name == 'deployment':
454 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mack528d4222014-12-05 17:13:08 -0500455
456 if db_field.name == 'controller':
457 kwargs['queryset'] = Controller.select_by_user(request.user)
458
Tony Macka7dbd422015-01-05 22:48:11 -0500459 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400460
461 def queryset(self, request):
Tony Macka7dbd422015-01-05 22:48:11 -0500462 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400463
464
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400465class SlicePrivilegeInline(PlStackTabularInline):
466 model = SlicePrivilege
467 suit_classes = 'suit-tab suit-tab-sliceprivileges'
468 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700469 fields = ('backend_status_icon', 'user', 'slice', 'role')
470 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400471
Tony Mackc2835a92013-05-28 09:18:49 -0400472 def formfield_for_foreignkey(self, db_field, request, **kwargs):
473 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700474 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400475 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700476 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400477
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400478 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400479
Tony Mack5b061472014-02-04 07:57:10 -0500480 def queryset(self, request):
481 return SlicePrivilege.select_by_user(request.user)
482
Scott Bakera0015eb2013-08-14 17:28:14 -0700483class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700484 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800485 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700486 extra = 0
487 verbose_name = "Network Connection"
488 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400489 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700490 fields = ['backend_status_icon', 'network']
491 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700492
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500493class ImageDeploymentsInline(PlStackTabularInline):
494 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700495 extra = 0
496 verbose_name = "Image Deployments"
497 verbose_name_plural = "Image Deployments"
498 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500499 fields = ['backend_status_icon', 'image', 'deployment']
500 readonly_fields = ['backend_status_icon']
501
502class ControllerImagesInline(PlStackTabularInline):
503 model = ControllerImages
504 extra = 0
505 verbose_name = "Controller Images"
506 verbose_name_plural = "Controller Images"
507 suit_classes = 'suit-tab suit-tab-admin-only'
508 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700509 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700510
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400511class SliceRoleAdmin(PlanetStackBaseAdmin):
512 model = SliceRole
513 pass
514
515class SiteRoleAdmin(PlanetStackBaseAdmin):
516 model = SiteRole
517 pass
518
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400519class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400520 sites = forms.ModelMultipleChoiceField(
521 queryset=Site.objects.all(),
522 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700523 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400524 widget=FilteredSelectMultiple(
525 verbose_name=('Sites'), is_stacked=False
526 )
527 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700528 images = forms.ModelMultipleChoiceField(
529 queryset=Image.objects.all(),
530 required=False,
531 help_text="Select which images should be deployed on this deployment",
532 widget=FilteredSelectMultiple(
533 verbose_name=('Images'), is_stacked=False
534 )
535 )
Scott Baker37b47902014-09-02 14:37:41 -0700536 flavors = forms.ModelMultipleChoiceField(
537 queryset=Flavor.objects.all(),
538 required=False,
539 help_text="Select which flavors should be usable on this deployment",
540 widget=FilteredSelectMultiple(
541 verbose_name=('Flavors'), is_stacked=False
542 )
543 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400544 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400545 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700546 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400547
Siobhan Tully320b4622014-01-17 15:11:14 -0500548 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700549 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500550 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
551
Scott Baker5380c522014-06-06 14:49:43 -0700552 self.fields['accessControl'].initial = "allow site " + request.user.site.name
553
Siobhan Tully320b4622014-01-17 15:11:14 -0500554 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800555 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments.all()]
556 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700557 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700558
559 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
560 """ helper function for handling m2m relations from the MultipleChoiceField
561
562 this_obj: the source object we want to link from
563
564 selected_objs: a list of destination objects we want to link to
565
566 all_relations: the full set of relations involving this_obj, including ones we don't want
567
568 relation_class: the class that implements the relation from source to dest
569
570 local_attrname: field name representing this_obj in relation_class
571
572 foreign_attrname: field name representing selected_objs in relation_class
573
574 This function will remove all newobjclass relations from this_obj
575 that are not contained in selected_objs, and add any relations that
576 are in selected_objs but don't exist in the data model yet.
577 """
578
579 existing_dest_objs = []
580 for relation in list(all_relations):
581 if getattr(relation, foreign_attrname) not in selected_objs:
582 #print "deleting site", sdp.site
583 relation.delete()
584 else:
585 existing_dest_objs.append(getattr(relation, foreign_attrname))
586
587 for dest_obj in selected_objs:
588 if dest_obj not in existing_dest_objs:
589 #print "adding site", site
590 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
591 relation = relation_class(**kwargs)
592 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500593
594 def save(self, commit=True):
595 deployment = super(DeploymentAdminForm, self).save(commit=False)
596
597 if commit:
598 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700599 # this has to be done after save() if/when a deployment is first created
600 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500601
602 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700603 # save_m2m() doesn't seem to work with 'through' relations. So we
604 # create/destroy the through models ourselves. There has to be
605 # a better way...
606
Tony Macka7dbd422015-01-05 22:48:11 -0500607 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments.all(), SiteDeployment, "deployment", "site")
Tony Mack592aa952014-12-15 11:45:02 -0500608 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
609 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
610 # so well handle that manually here
611 for flavor in deployment.flavors.all():
612 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mack11f4d202014-12-15 12:37:59 -0500613 deployment.flavors.remove(flavor)
Tony Mack592aa952014-12-15 11:45:02 -0500614 for flavor in self.cleaned_data['flavors']:
615 if flavor not in deployment.flavors.all():
616 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700617
Scott Baker37b47902014-09-02 14:37:41 -0700618 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500619
620 return deployment
621
Scott Bakerff5e0f32014-05-22 14:40:27 -0700622class DeploymentAdminROForm(DeploymentAdminForm):
623 def save(self, commit=True):
624 raise PermissionDenied
625
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500626class SiteAssocInline(PlStackTabularInline):
627 model = Site.deployments.through
628 extra = 0
629 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400630
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400631class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500632 model = Deployment
Tony Mack93d1b032014-12-08 16:43:02 -0500633 fieldList = ['backend_status_text', 'name', 'sites', 'images', 'flavors', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500634 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Tony Mack93d1b032014-12-08 16:43:02 -0500635 # node no longer directly connected to deployment
636 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
637 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700638 list_display = ['backend_status_icon', 'name']
639 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700640 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500641
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500642 user_readonly_fields = ['name']
643
Tony Mack93d1b032014-12-08 16:43:02 -0500644 # nodes no longer direclty connected to deployments
645 #suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
646 suit_form_tabs =(('sites','Deployment Details'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500647
Scott Bakerff5e0f32014-05-22 14:40:27 -0700648 def get_form(self, request, obj=None, **kwargs):
649 if request.user.isReadOnlyUser():
650 kwargs["form"] = DeploymentAdminROForm
651 else:
652 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700653 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
654
655 # from stackexchange: pass the request object into the form
656
657 class AdminFormMetaClass(adminForm):
658 def __new__(cls, *args, **kwargs):
659 kwargs['request'] = request
660 return adminForm(*args, **kwargs)
661
662 return AdminFormMetaClass
663
Tony Mack528d4222014-12-05 17:13:08 -0500664class ControllerAdminForm(forms.ModelForm):
Tony Mack26564362015-01-06 17:49:25 -0500665 sites = forms.ModelMultipleChoiceField(
666 queryset=Site.objects.all(),
Tony Mack528d4222014-12-05 17:13:08 -0500667 required=False,
Tony Mack26564362015-01-06 17:49:25 -0500668 help_text="Select which sites are managed by this controller",
Tony Mack528d4222014-12-05 17:13:08 -0500669 widget=FilteredSelectMultiple(
Tony Mack26564362015-01-06 17:49:25 -0500670 verbose_name=('Sites'), is_stacked=False
Tony Mack528d4222014-12-05 17:13:08 -0500671 )
672 )
673
674 class Meta:
675 model = Controller
676
Tony Mack93d1b032014-12-08 16:43:02 -0500677 def __init__(self, *args, **kwargs):
Tony Mack528d4222014-12-05 17:13:08 -0500678 request = kwargs.pop('request', None)
679 super(ControllerAdminForm, self).__init__(*args, **kwargs)
680
681 if self.instance and self.instance.pk:
Tony Mack26564362015-01-06 17:49:25 -0500682 self.fields['sites'].initial = [x.site_deployment for x in self.instance.controllersite.all()]
Tony Mack528d4222014-12-05 17:13:08 -0500683
Tony Mack93d1b032014-12-08 16:43:02 -0500684 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
Tony Mack528d4222014-12-05 17:13:08 -0500685 """ helper function for handling m2m relations from the MultipleChoiceField
686 this_obj: the source object we want to link from
687 selected_objs: a list of destination objects we want to link to
688 all_relations: the full set of relations involving this_obj, including ones we don't want
689 relation_class: the class that implements the relation from source to dest
690 local_attrname: field name representing this_obj in relation_class
691 foreign_attrname: field name representing selected_objs in relation_class
692 This function will remove all newobjclass relations from this_obj
693 that are not contained in selected_objs, and add any relations that
694 are in selected_objs but don't exist in the data model yet.
695 """
696 existing_dest_objs = []
697 for relation in list(all_relations):
698 if getattr(relation, foreign_attrname) not in selected_objs:
699 #print "deleting site", sdp.site
700 relation.delete()
701 else:
702 existing_dest_objs.append(getattr(relation, foreign_attrname))
703
704 for dest_obj in selected_objs:
705 if dest_obj not in existing_dest_objs:
706 #print "adding site", site
707 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
708 relation = relation_class(**kwargs)
709 relation.save()
710
Tony Mack93d1b032014-12-08 16:43:02 -0500711 def save(self, commit=True):
712 controller = super(ControllerAdminForm, self).save(commit=False)
713 if commit:
714 controller.save()
Tony Mack528d4222014-12-05 17:13:08 -0500715
Tony Mack93d1b032014-12-08 16:43:02 -0500716 if controller.pk:
717 # save_m2m() doesn't seem to work with 'through' relations. So we
718 # create/destroy the through models ourselves. There has to be
719 # a better way...
Tony Mack26564362015-01-06 17:49:25 -0500720 self.manipulate_m2m_objs(controller, self.cleaned_data['sites'], controller.controllersite.all(), ControllerSite, "controller", "site")
Tony Mack1e828282015-01-03 17:40:42 -0500721 pass
722
Tony Mack93d1b032014-12-08 16:43:02 -0500723 self.save_m2m()
Tony Mack528d4222014-12-05 17:13:08 -0500724
Tony Mack93d1b032014-12-08 16:43:02 -0500725 return controller
Tony Mack528d4222014-12-05 17:13:08 -0500726
727class ControllerAdmin(PlanetStackBaseAdmin):
728 model = Controller
Tony Mack93d1b032014-12-08 16:43:02 -0500729 fieldList = ['name', 'version', 'backend_type', 'auth_url', 'admin_user', 'admin_tenant','admin_password']
730 #fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Macka7dbd422015-01-05 22:48:11 -0500731 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500732 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
733 list_display_links = ('backend_status_icon', 'name', )
734 readonly_fields = ('backend_status_text',)
735
736 user_readonly_fields = []
737
738 def get_form(self, request, obj=None, **kwargs):
Tony Mack93d1b032014-12-08 16:43:02 -0500739 print self.fieldsets
Tony Mack528d4222014-12-05 17:13:08 -0500740 if request.user.isReadOnlyUser():
741 kwargs["form"] = ControllerAdminROForm
742 else:
743 kwargs["form"] = ControllerAdminForm
Tony Mack93d1b032014-12-08 16:43:02 -0500744 adminForm = super(ControllerAdmin,self).get_form(request, obj, **kwargs)
Tony Mack528d4222014-12-05 17:13:08 -0500745
746 # from stackexchange: pass the request object into the form
747
748 class AdminFormMetaClass(adminForm):
749 def __new__(cls, *args, **kwargs):
750 kwargs['request'] = request
751 return adminForm(*args, **kwargs)
752
753 return AdminFormMetaClass
754
Siobhan Tullyce652d02013-10-08 21:52:35 -0400755class ServiceAttrAsTabInline(PlStackTabularInline):
756 model = ServiceAttribute
757 fields = ['name','value']
758 extra = 0
759 suit_classes = 'suit-tab suit-tab-serviceattrs'
760
Siobhan Tullyce652d02013-10-08 21:52:35 -0400761class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700762 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
763 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700764 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500765 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
766 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700767 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500768
769 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500770
771 suit_form_tabs =(('general', 'Service Details'),
772 ('slices','Slices'),
773 ('serviceattrs','Additional Attributes'),
774 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400775
Tony Mack0553f282013-06-10 22:54:50 -0400776class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700777 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400778 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500779 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400780 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400781 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400782 suit_form_tabs =(('general', 'Site Details'),
783 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400784 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400785 ('deployments','Deployments'),
786 ('slices','Slices'),
Tony Mack93d1b032014-12-08 16:43:02 -0500787 #('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400788 ('tags','Tags'),
789 )
Scott Baker40c00762014-08-21 16:55:59 -0700790 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500791
792 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500793
Scott Baker63d1a552014-08-21 15:19:07 -0700794 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
795 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400796 filter_horizontal = ('deployments',)
Tony Macka7dbd422015-01-05 22:48:11 -0500797 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteDeploymentInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400798 search_fields = ['name']
799
Tony Mack04062832013-05-10 08:22:44 -0400800 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500801 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400802
Tony Mack5cd13202013-05-01 21:48:38 -0400803 def get_formsets(self, request, obj=None):
804 for inline in self.get_inline_instances(request, obj):
805 # hide MyInline in the add view
806 if obj is None:
807 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400808 if isinstance(inline, SliverInline):
809 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400810 yield inline.get_formset(request, obj)
811
Scott Baker545db2a2013-12-09 18:44:43 -0800812 def accountLink(self, obj):
813 link_obj = obj.accounts.all()
814 if link_obj:
815 reverse_path = "admin:core_account_change"
816 url = reverse(reverse_path, args =(link_obj[0].id,))
817 return "<a href='%s'>%s</a>" % (url, "view billing details")
818 else:
819 return "no billing data for this site"
820 accountLink.allow_tags = True
821 accountLink.short_description = "Billing"
822
Tony Mack332ee1d2014-02-04 15:33:45 -0500823 def save_model(self, request, obj, form, change):
824 # update openstack connection to use this site/tenant
825 obj.save_by_user(request.user)
826
827 def delete_model(self, request, obj):
828 obj.delete_by_user(request.user)
829
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500830
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400831class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700832 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400833 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500834 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400835 ]
Scott Baker40c00762014-08-21 16:55:59 -0700836 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700837 list_display = ('backend_status_icon', 'user', 'site', 'role')
838 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500839 user_readonly_fields = fieldList
840 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400841
Tony Mackc2835a92013-05-28 09:18:49 -0400842 def formfield_for_foreignkey(self, db_field, request, **kwargs):
843 if db_field.name == 'site':
844 if not request.user.is_admin:
845 # only show sites where user is an admin or pi
846 sites = set()
847 for site_privilege in SitePrivilege.objects.filer(user=request.user):
848 if site_privilege.role.role_type in ['admin', 'pi']:
849 sites.add(site_privilege.site)
850 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
851
852 if db_field.name == 'user':
853 if not request.user.is_admin:
854 # only show users from sites where caller has admin or pi role
855 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
856 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
857 sites = [site_privilege.site for site_privilege in site_privileges]
858 site_privileges = SitePrivilege.objects.filter(site__in=sites)
859 emails = [site_privilege.user.email for site_privilege in site_privileges]
860 users = User.objects.filter(email__in=emails)
861 kwargs['queryset'] = users
862
863 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
864
Tony Mack04062832013-05-10 08:22:44 -0400865 def queryset(self, request):
866 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400867 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400868 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500869 #if not request.user.is_admin:
870 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
871 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
872 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
873 # sites = Site.objects.filter(login_base__in=login_bases)
874 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400875 return qs
876
Siobhan Tullyce652d02013-10-08 21:52:35 -0400877class SliceForm(forms.ModelForm):
878 class Meta:
879 model = Slice
880 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700881 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400882 }
883
Tony Mack2cbd3802014-09-29 16:10:52 -0400884 def clean(self):
885 cleaned_data = super(SliceForm, self).clean()
886 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -0700887 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -0400888 slice_id = self.instance.id
889 if not site and slice_id:
890 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -0700891 if (not isinstance(site,Site)):
892 # previous code indicates 'site' could be a site_id and not a site?
893 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -0400894 if not name.startswith(site.login_base):
895 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
896 return cleaned_data
897
Tony Macka7dbd422015-01-05 22:48:11 -0500898class ControllerSliceInline(PlStackTabularInline):
899 model = ControllerSlice
Scott Bakerc4efdc72014-10-15 16:54:04 -0700900 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500901 verbose_name = "Controller Slices"
902 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -0700903 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -0500904 fields = ['backend_status_icon', 'controller', 'tenant_id']
Scott Bakerc4efdc72014-10-15 16:54:04 -0700905 readonly_fields = ('backend_status_icon', )
906
Tony Mack2bd5b412013-06-11 21:05:06 -0400907class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400908 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400909 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500910 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700911 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400912 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
913 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400914 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Macka7dbd422015-01-05 22:48:11 -0500915 admin_inlines = [ControllerSliceInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400916
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500917 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400918
Scott Bakerc4efdc72014-10-15 16:54:04 -0700919 @property
920 def suit_form_tabs(self):
921 tabs =[('general', 'Slice Details'),
922 ('slicenetworks','Networks'),
923 ('sliceprivileges','Privileges'),
924 ('slivers','Slivers'),
925 ('tags','Tags'),
926 ('reservations','Reservations'),
927 ]
928
929 request=getattr(_thread_locals, "request", None)
930 if request and request.user.is_admin:
931 tabs.append( ('admin-only', 'Admin-Only') )
932
933 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -0400934
935 def add_view(self, request, form_url='', extra_context=None):
936 # revert to default read-only fields
937 self.readonly_fields = ('backend_status_text',)
938 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
939
940 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -0400941 # cannot change the site of an existing slice so make the site field read only
942 if object_id:
943 self.readonly_fields = ('backend_status_text','site')
944 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400945
Scott Baker510fdbb2014-08-05 17:19:24 -0700946 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700947 deployment_nodes = []
948 for node in Node.objects.all():
Sapan Bhatia48208cd2014-12-22 01:37:26 -0500949 deployment_nodes.append( (node.site_deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -0700950
Scott Baker7a61dc42014-09-02 17:08:20 -0700951 deployment_flavors = []
952 for flavor in Flavor.objects.all():
953 for deployment in flavor.deployments.all():
954 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
955
Tony Mack93d1b032014-12-08 16:43:02 -0500956 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -0700957 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -0500958 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -0800959 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -0700960
Tony Mackec23b992014-09-02 21:18:45 -0400961 site_login_bases = []
962 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -0700963 site_login_bases.append((site.id, site.login_base))
964
Scott Baker510fdbb2014-08-05 17:19:24 -0700965 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700966 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -0700967 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400968 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700969 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
970
Tony Mackc2835a92013-05-28 09:18:49 -0400971 def formfield_for_foreignkey(self, db_field, request, **kwargs):
972 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500973 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400974 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 -0700975
Tony Mackc2835a92013-05-28 09:18:49 -0400976 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
977
Tony Mack04062832013-05-10 08:22:44 -0400978 def queryset(self, request):
979 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500980 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400981
Tony Mack79748612013-05-01 14:52:03 -0400982 def get_formsets(self, request, obj=None):
983 for inline in self.get_inline_instances(request, obj):
984 # hide MyInline in the add view
985 if obj is None:
986 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400987 if isinstance(inline, SliverInline):
988 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400989 yield inline.get_formset(request, obj)
990
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400991class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400992 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700993 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400994 ]
Scott Baker40c00762014-08-21 16:55:59 -0700995 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700996 list_display = ('backend_status_icon', 'user', 'slice', 'role')
997 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400998
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500999 user_readonly_fields = ['user', 'slice', 'role']
1000 user_readonly_inlines = []
1001
Tony Mackc2835a92013-05-28 09:18:49 -04001002 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1003 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001004 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001005
1006 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001007 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001008
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001009 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001010
Tony Mack04062832013-05-10 08:22:44 -04001011 def queryset(self, request):
1012 # admins can see all memberships. Users can only see memberships of
1013 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001014 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001015
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001016 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001017 # update openstack connection to use this site/tenant
1018 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001019 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001020 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001021 obj.save()
1022
1023 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001024 # update openstack connection to use this site/tenant
1025 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001026 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001027 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001028 obj.delete()
1029
Siobhan Tully567e3e62013-06-21 18:03:16 -04001030
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001031class ImageAdmin(PlanetStackBaseAdmin):
1032
Scott Baker36f50872014-08-21 13:01:25 -07001033 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001034 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001035 'classes': ['suit-tab suit-tab-general']})
1036 ]
Scott Baker40c00762014-08-21 16:55:59 -07001037 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001038
Tony Mack336e0f92014-11-30 15:53:08 -05001039 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001040
Tony Mack336e0f92014-11-30 15:53:08 -05001041 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001042
Tony Mack32e1ce32014-05-07 13:29:41 -04001043 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001044
Scott Baker63d1a552014-08-21 15:19:07 -07001045 list_display = ['backend_status_icon', 'name']
1046 list_display_links = ('backend_status_icon', 'name', )
1047
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001048class NodeForm(forms.ModelForm):
1049 class Meta:
1050 widgets = {
1051 'site': LinkedSelect,
1052 'deployment': LinkedSelect
1053 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001054
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001055class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001056 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -05001057 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001058 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -05001059 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001060
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001061 inlines = [TagInline,SliverInline]
Tony Mack93d1b032014-12-08 16:43:02 -05001062 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001063 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001064
Tony Mack93d1b032014-12-08 16:43:02 -05001065 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001066 user_readonly_inlines = [TagInline,SliverInline]
1067
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001068 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001069
Siobhan Tully567e3e62013-06-21 18:03:16 -04001070
Tony Mackd90cdbf2013-04-16 22:48:40 -04001071class SliverForm(forms.ModelForm):
1072 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001073 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001074 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001075 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001076 widgets = {
1077 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001078 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001079 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001080 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001081 'node': LinkedSelect,
1082 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001083 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001084
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001085class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001086 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1087 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001088 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1089 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001090
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001091class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001092 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001093 fieldsets = [
Tony Mackbf6aa302014-12-26 13:38:02 -05001094 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001095 ]
Scott Baker40c00762014-08-21 16:55:59 -07001096 readonly_fields = ('backend_status_text', )
Tony Mackbf6aa302014-12-26 13:38:02 -05001097 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Baker63d1a552014-08-21 15:19:07 -07001098 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001099
1100 suit_form_tabs =(('general', 'Sliver Details'),
1101 ('tags','Tags'),
1102 )
1103
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001104 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001105
Tony Mackbf6aa302014-12-26 13:38:02 -05001106 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001107
Tony Mackc2835a92013-05-28 09:18:49 -04001108 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1109 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001110 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001111
1112 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1113
Tony Mack04062832013-05-10 08:22:44 -04001114 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001115 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001116 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001117 return Sliver.select_by_user(request.user)
1118
Tony Mack04062832013-05-10 08:22:44 -04001119
Tony Mack1d6b85f2013-05-07 18:49:14 -04001120 def get_formsets(self, request, obj=None):
1121 # make some fields read only if we are updating an existing record
1122 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -07001123 #self.readonly_fields = ('ip', 'instance_name')
Scott Bakerfbb45862014-10-17 16:27:23 -07001124 self.readonly_fields = ('backend_status_text',)
Tony Mack1d6b85f2013-05-07 18:49:14 -04001125 else:
Scott Bakerfbb45862014-10-17 16:27:23 -07001126 self.readonly_fields = ('backend_status_text',)
Scott Baker36f50872014-08-21 13:01:25 -07001127 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001128
1129 for inline in self.get_inline_instances(request, obj):
1130 # hide MyInline in the add view
1131 if obj is None:
1132 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001133 if isinstance(inline, SliverInline):
1134 inline.model.caller = request.user
1135 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001136
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001137 #def save_model(self, request, obj, form, change):
1138 # # update openstack connection to use this site/tenant
1139 # auth = request.session.get('auth', {})
1140 # auth['tenant'] = obj.slice.name
1141 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1142 # obj.creator = request.user
1143 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001144
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001145 #def delete_model(self, request, obj):
1146 # # update openstack connection to use this site/tenant
1147 # auth = request.session.get('auth', {})
1148 # auth['tenant'] = obj.slice.name
1149 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1150 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001151
Siobhan Tully53437282013-04-26 19:30:27 -04001152class UserCreationForm(forms.ModelForm):
1153 """A form for creating new users. Includes all the required
1154 fields, plus a repeated password."""
1155 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1156 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1157
1158 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001159 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001160 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001161
1162 def clean_password2(self):
1163 # Check that the two password entries match
1164 password1 = self.cleaned_data.get("password1")
1165 password2 = self.cleaned_data.get("password2")
1166 if password1 and password2 and password1 != password2:
1167 raise forms.ValidationError("Passwords don't match")
1168 return password2
1169
1170 def save(self, commit=True):
1171 # Save the provided password in hashed format
1172 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001173 user.password = self.cleaned_data["password1"]
1174 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001175 if commit:
1176 user.save()
1177 return user
1178
Siobhan Tully567e3e62013-06-21 18:03:16 -04001179
Siobhan Tully53437282013-04-26 19:30:27 -04001180class UserChangeForm(forms.ModelForm):
1181 """A form for updating users. Includes all the fields on
1182 the user, but replaces the password field with admin's
1183 password hash display field.
1184 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001185 password = ReadOnlyPasswordHashField(label='Password',
1186 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001187
1188 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001189 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001190 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001191
1192 def clean_password(self):
1193 # Regardless of what the user provides, return the initial value.
1194 # This is done here, rather than on the field, because the
1195 # field does not have access to the initial value
1196 return self.initial["password"]
1197
Scott Baker2c3cb642014-05-19 17:55:56 -07001198class UserDashboardViewInline(PlStackTabularInline):
1199 model = UserDashboardView
1200 extra = 0
1201 suit_classes = 'suit-tab suit-tab-dashboards'
1202 fields = ['user', 'dashboardView', 'order']
1203
Scott Baker86c83ab2014-10-03 13:10:47 -07001204class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1205 # Note: Make sure PermissionCheckingAdminMixin is listed before
1206 # admin.ModelAdmin in the class declaration.
1207
Siobhan Tully53437282013-04-26 19:30:27 -04001208 class Meta:
1209 app_label = "core"
1210
1211 # The forms to add and change user instances
1212 form = UserChangeForm
1213 add_form = UserCreationForm
1214
1215 # The fields to be used in displaying the User model.
1216 # These override the definitions on the base UserAdmin
1217 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001218 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001219 list_filter = ('site',)
Tony Mack336e0f92014-11-30 15:53:08 -05001220 inlines = [SlicePrivilegeInline,SitePrivilegeInline,ControllerPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001221
Scott Bakercbfb6002014-10-03 00:32:37 -07001222 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001223 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1224
Siobhan Tully53437282013-04-26 19:30:27 -04001225 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001226 ('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 -04001227 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001228 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001229 #('Important dates', {'fields': ('last_login',)}),
1230 )
1231 add_fieldsets = (
1232 (None, {
1233 'classes': ('wide',),
Scott Baker0a5633b2014-10-06 17:51:20 -07001234 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001235 ),
1236 )
Scott Baker40c00762014-08-21 16:55:59 -07001237 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001238 search_fields = ('email',)
1239 ordering = ('email',)
1240 filter_horizontal = ()
1241
Scott Baker3ca51f62014-05-23 12:05:11 -07001242 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001243
Scott Baker0a5633b2014-10-06 17:51:20 -07001244 @property
1245 def suit_form_tabs(self):
1246 if getattr(_thread_locals, "obj", None) is None:
1247 return []
1248 else:
1249 return (('general','Login Details'),
1250 ('contact','Contact Information'),
1251 ('sliceprivileges','Slice Privileges'),
1252 ('siteprivileges','Site Privileges'),
Tony Mack336e0f92014-11-30 15:53:08 -05001253 ('controllerprivileges','Controller Privileges'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001254 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001255
Tony Mackc2835a92013-05-28 09:18:49 -04001256 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1257 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001258 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001259
1260 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1261
Tony Mack5b061472014-02-04 07:57:10 -05001262 def queryset(self, request):
1263 return User.select_by_user(request.user)
1264
Scott Bakera6a0c772014-12-22 17:35:34 -08001265class ControllerDashboardViewInline(PlStackTabularInline):
1266 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001267 extra = 0
1268 fields = ["controller", "url"]
1269 suit_classes = 'suit-tab suit-tab-controllers'
1270
Scott Baker2c3cb642014-05-19 17:55:56 -07001271class DashboardViewAdmin(PlanetStackBaseAdmin):
1272 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001273 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001274 'classes': ['suit-tab suit-tab-general']})
1275 ]
Scott Baker40c00762014-08-21 16:55:59 -07001276 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001277 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001278
Scott Bakereef5a6b2014-12-19 16:41:12 -08001279 suit_form_tabs =(('general','Dashboard View Details'),
1280 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001281
Scott Baker0165fac2014-01-13 11:49:26 -08001282class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001283 model = ServiceResource
1284 extra = 0
1285
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001286class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001287 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1288 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001289 inlines = [ServiceResourceInline]
1290
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001291 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1292 user_readonly_inlines = []
1293
Scott Baker0165fac2014-01-13 11:49:26 -08001294class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001295 model = ReservedResource
1296 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001297 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001298
1299 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1300 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1301
1302 if db_field.name == 'resource':
1303 # restrict resources to those that the slice's service class allows
1304 if request._slice is not None:
1305 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1306 if len(field.queryset) > 0:
1307 field.initial = field.queryset.all()[0]
1308 else:
1309 field.queryset = field.queryset.none()
1310 elif db_field.name == 'sliver':
1311 # restrict slivers to those that belong to the slice
1312 if request._slice is not None:
1313 field.queryset = field.queryset.filter(slice = request._slice)
1314 else:
1315 field.queryset = field.queryset.none()
1316
1317 return field
1318
Tony Mack5b061472014-02-04 07:57:10 -05001319 def queryset(self, request):
1320 return ReservedResource.select_by_user(request.user)
1321
Scott Baker133c9212013-05-17 09:09:11 -07001322class ReservationChangeForm(forms.ModelForm):
1323 class Meta:
1324 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001325 widgets = {
1326 'slice' : LinkedSelect
1327 }
Scott Baker133c9212013-05-17 09:09:11 -07001328
1329class ReservationAddForm(forms.ModelForm):
1330 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1331 refresh = forms.CharField(widget=forms.HiddenInput())
1332
1333 class Media:
1334 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1335
1336 def clean_slice(self):
1337 slice = self.cleaned_data.get("slice")
1338 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1339 if len(x) == 0:
1340 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1341 return slice
1342
1343 class Meta:
1344 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001345 widgets = {
1346 'slice' : LinkedSelect
1347 }
1348
Scott Baker133c9212013-05-17 09:09:11 -07001349
1350class ReservationAddRefreshForm(ReservationAddForm):
1351 """ This form is displayed when the Reservation Form receives an update
1352 from the Slice dropdown onChange handler. It doesn't validate the
1353 data and doesn't save the data. This will cause the form to be
1354 redrawn.
1355 """
1356
Scott Baker8737e5f2013-05-17 09:35:32 -07001357 """ don't validate anything other than slice """
1358 dont_validate_fields = ("startTime", "duration")
1359
Scott Baker133c9212013-05-17 09:09:11 -07001360 def full_clean(self):
1361 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001362
1363 for fieldname in self.dont_validate_fields:
1364 if fieldname in self._errors:
1365 del self._errors[fieldname]
1366
Scott Baker133c9212013-05-17 09:09:11 -07001367 return result
1368
1369 """ don't save anything """
1370 def is_valid(self):
1371 return False
1372
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001373class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001374 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001375 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001376 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001377 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001378 form = ReservationAddForm
1379
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001380 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1381
1382 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001383 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001384
Scott Baker133c9212013-05-17 09:09:11 -07001385 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001386 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001387 request._refresh = False
1388 request._slice = None
1389 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001390 # "refresh" will be set to "1" if the form was submitted due to
1391 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001392 if request.POST.get("refresh","1") == "1":
1393 request._refresh = True
1394 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001395
1396 # Keep track of the slice that was selected, so the
1397 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001398 request._slice = request.POST.get("slice",None)
1399 if (request._slice is not None):
1400 request._slice = Slice.objects.get(id=request._slice)
1401
1402 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1403 return result
1404
Scott Bakeracd45142013-05-19 16:19:16 -07001405 def changelist_view(self, request, extra_context = None):
1406 timezone.activate(request.user.timezone)
1407 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1408
Scott Baker133c9212013-05-17 09:09:11 -07001409 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001410 request._obj_ = obj
1411 if obj is not None:
1412 # For changes, set request._slice to the slice already set in the
1413 # object.
1414 request._slice = obj.slice
1415 self.form = ReservationChangeForm
1416 else:
1417 if getattr(request, "_refresh", False):
1418 self.form = ReservationAddRefreshForm
1419 else:
1420 self.form = ReservationAddForm
1421 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1422
Scott Baker133c9212013-05-17 09:09:11 -07001423 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001424 if (obj is not None):
1425 # Prevent slice from being changed after the reservation has been
1426 # created.
1427 return ['slice']
1428 else:
Scott Baker133c9212013-05-17 09:09:11 -07001429 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001430
Tony Mack5b061472014-02-04 07:57:10 -05001431 def queryset(self, request):
1432 return Reservation.select_by_user(request.user)
1433
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001434class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001435 list_display = ("backend_status_icon", "name", )
1436 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001437 user_readonly_fields = ['name']
1438 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001439
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001440class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001441 list_display = ("backend_status_icon", "name", )
1442 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001443 user_readonly_fields = ['name']
1444 user_readonly_inlines = []
1445
Scott Baker0165fac2014-01-13 11:49:26 -08001446class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001447 model = Router.networks.through
1448 extra = 0
1449 verbose_name_plural = "Routers"
1450 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001451 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001452
Scott Bakerb27b62c2014-08-15 16:29:16 -07001453class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001454 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001455 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001456 verbose_name_plural = "Parameters"
1457 verbose_name = "Parameter"
1458 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001459 fields = ['backend_status_icon', 'parameter', 'value']
1460 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001461
Scott Baker0165fac2014-01-13 11:49:26 -08001462class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001463 fields = ['backend_status_icon', 'network','sliver','ip']
1464 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001465 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001466 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001467 extra = 0
1468 verbose_name_plural = "Slivers"
1469 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001470 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001471
Scott Baker0165fac2014-01-13 11:49:26 -08001472class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001473 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001474 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001475 extra = 0
1476 verbose_name_plural = "Slices"
1477 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001478 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001479 fields = ['backend_status_icon', 'network','slice']
1480 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001481
Tony Macka7dbd422015-01-05 22:48:11 -05001482class ControllerNetworkInline(PlStackTabularInline):
1483 model = ControllerNetwork
Scott Bakerfbb45862014-10-17 16:27:23 -07001484 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001485 verbose_name_plural = "Controller Networks"
1486 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001487 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001488 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001489 readonly_fields = ('backend_status_icon', )
1490
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001491class NetworkForm(forms.ModelForm):
1492 class Meta:
1493 model = Network
1494 widgets = {
1495 'topologyParameters': UploadTextareaWidget,
1496 'controllerParameters': UploadTextareaWidget,
1497 }
1498
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001499class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001500 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1501 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001502 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001503
Scott Bakerd7d2a392013-08-06 08:57:30 -07001504 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Macka7dbd422015-01-05 22:48:11 -05001505 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001506
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001507 form=NetworkForm
1508
Siobhan Tully2d95e482013-09-06 10:56:06 -04001509 fieldsets = [
Scott Baker0451fb62015-01-03 12:29:29 -08001510 (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 -08001511 'classes':['suit-tab suit-tab-general']}),
Scott Baker0451fb62015-01-03 12:29:29 -08001512 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker40248712014-11-17 16:04:45 -08001513 'classes':['suit-tab suit-tab-sdn']}),
1514 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001515
Scott Baker40c00762014-08-21 16:55:59 -07001516 readonly_fields = ('backend_status_text', )
Scott Baker0451fb62015-01-03 12:29:29 -08001517 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 -04001518
Scott Bakerfbb45862014-10-17 16:27:23 -07001519 @property
1520 def suit_form_tabs(self):
1521 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001522 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001523 ('netparams', 'Parameters'),
1524 ('networkslivers','Slivers'),
1525 ('networkslices','Slices'),
1526 ('routers','Routers'),
1527 ]
1528
1529 request=getattr(_thread_locals, "request", None)
1530 if request and request.user.is_admin:
1531 tabs.append( ('admin-only', 'Admin-Only') )
1532
1533 return tabs
1534
1535
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001536class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001537 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001538 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001539 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001540 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001541 fieldsets = [
Scott Baker81fa17f2015-01-03 12:03:38 -08001542 (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 -08001543 'classes':['suit-tab suit-tab-general']}),]
1544 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001545
Scott Baker37b47902014-09-02 14:37:41 -07001546class FlavorAdmin(PlanetStackBaseAdmin):
1547 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1548 list_display_links = ("backend_status_icon", "name")
1549 user_readonly_fields = ("name", "flavor")
1550 fields = ("name", "description", "flavor", "order", "default")
1551
Tony Mack31c2b8f2013-04-26 20:01:42 -04001552# register a signal that caches the user's credentials when they log in
1553def cache_credentials(sender, user, request, **kwds):
1554 auth = {'username': request.POST['username'],
1555 'password': request.POST['password']}
1556 request.session['auth'] = auth
1557user_logged_in.connect(cache_credentials)
1558
Scott Baker15cddfa2013-12-09 13:45:19 -08001559def dollar_field(fieldName, short_description):
1560 def newFunc(self, obj):
1561 try:
1562 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1563 except:
1564 x=getattr(obj, fieldName, 0.0)
1565 return x
1566 newFunc.short_description = short_description
1567 return newFunc
1568
1569def right_dollar_field(fieldName, short_description):
1570 def newFunc(self, obj):
1571 try:
1572 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1573 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1574 except:
1575 x=getattr(obj, fieldName, 0.0)
1576 return x
1577 newFunc.short_description = short_description
1578 newFunc.allow_tags = True
1579 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001580
Scott Baker0165fac2014-01-13 11:49:26 -08001581class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001582 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001583 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001584 verbose_name_plural = "Charges"
1585 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001586 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001587 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1588 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1589 can_delete = False
1590 max_num = 0
1591
1592 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001593
1594class InvoiceAdmin(admin.ModelAdmin):
1595 list_display = ("date", "account")
1596
1597 inlines = [InvoiceChargeInline]
1598
Scott Baker9cb88a22013-12-09 18:56:00 -08001599 fields = ["date", "account", "dollar_amount"]
1600 readonly_fields = ["date", "account", "dollar_amount"]
1601
1602 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001603
Scott Baker0165fac2014-01-13 11:49:26 -08001604class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001605 model = Invoice
1606 extra = 0
1607 verbose_name_plural = "Invoices"
1608 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001609 fields = ["date", "dollar_amount"]
1610 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001611 suit_classes = 'suit-tab suit-tab-accountinvoice'
1612 can_delete=False
1613 max_num=0
1614
1615 dollar_amount = right_dollar_field("amount", "Amount")
1616
Scott Baker0165fac2014-01-13 11:49:26 -08001617class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001618 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001619 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001620 verbose_name_plural = "Charges"
1621 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001622 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001623 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1624 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001625 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001626 can_delete=False
1627 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001628
1629 def queryset(self, request):
1630 qs = super(PendingChargeInline, self).queryset(request)
1631 qs = qs.filter(state="pending")
1632 return qs
1633
Scott Baker15cddfa2013-12-09 13:45:19 -08001634 dollar_amount = right_dollar_field("amount", "Amount")
1635
Scott Baker0165fac2014-01-13 11:49:26 -08001636class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001637 model=Payment
1638 extra = 1
1639 verbose_name_plural = "Payments"
1640 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001641 fields = ["date", "dollar_amount"]
1642 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001643 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001644 can_delete=False
1645 max_num=0
1646
1647 dollar_amount = right_dollar_field("amount", "Amount")
1648
Scott Baker43105042013-12-06 23:23:36 -08001649class AccountAdmin(admin.ModelAdmin):
1650 list_display = ("site", "balance_due")
1651
1652 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1653
1654 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001655 (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 -08001656
Scott Baker15cddfa2013-12-09 13:45:19 -08001657 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001658
1659 suit_form_tabs =(
1660 ('general','Account Details'),
1661 ('accountinvoice', 'Invoices'),
1662 ('accountpayments', 'Payments'),
1663 ('accountpendingcharges','Pending Charges'),
1664 )
1665
Scott Baker15cddfa2013-12-09 13:45:19 -08001666 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1667 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1668 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1669
Siobhan Tully53437282013-04-26 19:30:27 -04001670# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001671admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001672# ... and, since we're not using Django's builtin permissions,
1673# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001674#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001675
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001676#Do not show django evolution in the admin interface
1677from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001678#admin.site.unregister(Version)
1679#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001680
1681
1682# When debugging it is often easier to see all the classes, but for regular use
1683# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001684showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001685
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001686admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001687admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001688admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001689admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001690admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001691admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001692admin.site.register(Network, NetworkAdmin)
1693admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001694admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001695admin.site.register(Account, AccountAdmin)
1696admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001697
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001698if True:
1699 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1700 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001701 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001702 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001703 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001704 admin.site.register(SiteRole)
1705 admin.site.register(SliceRole)
1706 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001707 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001708 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1709 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001710 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001711 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001712 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001713 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001714