blob: 6babec2e8ea6a165acc428532b9d4b2a0429ece9 [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 Baker69e045d2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Baker1a6a3902014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Scott Bakere2bbf7e2014-01-13 12:09:31 -080017from django.core.urlresolvers import reverse, NoReverseMatch
Scott Baker69e045d2014-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 Baker92d22172014-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 Baker6a995352014-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))
Sapan Bhatia4c877d62015-01-27 03:49:42 +000031 if (obj.enacted is not None) and obj.enacted >= obj.updated or obj.backend_status.startswith("1 -"):
Scott Bakerb171e522014-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:
Scott Baker95638be2015-01-22 15:14:50 -080034 if ((obj.backend_status is not None) and obj.backend_status.startswith("0 -")) or obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
Scott Bakerb171e522014-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 Baker92d22172014-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 Baker92d22172014-10-21 21:03:08 -070042 return "%s %s" % (icon, "successfully enacted")
Scott Baker40c00762014-08-21 16:55:59 -070043 else:
Scott Baker92d22172014-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 Baker69e045d2014-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 Bakerf4aeedc2014-10-03 13:10:47 -070065class PermissionCheckingAdminMixin(object):
Scott Baker1a6a3902014-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 Baker1a6a3902014-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 Baker1a6a3902014-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 Bakerf4aeedc2014-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 Bakerf4aeedc2014-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 Bakerf4aeedc2014-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 Bakerdc4724c2014-11-05 09:05:38 -0800155 def get_form(self, request, obj=None, **kwargs):
Scott Baker9b3c1af2014-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 Bakerdc4724c2014-11-05 09:05:38 -0800161 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker9b3c1af2014-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 Bakerf4aeedc2014-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 Baker1a6a3902014-10-03 00:32:37 -0700177 pass
178
179class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
180 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700181
Scott Bakere8859f92014-05-23 12:42:40 -0700182class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400183 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700184 if not super(SingletonAdmin, self).has_add_permission(request):
185 return False
186
Siobhan Tullyce652d02013-10-08 21:52:35 -0400187 num_objects = self.model.objects.count()
188 if num_objects >= 1:
189 return False
190 else:
191 return True
192
Siobhan Tullyd3515752013-06-21 16:34:53 -0400193class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800194 def __init__(self, *args, **kwargs):
195 super(PlStackTabularInline, self).__init__(*args, **kwargs)
196
197 # InlineModelAdmin as no get_fields() method, so in order to add
198 # the selflink field, we override __init__ to modify self.fields and
199 # self.readonly_fields.
200
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800201 self.setup_selflink()
202
Scott Baker874936e2014-01-13 18:15:34 -0800203 def get_change_url(self, model, id):
204 """ Get the URL to a change form in the admin for this model """
205 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800206 try:
Scott Baker874936e2014-01-13 18:15:34 -0800207 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800208 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800209 return None
210
211 return url
212
213 def setup_selflink(self):
214 if hasattr(self, "selflink_fieldname"):
215 """ self.selflink_model can be defined to punch through a relation
216 to its target object. For example, in SliceNetworkInline, set
217 selflink_model = "network", and the URL will lead to the Network
218 object instead of trying to bring up a change view of the
219 SliceNetwork object.
220 """
221 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
222 else:
223 self.selflink_model = self.model
224
225 url = self.get_change_url(self.selflink_model, 0)
226
227 # We don't have an admin for this object, so don't create the
228 # selflink.
229 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800230 return
231
Scott Baker874936e2014-01-13 18:15:34 -0800232 # Since we need to add "selflink" to the field list, we need to create
233 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800234 if (self.fields is None):
235 self.fields = []
236 for f in self.model._meta.fields:
237 if f.editable and f.name != "id":
238 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800239
Scott Baker874936e2014-01-13 18:15:34 -0800240 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800241
Scott Baker874936e2014-01-13 18:15:34 -0800242 if self.readonly_fields is None:
243 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800244
Scott Baker874936e2014-01-13 18:15:34 -0800245 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800246
247 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800248 if hasattr(self, "selflink_fieldname"):
249 obj = getattr(obj, self.selflink_fieldname)
250
Scott Baker86568322014-01-12 16:53:31 -0800251 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800252 url = self.get_change_url(self.selflink_model, obj.id)
253 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800254 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800255 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800256
257 selflink.allow_tags = True
258 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400259
Scott Bakerb27b62c2014-08-15 16:29:16 -0700260 def has_add_permission(self, request):
261 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500262
263 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700264 readonly_fields = list(self.readonly_fields)[:]
265 if request.user.isReadOnlyUser():
266 for field in self.fields:
267 if not field in readonly_fields:
268 readonly_fields.append(field)
269 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500270
Scott Baker40c00762014-08-21 16:55:59 -0700271 def backend_status_icon(self, obj):
272 return mark_safe(backend_icon(obj))
273 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700274
Scott Bakerb27b62c2014-08-15 16:29:16 -0700275class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500276 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700277 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500278
Scott Bakerb27b62c2014-08-15 16:29:16 -0700279 def get_readonly_fields(self, request, obj=None):
280 readonly_fields = list(self.readonly_fields)[:]
281 if request.user.isReadOnlyUser():
282 for field in self.fields:
283 if not field in readonly_fields:
284 readonly_fields.append(field)
285 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500286
Scott Baker40c00762014-08-21 16:55:59 -0700287 def backend_status_icon(self, obj):
288 return mark_safe(backend_icon(obj))
289 backend_status_icon.short_description = ""
290
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400291class ReservationInline(PlStackTabularInline):
292 model = Reservation
293 extra = 0
294 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700295
Tony Mack5b061472014-02-04 07:57:10 -0500296 def queryset(self, request):
297 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400298
Scott Bakerb27b62c2014-08-15 16:29:16 -0700299class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400300 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400301 extra = 0
302 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500303 fields = ['service', 'name', 'value']
304
305 def queryset(self, request):
306 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400307
Scott Baker74d8e622013-07-29 16:04:22 -0700308class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400309 """ This is a callable that looks up a network name in a sliver and returns
310 the ip address for that network.
311 """
312
Scott Baker434ca7e2014-08-15 12:29:20 -0700313 byNetworkName = {} # class variable
314
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400315 def __init__(self, name):
316 self.short_description = name
317 self.__name__ = name
318 self.network_name = name
319
320 def __call__(self, obj):
321 if obj is not None:
322 for nbs in obj.networksliver_set.all():
323 if (nbs.network.name == self.network_name):
324 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700325 return ""
326
327 def __str__(self):
328 return self.network_name
329
Scott Baker434ca7e2014-08-15 12:29:20 -0700330 @staticmethod
331 def get(network_name):
332 """ We want to make sure we alwars return the same NetworkLookerUpper
333 because sometimes django will cause them to be instantiated multiple
334 times (and we don't want different ones in form.fields vs
335 SliverInline.readonly_fields).
336 """
337 if network_name not in NetworkLookerUpper.byNetworkName:
338 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
339 return NetworkLookerUpper.byNetworkName[network_name]
340
Siobhan Tullyd3515752013-06-21 16:34:53 -0400341class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400342 model = Sliver
Scott Baker9d856052015-01-19 11:32:20 -0800343 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400344 extra = 0
Scott Baker9d856052015-01-19 11:32:20 -0800345 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', '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 Mackb2dba4b2014-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 Mackb2dba4b2014-12-26 13:38:02 -0500355 if db_field.name == 'flavor':
Scott Baker4b6d9442014-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 Mack68a1e422014-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 Mack68a1e422014-12-08 16:43:02 -0500397class DeploymentPrivilegeInline(PlStackTabularInline):
398 model = DeploymentPrivilege
399 extra = 0
Tony Mack88c89902015-02-09 21:41:57 -0500400 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack68a1e422014-12-08 16:43:02 -0500401 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 Mack3066a952015-01-05 22:48:11 -0500407class ControllerSiteInline(PlStackTabularInline):
408 model = ControllerSite
409 extra = 0
410 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Macke2363c12015-01-06 15:08:20 -0500411 fields = ['controller', 'site', 'tenant_id']
Tony Mack3066a952015-01-05 22:48:11 -0500412
413
Siobhan Tullyd3515752013-06-21 16:34:53 -0400414class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400415 model = SitePrivilege
416 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400417 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700418 fields = ['backend_status_icon', 'user','site', 'role']
419 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400420
Tony Mackc2835a92013-05-28 09:18:49 -0400421 def formfield_for_foreignkey(self, db_field, request, **kwargs):
422 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500423 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400424
425 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500426 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400427 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
428
Tony Mack5b061472014-02-04 07:57:10 -0500429 def queryset(self, request):
430 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400431
Tony Mack3066a952015-01-05 22:48:11 -0500432class SiteDeploymentInline(PlStackTabularInline):
433 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400434 extra = 0
Tony Mackb81d5e42015-01-30 10:58:29 -0500435 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mackd14d48f2014-12-05 17:13:08 -0500436 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700437 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400438
439 def formfield_for_foreignkey(self, db_field, request, **kwargs):
440 if db_field.name == 'site':
441 kwargs['queryset'] = Site.select_by_user(request.user)
442
443 if db_field.name == 'deployment':
444 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mackd14d48f2014-12-05 17:13:08 -0500445
446 if db_field.name == 'controller':
447 kwargs['queryset'] = Controller.select_by_user(request.user)
448
Tony Mack3066a952015-01-05 22:48:11 -0500449 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400450
451 def queryset(self, request):
Tony Mack3066a952015-01-05 22:48:11 -0500452 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400453
454
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400455class SlicePrivilegeInline(PlStackTabularInline):
456 model = SlicePrivilege
457 suit_classes = 'suit-tab suit-tab-sliceprivileges'
458 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700459 fields = ('backend_status_icon', 'user', 'slice', 'role')
460 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400461
Tony Mackc2835a92013-05-28 09:18:49 -0400462 def formfield_for_foreignkey(self, db_field, request, **kwargs):
463 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700464 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400465 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700466 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400467
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400468 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400469
Tony Mack5b061472014-02-04 07:57:10 -0500470 def queryset(self, request):
471 return SlicePrivilege.select_by_user(request.user)
472
Scott Bakera0015eb2013-08-14 17:28:14 -0700473class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700474 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800475 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700476 extra = 0
477 verbose_name = "Network Connection"
478 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400479 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700480 fields = ['backend_status_icon', 'network']
481 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700482
Sapan Bhatia1b6bba22014-11-19 15:10:16 -0500483class ImageDeploymentsInline(PlStackTabularInline):
484 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700485 extra = 0
486 verbose_name = "Image Deployments"
487 verbose_name_plural = "Image Deployments"
488 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack06c8e472014-11-30 15:53:08 -0500489 fields = ['backend_status_icon', 'image', 'deployment']
490 readonly_fields = ['backend_status_icon']
491
492class ControllerImagesInline(PlStackTabularInline):
493 model = ControllerImages
494 extra = 0
495 verbose_name = "Controller Images"
496 verbose_name_plural = "Controller Images"
497 suit_classes = 'suit-tab suit-tab-admin-only'
498 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700499 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700500
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400501class SliceRoleAdmin(PlanetStackBaseAdmin):
502 model = SliceRole
503 pass
504
505class SiteRoleAdmin(PlanetStackBaseAdmin):
506 model = SiteRole
507 pass
508
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400509class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700510 images = forms.ModelMultipleChoiceField(
511 queryset=Image.objects.all(),
512 required=False,
513 help_text="Select which images should be deployed on this deployment",
514 widget=FilteredSelectMultiple(
515 verbose_name=('Images'), is_stacked=False
516 )
517 )
Scott Baker37b47902014-09-02 14:37:41 -0700518 flavors = forms.ModelMultipleChoiceField(
519 queryset=Flavor.objects.all(),
520 required=False,
521 help_text="Select which flavors should be usable on this deployment",
522 widget=FilteredSelectMultiple(
523 verbose_name=('Flavors'), is_stacked=False
524 )
525 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400526 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400527 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700528 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400529
Siobhan Tully320b4622014-01-17 15:11:14 -0500530 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700531 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500532 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
533
Scott Baker5380c522014-06-06 14:49:43 -0700534 self.fields['accessControl'].initial = "allow site " + request.user.site.name
535
Siobhan Tully320b4622014-01-17 15:11:14 -0500536 if self.instance and self.instance.pk:
Scott Baker69e045d2014-11-17 23:44:03 -0800537 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700538 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700539
540 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
541 """ helper function for handling m2m relations from the MultipleChoiceField
542
543 this_obj: the source object we want to link from
544
545 selected_objs: a list of destination objects we want to link to
546
547 all_relations: the full set of relations involving this_obj, including ones we don't want
548
549 relation_class: the class that implements the relation from source to dest
550
551 local_attrname: field name representing this_obj in relation_class
552
553 foreign_attrname: field name representing selected_objs in relation_class
554
555 This function will remove all newobjclass relations from this_obj
556 that are not contained in selected_objs, and add any relations that
557 are in selected_objs but don't exist in the data model yet.
558 """
559
560 existing_dest_objs = []
561 for relation in list(all_relations):
562 if getattr(relation, foreign_attrname) not in selected_objs:
563 #print "deleting site", sdp.site
564 relation.delete()
565 else:
566 existing_dest_objs.append(getattr(relation, foreign_attrname))
567
568 for dest_obj in selected_objs:
569 if dest_obj not in existing_dest_objs:
570 #print "adding site", site
571 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
572 relation = relation_class(**kwargs)
573 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500574
575 def save(self, commit=True):
576 deployment = super(DeploymentAdminForm, self).save(commit=False)
577
578 if commit:
579 deployment.save()
Scott Baker0057d052014-10-06 17:17:40 -0700580 # this has to be done after save() if/when a deployment is first created
581 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500582
583 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700584 # save_m2m() doesn't seem to work with 'through' relations. So we
585 # create/destroy the through models ourselves. There has to be
586 # a better way...
587
Tony Mackb2fde612014-12-15 11:45:02 -0500588 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
589 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
590 # so well handle that manually here
591 for flavor in deployment.flavors.all():
592 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mackd4ab7822014-12-15 12:37:59 -0500593 deployment.flavors.remove(flavor)
Tony Mackb2fde612014-12-15 11:45:02 -0500594 for flavor in self.cleaned_data['flavors']:
595 if flavor not in deployment.flavors.all():
596 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700597
Scott Baker37b47902014-09-02 14:37:41 -0700598 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500599
600 return deployment
601
Scott Bakerff5e0f32014-05-22 14:40:27 -0700602class DeploymentAdminROForm(DeploymentAdminForm):
603 def save(self, commit=True):
604 raise PermissionDenied
605
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500606class SiteAssocInline(PlStackTabularInline):
607 model = Site.deployments.through
608 extra = 0
609 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400610
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400611class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500612 model = Deployment
Scott Baker622bcf02015-02-10 08:40:34 -0800613 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
614 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack68a1e422014-12-08 16:43:02 -0500615 # node no longer directly connected to deployment
616 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mackb81d5e42015-01-30 10:58:29 -0500617 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700618 list_display = ['backend_status_icon', 'name']
619 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700620 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500621
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500622 user_readonly_fields = ['name']
623
Tony Mack68a1e422014-12-08 16:43:02 -0500624 # nodes no longer direclty connected to deployments
Scott Baker622bcf02015-02-10 08:40:34 -0800625 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500626
Scott Bakerff5e0f32014-05-22 14:40:27 -0700627 def get_form(self, request, obj=None, **kwargs):
Tony Mackcf29cfa2015-02-05 06:13:04 -0500628 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700629 kwargs["form"] = DeploymentAdminROForm
630 else:
631 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700632 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
633
634 # from stackexchange: pass the request object into the form
635
636 class AdminFormMetaClass(adminForm):
637 def __new__(cls, *args, **kwargs):
638 kwargs['request'] = request
639 return adminForm(*args, **kwargs)
640
641 return AdminFormMetaClass
642
Tony Mackd14d48f2014-12-05 17:13:08 -0500643class ControllerAdmin(PlanetStackBaseAdmin):
Scott Baker622bcf02015-02-10 08:40:34 -0800644 model = Controller
Tony Mackad2208f2015-01-19 15:05:42 -0500645 fieldList = ['name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password']
Scott Baker622bcf02015-02-10 08:40:34 -0800646 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack3066a952015-01-05 22:48:11 -0500647 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mackd14d48f2014-12-05 17:13:08 -0500648 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
649 list_display_links = ('backend_status_icon', 'name', )
650 readonly_fields = ('backend_status_text',)
651
652 user_readonly_fields = []
653
Tony Mack2e897fa2015-01-13 17:33:08 -0500654 def save_model(self, request, obj, form, change):
655 # update openstack connection to use this site/tenant
656 obj.save_by_user(request.user)
657
658 def delete_model(self, request, obj):
Scott Baker622bcf02015-02-10 08:40:34 -0800659 obj.delete_by_user(request.user)
660
661 @property
662 def suit_form_tabs(self):
663 tabs = [('general', 'Controller Details'),
664 ]
665
666 request=getattr(_thread_locals, "request", None)
667 if request and request.user.is_admin:
668 tabs.append( ('admin-only', 'Admin-Only') )
669
670 return tabs
Tony Mack2e897fa2015-01-13 17:33:08 -0500671
Siobhan Tullyce652d02013-10-08 21:52:35 -0400672class ServiceAttrAsTabInline(PlStackTabularInline):
673 model = ServiceAttribute
674 fields = ['name','value']
675 extra = 0
676 suit_classes = 'suit-tab suit-tab-serviceattrs'
677
Siobhan Tullyce652d02013-10-08 21:52:35 -0400678class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700679 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
680 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700681 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500682 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
683 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700684 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500685
686 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500687
688 suit_form_tabs =(('general', 'Service Details'),
689 ('slices','Slices'),
690 ('serviceattrs','Additional Attributes'),
691 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400692
Tony Mack4f134e62015-01-14 20:58:38 -0500693class SiteNodeInline(PlStackTabularInline):
694 model = Node
695 fields = ['name', 'site_deployment']
696 extra = 0
697 suit_classes = 'suit-tab suit-tab-nodes'
698
Tony Mack0553f282013-06-10 22:54:50 -0400699class SiteAdmin(PlanetStackBaseAdmin):
Tony Mack450b6e02015-01-25 12:35:29 -0500700 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
701 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400702 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500703 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400704 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400705 ]
Tony Mack450b6e02015-01-25 12:35:29 -0500706 #readonly_fields = ['backend_status_text', 'accountLink']
707 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500708
Tony Mack450b6e02015-01-25 12:35:29 -0500709 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
710 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500711
Scott Baker63d1a552014-08-21 15:19:07 -0700712 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
713 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400714 filter_horizontal = ('deployments',)
Tony Mackb81d5e42015-01-30 10:58:29 -0500715 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mackde100182015-01-14 12:11:05 -0500716 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400717 search_fields = ['name']
718
Tony Mack30dfcd72015-01-10 23:08:10 -0500719 @property
720 def suit_form_tabs(self):
721 tabs = [('general', 'Site Details'),
722 ('users','Users'),
723 ('siteprivileges','Privileges'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500724 ('slices','Slices'),
Tony Mack4f134e62015-01-14 20:58:38 -0500725 ('nodes','Nodes'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500726 ]
727
728 request=getattr(_thread_locals, "request", None)
729 if request and request.user.is_admin:
730 tabs.append( ('admin-only', 'Admin-Only') )
731
732 return tabs
733
Tony Mack04062832013-05-10 08:22:44 -0400734 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500735 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400736
Tony Mack5cd13202013-05-01 21:48:38 -0400737 def get_formsets(self, request, obj=None):
738 for inline in self.get_inline_instances(request, obj):
739 # hide MyInline in the add view
740 if obj is None:
741 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400742 if isinstance(inline, SliverInline):
743 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400744 yield inline.get_formset(request, obj)
745
Scott Baker545db2a2013-12-09 18:44:43 -0800746 def accountLink(self, obj):
747 link_obj = obj.accounts.all()
748 if link_obj:
749 reverse_path = "admin:core_account_change"
750 url = reverse(reverse_path, args =(link_obj[0].id,))
751 return "<a href='%s'>%s</a>" % (url, "view billing details")
752 else:
753 return "no billing data for this site"
754 accountLink.allow_tags = True
755 accountLink.short_description = "Billing"
756
Tony Mack332ee1d2014-02-04 15:33:45 -0500757 def save_model(self, request, obj, form, change):
758 # update openstack connection to use this site/tenant
759 obj.save_by_user(request.user)
760
761 def delete_model(self, request, obj):
762 obj.delete_by_user(request.user)
763
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500764
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400765class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700766 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400767 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500768 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400769 ]
Scott Baker40c00762014-08-21 16:55:59 -0700770 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700771 list_display = ('backend_status_icon', 'user', 'site', 'role')
772 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500773 user_readonly_fields = fieldList
774 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400775
Tony Mackc2835a92013-05-28 09:18:49 -0400776 def formfield_for_foreignkey(self, db_field, request, **kwargs):
777 if db_field.name == 'site':
778 if not request.user.is_admin:
779 # only show sites where user is an admin or pi
780 sites = set()
781 for site_privilege in SitePrivilege.objects.filer(user=request.user):
782 if site_privilege.role.role_type in ['admin', 'pi']:
783 sites.add(site_privilege.site)
784 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
785
786 if db_field.name == 'user':
787 if not request.user.is_admin:
788 # only show users from sites where caller has admin or pi role
789 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
790 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
791 sites = [site_privilege.site for site_privilege in site_privileges]
792 site_privileges = SitePrivilege.objects.filter(site__in=sites)
793 emails = [site_privilege.user.email for site_privilege in site_privileges]
794 users = User.objects.filter(email__in=emails)
795 kwargs['queryset'] = users
796
797 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
798
Tony Mack04062832013-05-10 08:22:44 -0400799 def queryset(self, request):
800 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400801 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400802 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500803 #if not request.user.is_admin:
804 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
805 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
806 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
807 # sites = Site.objects.filter(login_base__in=login_bases)
808 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400809 return qs
810
Siobhan Tullyce652d02013-10-08 21:52:35 -0400811class SliceForm(forms.ModelForm):
812 class Meta:
813 model = Slice
814 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700815 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400816 }
817
Tony Macke75841e2014-09-29 16:10:52 -0400818 def clean(self):
819 cleaned_data = super(SliceForm, self).clean()
820 name = cleaned_data.get('name')
Scott Baker3cb382c2014-10-06 23:09:59 -0700821 site = cleaned_data.get('site')
Tony Mackcc9e2592014-10-22 12:54:19 -0400822 slice_id = self.instance.id
823 if not site and slice_id:
824 site = Slice.objects.get(id=slice_id).site
Scott Baker3cb382c2014-10-06 23:09:59 -0700825 if (not isinstance(site,Site)):
826 # previous code indicates 'site' could be a site_id and not a site?
827 site = Slice.objects.get(id=site.id)
Tony Macke75841e2014-09-29 16:10:52 -0400828 if not name.startswith(site.login_base):
829 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
830 return cleaned_data
831
Tony Mack3066a952015-01-05 22:48:11 -0500832class ControllerSliceInline(PlStackTabularInline):
833 model = ControllerSlice
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700834 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -0500835 verbose_name = "Controller Slices"
836 verbose_name_plural = "Controller Slices"
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700837 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -0500838 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack30dfcd72015-01-10 23:08:10 -0500839 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700840
Tony Mack2bd5b412013-06-11 21:05:06 -0400841class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400842 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400843 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500844 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700845 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400846 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
847 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400848 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Mack3066a952015-01-05 22:48:11 -0500849 admin_inlines = [ControllerSliceInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400850
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500851 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400852
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700853 @property
854 def suit_form_tabs(self):
855 tabs =[('general', 'Slice Details'),
856 ('slicenetworks','Networks'),
857 ('sliceprivileges','Privileges'),
858 ('slivers','Slivers'),
Tony Mack450b6e02015-01-25 12:35:29 -0500859 #('reservations','Reservations'),
Tony Mackd2433382015-01-15 14:44:06 -0500860 ('tags','Tags'),
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700861 ]
862
863 request=getattr(_thread_locals, "request", None)
864 if request and request.user.is_admin:
865 tabs.append( ('admin-only', 'Admin-Only') )
866
867 return tabs
Tony Mack0aa732a2014-10-22 11:54:29 -0400868
869 def add_view(self, request, form_url='', extra_context=None):
870 # revert to default read-only fields
871 self.readonly_fields = ('backend_status_text',)
872 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
873
874 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack0aa732a2014-10-22 11:54:29 -0400875 # cannot change the site of an existing slice so make the site field read only
876 if object_id:
877 self.readonly_fields = ('backend_status_text','site')
878 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400879
Scott Baker510fdbb2014-08-05 17:19:24 -0700880 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700881 deployment_nodes = []
882 for node in Node.objects.all():
Scott Baker66b11e22015-01-21 16:24:07 -0800883 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -0700884
Scott Baker7a61dc42014-09-02 17:08:20 -0700885 deployment_flavors = []
886 for flavor in Flavor.objects.all():
887 for deployment in flavor.deployments.all():
888 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
889
Tony Mack68a1e422014-12-08 16:43:02 -0500890 deployment_images = []
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700891 for image in Image.objects.all():
Tony Mack68a1e422014-12-08 16:43:02 -0500892 for deployment_image in image.imagedeployments.all():
Scott Bakerf2c0c512014-12-22 17:35:34 -0800893 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700894
Tony Mackec23b992014-09-02 21:18:45 -0400895 site_login_bases = []
896 for site in Site.objects.all():
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700897 site_login_bases.append((site.id, site.login_base))
898
Scott Baker510fdbb2014-08-05 17:19:24 -0700899 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700900 context["deployment_flavors"] = deployment_flavors
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700901 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400902 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700903 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
904
Tony Mackc2835a92013-05-28 09:18:49 -0400905 def formfield_for_foreignkey(self, db_field, request, **kwargs):
906 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500907 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400908 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 -0700909
Tony Mackc2835a92013-05-28 09:18:49 -0400910 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
911
Tony Mack04062832013-05-10 08:22:44 -0400912 def queryset(self, request):
913 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500914 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400915
Tony Mack79748612013-05-01 14:52:03 -0400916 def get_formsets(self, request, obj=None):
917 for inline in self.get_inline_instances(request, obj):
918 # hide MyInline in the add view
919 if obj is None:
920 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400921 if isinstance(inline, SliverInline):
922 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400923 yield inline.get_formset(request, obj)
924
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400925class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400926 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700927 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400928 ]
Scott Baker40c00762014-08-21 16:55:59 -0700929 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700930 list_display = ('backend_status_icon', 'user', 'slice', 'role')
931 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400932
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500933 user_readonly_fields = ['user', 'slice', 'role']
934 user_readonly_inlines = []
935
Tony Mackc2835a92013-05-28 09:18:49 -0400936 def formfield_for_foreignkey(self, db_field, request, **kwargs):
937 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500938 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400939
940 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500941 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400942
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400943 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400944
Tony Mack04062832013-05-10 08:22:44 -0400945 def queryset(self, request):
946 # admins can see all memberships. Users can only see memberships of
947 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -0500948 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400949
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400950 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -0400951 # update openstack connection to use this site/tenant
952 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400953 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400954 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400955 obj.save()
956
957 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -0400958 # update openstack connection to use this site/tenant
959 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -0400960 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -0400961 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400962 obj.delete()
963
Siobhan Tully567e3e62013-06-21 18:03:16 -0400964
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400965class ImageAdmin(PlanetStackBaseAdmin):
966
Scott Baker36f50872014-08-21 13:01:25 -0700967 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -0700968 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400969 'classes': ['suit-tab suit-tab-general']})
970 ]
Scott Baker40c00762014-08-21 16:55:59 -0700971 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400972
Tony Mack06c8e472014-11-30 15:53:08 -0500973 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400974
Tony Mack06c8e472014-11-30 15:53:08 -0500975 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -0700976
Tony Mack32e1ce32014-05-07 13:29:41 -0400977 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -0700978
Scott Baker63d1a552014-08-21 15:19:07 -0700979 list_display = ['backend_status_icon', 'name']
980 list_display_links = ('backend_status_icon', 'name', )
981
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400982class NodeForm(forms.ModelForm):
983 class Meta:
984 widgets = {
985 'site': LinkedSelect,
986 'deployment': LinkedSelect
987 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400988
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500989class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400990 form = NodeForm
Tony Mack68a1e422014-12-08 16:43:02 -0500991 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -0700992 list_display_links = ('backend_status_icon', 'name', )
Tony Mack68a1e422014-12-08 16:43:02 -0500993 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500994
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400995 inlines = [TagInline,SliverInline]
Tony Mack68a1e422014-12-08 16:43:02 -0500996 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -0700997 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400998
Tony Mack68a1e422014-12-08 16:43:02 -0500999 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001000 user_readonly_inlines = [TagInline,SliverInline]
1001
Tony Mack5fecf712015-01-12 21:40:09 -05001002 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001003
Siobhan Tully567e3e62013-06-21 18:03:16 -04001004
Tony Mackd90cdbf2013-04-16 22:48:40 -04001005class SliverForm(forms.ModelForm):
1006 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001007 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001008 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001009 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001010 widgets = {
1011 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001012 'instance_name': PlainTextWidget(),
Scott Baker9d856052015-01-19 11:32:20 -08001013 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001014 'slice': LinkedSelect,
Tony Mackb2dba4b2014-12-26 13:38:02 -05001015 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001016 'node': LinkedSelect,
1017 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001018 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001019
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001020class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001021 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1022 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001023 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1024 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001025
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001026class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001027 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001028 fieldsets = [
Tony Mack707f7d72015-01-30 12:52:46 -05001029 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'flavor', 'image', 'ssh_command'], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001030 ]
Tony Mackdb8580b2015-01-30 17:20:46 -05001031 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack707f7d72015-01-30 12:52:46 -05001032 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
1033 list_display_links = ('backend_status_icon', 'all_ips_string',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001034
Scott Bakerc72997a2015-01-19 08:24:08 -08001035 suit_form_tabs =(('general', 'Sliver Details'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001036
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001037 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001038
Tony Mackb2dba4b2014-12-26 13:38:02 -05001039 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001040
Scott Baker7ccc6ad2015-01-25 22:16:13 -08001041 def ssh_command(self, obj):
1042 ssh_command = obj.get_ssh_command()
1043 if ssh_command:
1044 return ssh_command
1045 else:
1046 return "(not available)"
1047
Tony Mackc2835a92013-05-28 09:18:49 -04001048 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1049 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001050 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001051
1052 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1053
Tony Mack04062832013-05-10 08:22:44 -04001054 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001055 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001056 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001057 return Sliver.select_by_user(request.user)
1058
Tony Mack04062832013-05-10 08:22:44 -04001059
Tony Mack1d6b85f2013-05-07 18:49:14 -04001060 def get_formsets(self, request, obj=None):
1061 # make some fields read only if we are updating an existing record
1062 if obj == None:
Tony Mackb428feb2015-01-30 17:42:10 -05001063 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001064 else:
Tony Mackb428feb2015-01-30 17:42:10 -05001065 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'slice', 'flavor', 'image', 'node')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001066
1067 for inline in self.get_inline_instances(request, obj):
1068 # hide MyInline in the add view
1069 if obj is None:
1070 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001071 if isinstance(inline, SliverInline):
1072 inline.model.caller = request.user
1073 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001074
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001075 #def save_model(self, request, obj, form, change):
1076 # # update openstack connection to use this site/tenant
1077 # auth = request.session.get('auth', {})
1078 # auth['tenant'] = obj.slice.name
1079 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1080 # obj.creator = request.user
1081 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001082
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001083 #def delete_model(self, request, obj):
1084 # # update openstack connection to use this site/tenant
1085 # auth = request.session.get('auth', {})
1086 # auth['tenant'] = obj.slice.name
1087 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1088 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001089
Siobhan Tully53437282013-04-26 19:30:27 -04001090class UserCreationForm(forms.ModelForm):
1091 """A form for creating new users. Includes all the required
1092 fields, plus a repeated password."""
1093 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1094 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1095
1096 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001097 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001098 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001099
1100 def clean_password2(self):
1101 # Check that the two password entries match
1102 password1 = self.cleaned_data.get("password1")
1103 password2 = self.cleaned_data.get("password2")
1104 if password1 and password2 and password1 != password2:
1105 raise forms.ValidationError("Passwords don't match")
1106 return password2
1107
1108 def save(self, commit=True):
1109 # Save the provided password in hashed format
1110 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001111 user.password = self.cleaned_data["password1"]
1112 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001113 if commit:
1114 user.save()
1115 return user
1116
Siobhan Tully567e3e62013-06-21 18:03:16 -04001117
Siobhan Tully53437282013-04-26 19:30:27 -04001118class UserChangeForm(forms.ModelForm):
1119 """A form for updating users. Includes all the fields on
1120 the user, but replaces the password field with admin's
1121 password hash display field.
1122 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001123 password = ReadOnlyPasswordHashField(label='Password',
1124 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001125
1126 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001127 model = User
Scott Baker69e045d2014-11-17 23:44:03 -08001128 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001129
1130 def clean_password(self):
1131 # Regardless of what the user provides, return the initial value.
1132 # This is done here, rather than on the field, because the
1133 # field does not have access to the initial value
1134 return self.initial["password"]
1135
Scott Baker2c3cb642014-05-19 17:55:56 -07001136class UserDashboardViewInline(PlStackTabularInline):
1137 model = UserDashboardView
1138 extra = 0
1139 suit_classes = 'suit-tab suit-tab-dashboards'
1140 fields = ['user', 'dashboardView', 'order']
1141
Tony Mack30dfcd72015-01-10 23:08:10 -05001142class ControllerUserInline(PlStackTabularInline):
1143 model = ControllerUser
1144 extra = 0
1145 suit_classes = 'suit-tab suit-tab-admin-only'
1146 fields = ['controller', 'user', 'kuser_id']
1147 readonly_fields=['controller']
1148
1149
Scott Bakerf4aeedc2014-10-03 13:10:47 -07001150class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1151 # Note: Make sure PermissionCheckingAdminMixin is listed before
1152 # admin.ModelAdmin in the class declaration.
1153
Siobhan Tully53437282013-04-26 19:30:27 -04001154 class Meta:
1155 app_label = "core"
1156
1157 # The forms to add and change user instances
1158 form = UserChangeForm
1159 add_form = UserCreationForm
1160
1161 # The fields to be used in displaying the User model.
1162 # These override the definitions on the base UserAdmin
1163 # that reference specific fields on auth.User.
Scott Bakera111f442015-01-24 13:33:26 -08001164 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1165 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001166 list_filter = ('site',)
Scott Baker90472612015-01-29 10:55:53 -08001167 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack30dfcd72015-01-10 23:08:10 -05001168 admin_inlines = [ControllerUserInline]
Scott Baker1a6a3902014-10-03 00:32:37 -07001169 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001170 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1171
Siobhan Tully53437282013-04-26 19:30:27 -04001172 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001173 ('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 -04001174 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001175 #('Important dates', {'fields': ('last_login',)}),
1176 )
1177 add_fieldsets = (
1178 (None, {
1179 'classes': ('wide',),
Tony Mack365545d2015-01-28 12:03:15 -05001180 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001181 ),
1182 )
Scott Baker40c00762014-08-21 16:55:59 -07001183 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001184 search_fields = ('email',)
1185 ordering = ('email',)
1186 filter_horizontal = ()
1187
Scott Baker3ca51f62014-05-23 12:05:11 -07001188 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001189
Scott Baker6a995352014-10-06 17:51:20 -07001190 @property
1191 def suit_form_tabs(self):
1192 if getattr(_thread_locals, "obj", None) is None:
1193 return []
1194 else:
Tony Mack30dfcd72015-01-10 23:08:10 -05001195 tabs = [('general','Login Details'),
Scott Baker6a995352014-10-06 17:51:20 -07001196 ('contact','Contact Information'),
1197 ('sliceprivileges','Slice Privileges'),
Scott Baker90472612015-01-29 10:55:53 -08001198 ('siteprivileges','Site Privileges')]
Tony Mack30dfcd72015-01-10 23:08:10 -05001199
1200 request=getattr(_thread_locals, "request", None)
1201 if request and request.user.is_admin:
1202 tabs.append( ('admin-only', 'Admin-Only') )
1203
1204 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001205
Tony Mackc2835a92013-05-28 09:18:49 -04001206 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1207 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001208 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001209
1210 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1211
Tony Mack5b061472014-02-04 07:57:10 -05001212 def queryset(self, request):
1213 return User.select_by_user(request.user)
1214
Tony Mackc8f443d2015-01-25 21:58:30 -05001215 def get_form(self, request, obj=None, **kwargs):
Tony Mack03b92292015-01-28 12:37:12 -05001216 # copy login details list
1217 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack933b2912015-01-28 12:49:58 -05001218 if not request.user.is_admin:
Scott Baker90472612015-01-29 10:55:53 -08001219 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mack03b92292015-01-28 12:37:12 -05001220 if 'is_admin' in login_details_fields:
1221 login_details_fields.remove('is_admin')
1222 if 'is_readonly' in login_details_fields:
1223 login_details_fields.remove('is_readonly')
Tony Mack933b2912015-01-28 12:49:58 -05001224 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Tony Mack03b92292015-01-28 12:37:12 -05001225 # only admins and pis can change a user's site
Tony Mack933b2912015-01-28 12:49:58 -05001226 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mack03b92292015-01-28 12:37:12 -05001227 self.fieldsets = (
1228 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1229 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1230 )
Tony Mackc8f443d2015-01-25 21:58:30 -05001231 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1232
Scott Bakerf2c0c512014-12-22 17:35:34 -08001233class ControllerDashboardViewInline(PlStackTabularInline):
1234 model = ControllerDashboardView
Scott Baker786a9c12014-12-19 16:41:12 -08001235 extra = 0
1236 fields = ["controller", "url"]
1237 suit_classes = 'suit-tab suit-tab-controllers'
1238
Scott Baker2c3cb642014-05-19 17:55:56 -07001239class DashboardViewAdmin(PlanetStackBaseAdmin):
1240 fieldsets = [('Dashboard View Details',
Scott Bakerc15ad1b2015-01-18 16:33:30 -08001241 {'fields': ['backend_status_text', 'name', 'url', 'enabled'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001242 'classes': ['suit-tab suit-tab-general']})
1243 ]
Scott Baker2c44e6e2015-01-18 16:46:26 -08001244 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001245 readonly_fields = ('backend_status_text', )
Scott Bakerf2c0c512014-12-22 17:35:34 -08001246 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001247
Scott Baker786a9c12014-12-19 16:41:12 -08001248 suit_form_tabs =(('general','Dashboard View Details'),
1249 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001250
Scott Baker0165fac2014-01-13 11:49:26 -08001251class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001252 model = ServiceResource
1253 extra = 0
1254
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001255class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001256 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1257 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001258 inlines = [ServiceResourceInline]
1259
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001260 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1261 user_readonly_inlines = []
1262
Scott Baker0165fac2014-01-13 11:49:26 -08001263class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001264 model = ReservedResource
1265 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001266 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001267
1268 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1269 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1270
1271 if db_field.name == 'resource':
1272 # restrict resources to those that the slice's service class allows
1273 if request._slice is not None:
1274 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1275 if len(field.queryset) > 0:
1276 field.initial = field.queryset.all()[0]
1277 else:
1278 field.queryset = field.queryset.none()
1279 elif db_field.name == 'sliver':
1280 # restrict slivers to those that belong to the slice
1281 if request._slice is not None:
1282 field.queryset = field.queryset.filter(slice = request._slice)
1283 else:
1284 field.queryset = field.queryset.none()
1285
1286 return field
1287
Tony Mack5b061472014-02-04 07:57:10 -05001288 def queryset(self, request):
1289 return ReservedResource.select_by_user(request.user)
1290
Scott Baker133c9212013-05-17 09:09:11 -07001291class ReservationChangeForm(forms.ModelForm):
1292 class Meta:
1293 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001294 widgets = {
1295 'slice' : LinkedSelect
1296 }
Scott Baker133c9212013-05-17 09:09:11 -07001297
1298class ReservationAddForm(forms.ModelForm):
1299 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1300 refresh = forms.CharField(widget=forms.HiddenInput())
1301
1302 class Media:
1303 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1304
1305 def clean_slice(self):
1306 slice = self.cleaned_data.get("slice")
1307 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1308 if len(x) == 0:
1309 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1310 return slice
1311
1312 class Meta:
1313 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001314 widgets = {
1315 'slice' : LinkedSelect
1316 }
1317
Scott Baker133c9212013-05-17 09:09:11 -07001318
1319class ReservationAddRefreshForm(ReservationAddForm):
1320 """ This form is displayed when the Reservation Form receives an update
1321 from the Slice dropdown onChange handler. It doesn't validate the
1322 data and doesn't save the data. This will cause the form to be
1323 redrawn.
1324 """
1325
Scott Baker8737e5f2013-05-17 09:35:32 -07001326 """ don't validate anything other than slice """
1327 dont_validate_fields = ("startTime", "duration")
1328
Scott Baker133c9212013-05-17 09:09:11 -07001329 def full_clean(self):
1330 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001331
1332 for fieldname in self.dont_validate_fields:
1333 if fieldname in self._errors:
1334 del self._errors[fieldname]
1335
Scott Baker133c9212013-05-17 09:09:11 -07001336 return result
1337
1338 """ don't save anything """
1339 def is_valid(self):
1340 return False
1341
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001342class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001343 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001344 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001345 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001346 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001347 form = ReservationAddForm
1348
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001349 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1350
1351 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001352 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001353
Scott Baker133c9212013-05-17 09:09:11 -07001354 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001355 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001356 request._refresh = False
1357 request._slice = None
1358 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001359 # "refresh" will be set to "1" if the form was submitted due to
1360 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001361 if request.POST.get("refresh","1") == "1":
1362 request._refresh = True
1363 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001364
1365 # Keep track of the slice that was selected, so the
1366 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001367 request._slice = request.POST.get("slice",None)
1368 if (request._slice is not None):
1369 request._slice = Slice.objects.get(id=request._slice)
1370
1371 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1372 return result
1373
Scott Bakeracd45142013-05-19 16:19:16 -07001374 def changelist_view(self, request, extra_context = None):
1375 timezone.activate(request.user.timezone)
1376 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1377
Scott Baker133c9212013-05-17 09:09:11 -07001378 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001379 request._obj_ = obj
1380 if obj is not None:
1381 # For changes, set request._slice to the slice already set in the
1382 # object.
1383 request._slice = obj.slice
1384 self.form = ReservationChangeForm
1385 else:
1386 if getattr(request, "_refresh", False):
1387 self.form = ReservationAddRefreshForm
1388 else:
1389 self.form = ReservationAddForm
1390 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1391
Scott Baker133c9212013-05-17 09:09:11 -07001392 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001393 if (obj is not None):
1394 # Prevent slice from being changed after the reservation has been
1395 # created.
1396 return ['slice']
1397 else:
Scott Baker133c9212013-05-17 09:09:11 -07001398 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001399
Tony Mack5b061472014-02-04 07:57:10 -05001400 def queryset(self, request):
1401 return Reservation.select_by_user(request.user)
1402
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001403class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001404 list_display = ("backend_status_icon", "name", )
1405 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001406 user_readonly_fields = ['name']
1407 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001408
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001409class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001410 list_display = ("backend_status_icon", "name", )
1411 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001412 user_readonly_fields = ['name']
1413 user_readonly_inlines = []
1414
Scott Baker0165fac2014-01-13 11:49:26 -08001415class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001416 model = Router.networks.through
1417 extra = 0
1418 verbose_name_plural = "Routers"
1419 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001420 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001421
Scott Bakerb27b62c2014-08-15 16:29:16 -07001422class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001423 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001424 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001425 verbose_name_plural = "Parameters"
1426 verbose_name = "Parameter"
1427 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001428 fields = ['backend_status_icon', 'parameter', 'value']
1429 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001430
Scott Baker0165fac2014-01-13 11:49:26 -08001431class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001432 fields = ['backend_status_icon', 'network','sliver','ip']
1433 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001434 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001435 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001436 extra = 0
1437 verbose_name_plural = "Slivers"
1438 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001439 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001440
Scott Baker0165fac2014-01-13 11:49:26 -08001441class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001442 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001443 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001444 extra = 0
1445 verbose_name_plural = "Slices"
1446 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001447 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001448 fields = ['backend_status_icon', 'network','slice']
1449 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001450
Tony Mack3066a952015-01-05 22:48:11 -05001451class ControllerNetworkInline(PlStackTabularInline):
1452 model = ControllerNetwork
Scott Baker8806cdf2014-10-17 16:27:23 -07001453 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -05001454 verbose_name_plural = "Controller Networks"
1455 verbose_name = "Controller Network"
Scott Baker8806cdf2014-10-17 16:27:23 -07001456 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -05001457 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Baker8806cdf2014-10-17 16:27:23 -07001458 readonly_fields = ('backend_status_icon', )
1459
Scott Baker69e045d2014-11-17 23:44:03 -08001460class NetworkForm(forms.ModelForm):
1461 class Meta:
1462 model = Network
1463 widgets = {
1464 'topologyParameters': UploadTextareaWidget,
1465 'controllerParameters': UploadTextareaWidget,
1466 }
1467
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001468class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001469 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1470 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001471 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001472
Scott Bakerd7d2a392013-08-06 08:57:30 -07001473 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Mack3066a952015-01-05 22:48:11 -05001474 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001475
Scott Baker69e045d2014-11-17 23:44:03 -08001476 form=NetworkForm
1477
Siobhan Tully2d95e482013-09-06 10:56:06 -04001478 fieldsets = [
Scott Baker549aa252015-01-03 12:29:29 -08001479 (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 Baker3e28dd72014-11-17 16:04:45 -08001480 'classes':['suit-tab suit-tab-general']}),
Scott Baker549aa252015-01-03 12:29:29 -08001481 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001482 'classes':['suit-tab suit-tab-sdn']}),
1483 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001484
Scott Baker40c00762014-08-21 16:55:59 -07001485 readonly_fields = ('backend_status_text', )
Scott Baker549aa252015-01-03 12:29:29 -08001486 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 -04001487
Scott Baker8806cdf2014-10-17 16:27:23 -07001488 @property
1489 def suit_form_tabs(self):
1490 tabs=[('general','Network Details'),
Scott Baker3e28dd72014-11-17 16:04:45 -08001491 ('sdn', 'SDN Configuration'),
Scott Baker8806cdf2014-10-17 16:27:23 -07001492 ('netparams', 'Parameters'),
1493 ('networkslivers','Slivers'),
1494 ('networkslices','Slices'),
1495 ('routers','Routers'),
1496 ]
1497
1498 request=getattr(_thread_locals, "request", None)
1499 if request and request.user.is_admin:
1500 tabs.append( ('admin-only', 'Admin-Only') )
1501
1502 return tabs
1503
1504
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001505class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker369f9b92015-01-03 12:03:38 -08001506 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001507 list_display_links = ('backend_status_icon', 'name', )
Scott Baker369f9b92015-01-03 12:03:38 -08001508 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001509 user_readonly_inlines = []
Scott Baker3e28dd72014-11-17 16:04:45 -08001510 fieldsets = [
Scott Baker369f9b92015-01-03 12:03:38 -08001511 (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001512 'classes':['suit-tab suit-tab-general']}),]
1513 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001514
Scott Baker37b47902014-09-02 14:37:41 -07001515class FlavorAdmin(PlanetStackBaseAdmin):
1516 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1517 list_display_links = ("backend_status_icon", "name")
1518 user_readonly_fields = ("name", "flavor")
1519 fields = ("name", "description", "flavor", "order", "default")
1520
Tony Mack31c2b8f2013-04-26 20:01:42 -04001521# register a signal that caches the user's credentials when they log in
1522def cache_credentials(sender, user, request, **kwds):
1523 auth = {'username': request.POST['username'],
1524 'password': request.POST['password']}
1525 request.session['auth'] = auth
1526user_logged_in.connect(cache_credentials)
1527
Scott Baker15cddfa2013-12-09 13:45:19 -08001528def dollar_field(fieldName, short_description):
1529 def newFunc(self, obj):
1530 try:
1531 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1532 except:
1533 x=getattr(obj, fieldName, 0.0)
1534 return x
1535 newFunc.short_description = short_description
1536 return newFunc
1537
1538def right_dollar_field(fieldName, short_description):
1539 def newFunc(self, obj):
1540 try:
1541 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1542 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1543 except:
1544 x=getattr(obj, fieldName, 0.0)
1545 return x
1546 newFunc.short_description = short_description
1547 newFunc.allow_tags = True
1548 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001549
Scott Baker0165fac2014-01-13 11:49:26 -08001550class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001551 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001552 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001553 verbose_name_plural = "Charges"
1554 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001555 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001556 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1557 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1558 can_delete = False
1559 max_num = 0
1560
1561 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001562
1563class InvoiceAdmin(admin.ModelAdmin):
1564 list_display = ("date", "account")
1565
1566 inlines = [InvoiceChargeInline]
1567
Scott Baker9cb88a22013-12-09 18:56:00 -08001568 fields = ["date", "account", "dollar_amount"]
1569 readonly_fields = ["date", "account", "dollar_amount"]
1570
1571 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001572
Scott Baker0165fac2014-01-13 11:49:26 -08001573class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001574 model = Invoice
1575 extra = 0
1576 verbose_name_plural = "Invoices"
1577 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001578 fields = ["date", "dollar_amount"]
1579 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001580 suit_classes = 'suit-tab suit-tab-accountinvoice'
1581 can_delete=False
1582 max_num=0
1583
1584 dollar_amount = right_dollar_field("amount", "Amount")
1585
Scott Baker0165fac2014-01-13 11:49:26 -08001586class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001587 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001588 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001589 verbose_name_plural = "Charges"
1590 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001591 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001592 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1593 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001594 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001595 can_delete=False
1596 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001597
1598 def queryset(self, request):
1599 qs = super(PendingChargeInline, self).queryset(request)
1600 qs = qs.filter(state="pending")
1601 return qs
1602
Scott Baker15cddfa2013-12-09 13:45:19 -08001603 dollar_amount = right_dollar_field("amount", "Amount")
1604
Scott Baker0165fac2014-01-13 11:49:26 -08001605class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001606 model=Payment
1607 extra = 1
1608 verbose_name_plural = "Payments"
1609 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001610 fields = ["date", "dollar_amount"]
1611 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001612 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001613 can_delete=False
1614 max_num=0
1615
1616 dollar_amount = right_dollar_field("amount", "Amount")
1617
Scott Baker43105042013-12-06 23:23:36 -08001618class AccountAdmin(admin.ModelAdmin):
1619 list_display = ("site", "balance_due")
1620
1621 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1622
1623 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001624 (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 -08001625
Scott Baker15cddfa2013-12-09 13:45:19 -08001626 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001627
1628 suit_form_tabs =(
1629 ('general','Account Details'),
1630 ('accountinvoice', 'Invoices'),
1631 ('accountpayments', 'Payments'),
1632 ('accountpendingcharges','Pending Charges'),
1633 )
1634
Scott Baker15cddfa2013-12-09 13:45:19 -08001635 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1636 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1637 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1638
Siobhan Tully53437282013-04-26 19:30:27 -04001639# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001640admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001641# ... and, since we're not using Django's builtin permissions,
1642# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001643#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001644
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001645#Do not show django evolution in the admin interface
1646from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001647#admin.site.unregister(Version)
1648#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001649
1650
1651# When debugging it is often easier to see all the classes, but for regular use
1652# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001653showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001654
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001655admin.site.register(Deployment, DeploymentAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05001656admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001657admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001658admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001659admin.site.register(Service, ServiceAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05001660#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001661admin.site.register(Network, NetworkAdmin)
1662admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001663admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05001664#admin.site.register(Account, AccountAdmin)
1665#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001666
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001667if True:
1668 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1669 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001670 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001671 admin.site.register(Tag, TagAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05001672 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001673 admin.site.register(SiteRole)
1674 admin.site.register(SliceRole)
1675 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001676 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001677 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1678 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001679 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001680 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001681 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001682 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001683