blob: 29cecfd9d7b3a1e33ee141924b473f2a4d07dd86 [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Scott Baker9f6b8ed2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Bakercbfb6002014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Scott Bakere2bbf7e2014-01-13 12:09:31 -080017from django.core.urlresolvers import reverse, NoReverseMatch
Scott Baker9f6b8ed2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
20from django.forms.utils import flatatt, to_current_timezone
Scott Baker3cde7372014-10-21 21:03:08 -070021from cgi import escape as html_escape
Tony Mack7130ac32013-03-22 21:58:00 -040022
Scott Baker36f50872014-08-21 13:01:25 -070023import django_evolution
Scott Baker0a5633b2014-10-06 17:51:20 -070024import threading
25
26# thread locals necessary to work around a django-suit issue
27_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070028
Scott Baker40c00762014-08-21 16:55:59 -070029def backend_icon(obj): # backend_status, enacted, updated):
30 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
31 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Bakerfbf06642014-09-09 10:38:15 -070032 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
Scott Baker40c00762014-08-21 16:55:59 -070033 else:
34 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
Scott Bakerfbf06642014-09-09 10:38:15 -070035 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
Scott Baker63d1a552014-08-21 15:19:07 -070036 else:
Scott Baker3cde7372014-10-21 21:03:08 -070037 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % html_escape(obj.backend_status, quote=True)
Scott Baker40c00762014-08-21 16:55:59 -070038
39def backend_text(obj):
40 icon = backend_icon(obj)
41 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Baker3cde7372014-10-21 21:03:08 -070042 return "%s %s" % (icon, "successfully enacted")
Scott Baker40c00762014-08-21 16:55:59 -070043 else:
Scott Baker3cde7372014-10-21 21:03:08 -070044 return "%s %s" % (icon, html_escape(obj.backend_status, quote=True))
Scott Baker63d1a552014-08-21 15:19:07 -070045
Scott Baker9f6b8ed2014-11-17 23:44:03 -080046class UploadTextareaWidget(AdminTextareaWidget):
47 def render(self, name, value, attrs=None):
48 if value is None:
49 value = ''
50 final_attrs = self.build_attrs(attrs, name=name)
51 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
52 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
53 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
54 flatatt(final_attrs),
55 force_text(value))
56
Scott Baker36f50872014-08-21 13:01:25 -070057class PlainTextWidget(forms.HiddenInput):
58 input_type = 'hidden'
59
60 def render(self, name, value, attrs=None):
61 if value is None:
62 value = ''
63 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
64
Scott Baker86c83ab2014-10-03 13:10:47 -070065class PermissionCheckingAdminMixin(object):
Scott Bakercbfb6002014-10-03 00:32:37 -070066 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050067
68 def has_add_permission(self, request, obj=None):
69 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070070
Siobhan Tullycf04fb62014-01-11 11:25:57 -050071 def has_delete_permission(self, request, obj=None):
72 return (not self.__user_is_readonly(request))
73
74 def save_model(self, request, obj, form, change):
75 if self.__user_is_readonly(request):
Scott Bakercbfb6002014-10-03 00:32:37 -070076 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050077 raise PermissionDenied
Scott Bakercbfb6002014-10-03 00:32:37 -070078
79 obj.caller = request.user
80 # update openstack connection to use this site/tenant
81 obj.save_by_user(request.user)
82
83 def delete_model(self, request, obj):
84 obj.delete_by_user(request.user)
85
86 def save_formset(self, request, form, formset, change):
87 instances = formset.save(commit=False)
88 for instance in instances:
89 instance.save_by_user(request.user)
90
91 # BUG in django 1.7? Objects are not deleted by formset.save if
92 # commit is False. So let's delete them ourselves.
93 #
94 # code from forms/models.py save_existing_objects()
95 try:
96 forms_to_delete = formset.deleted_forms
97 except AttributeError:
98 forms_to_delete = []
99 if formset.initial_forms:
100 for form in formset.initial_forms:
101 obj = form.instance
102 if form in forms_to_delete:
103 if obj.pk is None:
104 continue
105 formset.deleted_objects.append(obj)
106 obj.delete()
107
108 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500109
110 def get_actions(self,request):
Scott Baker86c83ab2014-10-03 13:10:47 -0700111 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500112
113 if self.__user_is_readonly(request):
114 if 'delete_selected' in actions:
115 del actions['delete_selected']
116
117 return actions
118
119 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500120 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -0700121 if not hasattr(self, "readonly_save"):
122 # save the original readonly fields
123 self.readonly_save = self.readonly_fields
124 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -0700125 if hasattr(self, "user_readonly_fields"):
126 self.readonly_fields=self.user_readonly_fields
127 if hasattr(self, "user_readonly_inlines"):
128 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -0700129 else:
130 if hasattr(self, "readonly_save"):
131 # restore the original readonly fields
132 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -0700133 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700134 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500135
136 try:
Scott Baker86c83ab2014-10-03 13:10:47 -0700137 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500138 except PermissionDenied:
139 pass
140 if request.method == 'POST':
141 raise PermissionDenied
142 request.readonly = True
Scott Baker86c83ab2014-10-03 13:10:47 -0700143 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500144
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500145 def __user_is_readonly(self, request):
146 return request.user.isReadOnlyUser()
147
Scott Baker40c00762014-08-21 16:55:59 -0700148 def backend_status_text(self, obj):
149 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700150
Scott Baker63d1a552014-08-21 15:19:07 -0700151 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700152 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700153 backend_status_icon.short_description = ""
154
Scott Baker24ded6a2014-11-05 09:05:38 -0800155 def get_form(self, request, obj=None, **kwargs):
Scott Baker5c432692014-10-16 00:57:55 -0700156 # Save obj and request in thread-local storage, so suit_form_tabs can
157 # use it to determine whether we're in edit or add mode, and can
158 # determine whether the user is an admin.
159 _thread_locals.request = request
160 _thread_locals.obj = obj
Scott Baker24ded6a2014-11-05 09:05:38 -0800161 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker5c432692014-10-16 00:57:55 -0700162
163 def get_inline_instances(self, request, obj=None):
164 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
165
166 # inlines that should only be shown to an admin user
167 if request.user.is_admin:
168 for inline_class in getattr(self, "admin_inlines", []):
169 inlines.append(inline_class(self.model, self.admin_site))
170
171 return inlines
172
Scott Baker86c83ab2014-10-03 13:10:47 -0700173class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
174 # Note: Make sure PermissionCheckingAdminMixin is listed before
175 # admin.ModelAdmin in the class declaration.
176
Scott Bakercbfb6002014-10-03 00:32:37 -0700177 pass
178
179class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
180 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700181
Scott Bakere8859f92014-05-23 12:42:40 -0700182class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400183 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700184 if not super(SingletonAdmin, self).has_add_permission(request):
185 return False
186
Siobhan Tullyce652d02013-10-08 21:52:35 -0400187 num_objects = self.model.objects.count()
188 if num_objects >= 1:
189 return False
190 else:
191 return True
192
Siobhan Tullyd3515752013-06-21 16:34:53 -0400193class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800194 def __init__(self, *args, **kwargs):
195 super(PlStackTabularInline, self).__init__(*args, **kwargs)
196
197 # InlineModelAdmin as no get_fields() method, so in order to add
198 # the selflink field, we override __init__ to modify self.fields and
199 # self.readonly_fields.
200
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800201 self.setup_selflink()
202
Scott Baker874936e2014-01-13 18:15:34 -0800203 def get_change_url(self, model, id):
204 """ Get the URL to a change form in the admin for this model """
205 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800206 try:
Scott Baker874936e2014-01-13 18:15:34 -0800207 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800208 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800209 return None
210
211 return url
212
213 def setup_selflink(self):
214 if hasattr(self, "selflink_fieldname"):
215 """ self.selflink_model can be defined to punch through a relation
216 to its target object. For example, in SliceNetworkInline, set
217 selflink_model = "network", and the URL will lead to the Network
218 object instead of trying to bring up a change view of the
219 SliceNetwork object.
220 """
221 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
222 else:
223 self.selflink_model = self.model
224
225 url = self.get_change_url(self.selflink_model, 0)
226
227 # We don't have an admin for this object, so don't create the
228 # selflink.
229 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800230 return
231
Scott Baker874936e2014-01-13 18:15:34 -0800232 # Since we need to add "selflink" to the field list, we need to create
233 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800234 if (self.fields is None):
235 self.fields = []
236 for f in self.model._meta.fields:
237 if f.editable and f.name != "id":
238 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800239
Scott Baker874936e2014-01-13 18:15:34 -0800240 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800241
Scott Baker874936e2014-01-13 18:15:34 -0800242 if self.readonly_fields is None:
243 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800244
Scott Baker874936e2014-01-13 18:15:34 -0800245 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800246
247 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800248 if hasattr(self, "selflink_fieldname"):
249 obj = getattr(obj, self.selflink_fieldname)
250
Scott Baker86568322014-01-12 16:53:31 -0800251 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800252 url = self.get_change_url(self.selflink_model, obj.id)
253 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800254 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800255 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800256
257 selflink.allow_tags = True
258 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400259
Scott Bakerb27b62c2014-08-15 16:29:16 -0700260 def has_add_permission(self, request):
261 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500262
263 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700264 readonly_fields = list(self.readonly_fields)[:]
265 if request.user.isReadOnlyUser():
266 for field in self.fields:
267 if not field in readonly_fields:
268 readonly_fields.append(field)
269 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500270
Scott Baker40c00762014-08-21 16:55:59 -0700271 def backend_status_icon(self, obj):
272 return mark_safe(backend_icon(obj))
273 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700274
Scott Bakerb27b62c2014-08-15 16:29:16 -0700275class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500276 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700277 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500278
Scott Bakerb27b62c2014-08-15 16:29:16 -0700279 def get_readonly_fields(self, request, obj=None):
280 readonly_fields = list(self.readonly_fields)[:]
281 if request.user.isReadOnlyUser():
282 for field in self.fields:
283 if not field in readonly_fields:
284 readonly_fields.append(field)
285 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500286
Scott Baker40c00762014-08-21 16:55:59 -0700287 def backend_status_icon(self, obj):
288 return mark_safe(backend_icon(obj))
289 backend_status_icon.short_description = ""
290
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400291class ReservationInline(PlStackTabularInline):
292 model = Reservation
293 extra = 0
294 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700295
Tony Mack5b061472014-02-04 07:57:10 -0500296 def queryset(self, request):
297 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400298
Scott Bakerb27b62c2014-08-15 16:29:16 -0700299class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400300 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400301 extra = 0
302 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500303 fields = ['service', 'name', 'value']
304
305 def queryset(self, request):
306 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400307
Scott Baker74d8e622013-07-29 16:04:22 -0700308class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400309 """ This is a callable that looks up a network name in a sliver and returns
310 the ip address for that network.
311 """
312
Scott Baker434ca7e2014-08-15 12:29:20 -0700313 byNetworkName = {} # class variable
314
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400315 def __init__(self, name):
316 self.short_description = name
317 self.__name__ = name
318 self.network_name = name
319
320 def __call__(self, obj):
321 if obj is not None:
322 for nbs in obj.networksliver_set.all():
323 if (nbs.network.name == self.network_name):
324 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700325 return ""
326
327 def __str__(self):
328 return self.network_name
329
Scott Baker434ca7e2014-08-15 12:29:20 -0700330 @staticmethod
331 def get(network_name):
332 """ We want to make sure we alwars return the same NetworkLookerUpper
333 because sometimes django will cause them to be instantiated multiple
334 times (and we don't want different ones in form.fields vs
335 SliverInline.readonly_fields).
336 """
337 if network_name not in NetworkLookerUpper.byNetworkName:
338 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
339 return NetworkLookerUpper.byNetworkName[network_name]
340
Siobhan Tullyd3515752013-06-21 16:34:53 -0400341class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400342 model = Sliver
Tony Mackbf6aa302014-12-26 13:38:02 -0500343 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400344 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700345 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400346 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700347
Tony Mack5b061472014-02-04 07:57:10 -0500348 def queryset(self, request):
349 return Sliver.select_by_user(request.user)
350
Scott Bakerb24cc932014-06-09 10:51:16 -0700351 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackbf6aa302014-12-26 13:38:02 -0500352 if db_field.name == 'deployment':
Scott Baker3b678742014-06-09 13:11:54 -0700353 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker7a61dc42014-09-02 17:08:20 -0700354 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Tony Mackbf6aa302014-12-26 13:38:02 -0500355 if db_field.name == 'flavor':
Scott Baker32481312014-09-08 12:14:14 -0700356 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700357
358 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700359
360 return field
361
Siobhan Tullyd3515752013-06-21 16:34:53 -0400362class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400363 model = Site
364 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400365 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400366
Tony Mack5b061472014-02-04 07:57:10 -0500367 def queryset(self, request):
368 return Site.select_by_user(request.user)
369
Siobhan Tullyd3515752013-06-21 16:34:53 -0400370class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400371 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700372 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
373 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400374 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400375 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400376
Tony Mack5b061472014-02-04 07:57:10 -0500377 def queryset(self, request):
378 return User.select_by_user(request.user)
379
Siobhan Tullyd3515752013-06-21 16:34:53 -0400380class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400381 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700382 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
383 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400384 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400385 suit_classes = 'suit-tab suit-tab-slices'
386
Tony Mack5b061472014-02-04 07:57:10 -0500387 def queryset(self, request):
388 return Slice.select_by_user(request.user)
389
Siobhan Tullyd3515752013-06-21 16:34:53 -0400390class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400391 model = Node
392 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400393 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack93d1b032014-12-08 16:43:02 -0500394 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700395 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400396
Tony Mack93d1b032014-12-08 16:43:02 -0500397class DeploymentPrivilegeInline(PlStackTabularInline):
398 model = DeploymentPrivilege
399 extra = 0
400 suit_classes = 'suit-tab suit-tab-admin-only'
401 fields = ['backend_status_icon', 'user','role','deployment']
402 readonly_fields = ('backend_status_icon', )
403
404 def queryset(self, request):
405 return DeploymentPrivilege.select_by_user(request.user)
406
Tony Macka7dbd422015-01-05 22:48:11 -0500407class ControllerSiteInline(PlStackTabularInline):
408 model = ControllerSite
409 extra = 0
410 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack8f30ebe2015-01-06 15:08:20 -0500411 fields = ['controller', 'site', 'tenant_id']
Tony Macka7dbd422015-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 Macka7dbd422015-01-05 22:48:11 -0500432class SiteDeploymentInline(PlStackTabularInline):
433 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400434 extra = 0
435 suit_classes = 'suit-tab suit-tab-deployments'
Tony Mack528d4222014-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 Mack528d4222014-12-05 17:13:08 -0500445
446 if db_field.name == 'controller':
447 kwargs['queryset'] = Controller.select_by_user(request.user)
448
Tony Macka7dbd422015-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 Macka7dbd422015-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 Bhatiae9f96f62014-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 Mack336e0f92014-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):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400510 sites = forms.ModelMultipleChoiceField(
511 queryset=Site.objects.all(),
512 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700513 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400514 widget=FilteredSelectMultiple(
515 verbose_name=('Sites'), is_stacked=False
516 )
517 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700518 images = forms.ModelMultipleChoiceField(
519 queryset=Image.objects.all(),
520 required=False,
521 help_text="Select which images should be deployed on this deployment",
522 widget=FilteredSelectMultiple(
523 verbose_name=('Images'), is_stacked=False
524 )
525 )
Scott Baker37b47902014-09-02 14:37:41 -0700526 flavors = forms.ModelMultipleChoiceField(
527 queryset=Flavor.objects.all(),
528 required=False,
529 help_text="Select which flavors should be usable on this deployment",
530 widget=FilteredSelectMultiple(
531 verbose_name=('Flavors'), is_stacked=False
532 )
533 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400534 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400535 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700536 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400537
Siobhan Tully320b4622014-01-17 15:11:14 -0500538 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700539 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500540 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
541
Scott Baker5380c522014-06-06 14:49:43 -0700542 self.fields['accessControl'].initial = "allow site " + request.user.site.name
543
Siobhan Tully320b4622014-01-17 15:11:14 -0500544 if self.instance and self.instance.pk:
Scott Baker43574572015-01-08 21:20:28 -0800545 self.fields['sites'].initial = [x for x in self.instance.sites.all()]
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800546 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700547 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700548
549 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
550 """ helper function for handling m2m relations from the MultipleChoiceField
551
552 this_obj: the source object we want to link from
553
554 selected_objs: a list of destination objects we want to link to
555
556 all_relations: the full set of relations involving this_obj, including ones we don't want
557
558 relation_class: the class that implements the relation from source to dest
559
560 local_attrname: field name representing this_obj in relation_class
561
562 foreign_attrname: field name representing selected_objs in relation_class
563
564 This function will remove all newobjclass relations from this_obj
565 that are not contained in selected_objs, and add any relations that
566 are in selected_objs but don't exist in the data model yet.
567 """
568
569 existing_dest_objs = []
570 for relation in list(all_relations):
571 if getattr(relation, foreign_attrname) not in selected_objs:
572 #print "deleting site", sdp.site
573 relation.delete()
574 else:
575 existing_dest_objs.append(getattr(relation, foreign_attrname))
576
577 for dest_obj in selected_objs:
578 if dest_obj not in existing_dest_objs:
579 #print "adding site", site
580 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
581 relation = relation_class(**kwargs)
582 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500583
584 def save(self, commit=True):
585 deployment = super(DeploymentAdminForm, self).save(commit=False)
586
587 if commit:
588 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700589 # this has to be done after save() if/when a deployment is first created
590 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500591
592 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700593 # save_m2m() doesn't seem to work with 'through' relations. So we
594 # create/destroy the through models ourselves. There has to be
595 # a better way...
596
Scott Baker43574572015-01-08 21:20:28 -0800597 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployment.all(), SiteDeployment, "deployment", "site")
Tony Mack592aa952014-12-15 11:45:02 -0500598 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
599 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
600 # so well handle that manually here
601 for flavor in deployment.flavors.all():
602 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mack11f4d202014-12-15 12:37:59 -0500603 deployment.flavors.remove(flavor)
Tony Mack592aa952014-12-15 11:45:02 -0500604 for flavor in self.cleaned_data['flavors']:
605 if flavor not in deployment.flavors.all():
606 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700607
Scott Baker37b47902014-09-02 14:37:41 -0700608 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500609
610 return deployment
611
Scott Bakerff5e0f32014-05-22 14:40:27 -0700612class DeploymentAdminROForm(DeploymentAdminForm):
613 def save(self, commit=True):
614 raise PermissionDenied
615
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500616class SiteAssocInline(PlStackTabularInline):
617 model = Site.deployments.through
618 extra = 0
619 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400620
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400621class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500622 model = Deployment
Tony Mack93d1b032014-12-08 16:43:02 -0500623 fieldList = ['backend_status_text', 'name', 'sites', 'images', 'flavors', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500624 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Tony Mack93d1b032014-12-08 16:43:02 -0500625 # node no longer directly connected to deployment
626 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
627 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700628 list_display = ['backend_status_icon', 'name']
629 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700630 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500631
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500632 user_readonly_fields = ['name']
633
Tony Mack93d1b032014-12-08 16:43:02 -0500634 # nodes no longer direclty connected to deployments
635 #suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
Tony Mackab6538f2015-01-12 21:40:09 -0500636 suit_form_tabs =(('sites','Deployment Details'),('deploymentprivileges','Privileges'),,('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500637
Scott Bakerff5e0f32014-05-22 14:40:27 -0700638 def get_form(self, request, obj=None, **kwargs):
639 if request.user.isReadOnlyUser():
640 kwargs["form"] = DeploymentAdminROForm
641 else:
642 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700643 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
644
645 # from stackexchange: pass the request object into the form
646
647 class AdminFormMetaClass(adminForm):
648 def __new__(cls, *args, **kwargs):
649 kwargs['request'] = request
650 return adminForm(*args, **kwargs)
651
652 return AdminFormMetaClass
653
Tony Mack528d4222014-12-05 17:13:08 -0500654class ControllerAdminForm(forms.ModelForm):
Tony Mack26564362015-01-06 17:49:25 -0500655 sites = forms.ModelMultipleChoiceField(
656 queryset=Site.objects.all(),
Tony Mack528d4222014-12-05 17:13:08 -0500657 required=False,
Tony Mack26564362015-01-06 17:49:25 -0500658 help_text="Select which sites are managed by this controller",
Tony Mack528d4222014-12-05 17:13:08 -0500659 widget=FilteredSelectMultiple(
Tony Mack26564362015-01-06 17:49:25 -0500660 verbose_name=('Sites'), is_stacked=False
Tony Mack528d4222014-12-05 17:13:08 -0500661 )
662 )
663
664 class Meta:
665 model = Controller
666
Tony Mack93d1b032014-12-08 16:43:02 -0500667 def __init__(self, *args, **kwargs):
Tony Mack528d4222014-12-05 17:13:08 -0500668 request = kwargs.pop('request', None)
669 super(ControllerAdminForm, self).__init__(*args, **kwargs)
670
671 if self.instance and self.instance.pk:
Tony Mack26564362015-01-06 17:49:25 -0500672 self.fields['sites'].initial = [x.site_deployment for x in self.instance.controllersite.all()]
Tony Mack528d4222014-12-05 17:13:08 -0500673
Tony Mack93d1b032014-12-08 16:43:02 -0500674 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
Tony Mack528d4222014-12-05 17:13:08 -0500675 """ helper function for handling m2m relations from the MultipleChoiceField
676 this_obj: the source object we want to link from
677 selected_objs: a list of destination objects we want to link to
678 all_relations: the full set of relations involving this_obj, including ones we don't want
679 relation_class: the class that implements the relation from source to dest
680 local_attrname: field name representing this_obj in relation_class
681 foreign_attrname: field name representing selected_objs in relation_class
682 This function will remove all newobjclass relations from this_obj
683 that are not contained in selected_objs, and add any relations that
684 are in selected_objs but don't exist in the data model yet.
685 """
686 existing_dest_objs = []
687 for relation in list(all_relations):
688 if getattr(relation, foreign_attrname) not in selected_objs:
689 #print "deleting site", sdp.site
690 relation.delete()
691 else:
692 existing_dest_objs.append(getattr(relation, foreign_attrname))
693
694 for dest_obj in selected_objs:
695 if dest_obj not in existing_dest_objs:
696 #print "adding site", site
697 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
698 relation = relation_class(**kwargs)
699 relation.save()
700
Tony Mack93d1b032014-12-08 16:43:02 -0500701 def save(self, commit=True):
702 controller = super(ControllerAdminForm, self).save(commit=False)
703 if commit:
704 controller.save()
Tony Mack528d4222014-12-05 17:13:08 -0500705
Tony Mack93d1b032014-12-08 16:43:02 -0500706 if controller.pk:
707 # save_m2m() doesn't seem to work with 'through' relations. So we
708 # create/destroy the through models ourselves. There has to be
709 # a better way...
Tony Mack26564362015-01-06 17:49:25 -0500710 self.manipulate_m2m_objs(controller, self.cleaned_data['sites'], controller.controllersite.all(), ControllerSite, "controller", "site")
Tony Mack1e828282015-01-03 17:40:42 -0500711 pass
712
Tony Mack93d1b032014-12-08 16:43:02 -0500713 self.save_m2m()
Tony Mack528d4222014-12-05 17:13:08 -0500714
Tony Mack93d1b032014-12-08 16:43:02 -0500715 return controller
Tony Mack528d4222014-12-05 17:13:08 -0500716
717class ControllerAdmin(PlanetStackBaseAdmin):
718 model = Controller
Tony Mack93d1b032014-12-08 16:43:02 -0500719 fieldList = ['name', 'version', 'backend_type', 'auth_url', 'admin_user', 'admin_tenant','admin_password']
720 #fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Macka7dbd422015-01-05 22:48:11 -0500721 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500722 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
723 list_display_links = ('backend_status_icon', 'name', )
724 readonly_fields = ('backend_status_text',)
725
726 user_readonly_fields = []
727
728 def get_form(self, request, obj=None, **kwargs):
Tony Mack93d1b032014-12-08 16:43:02 -0500729 print self.fieldsets
Tony Mack528d4222014-12-05 17:13:08 -0500730 if request.user.isReadOnlyUser():
731 kwargs["form"] = ControllerAdminROForm
732 else:
733 kwargs["form"] = ControllerAdminForm
Tony Mack93d1b032014-12-08 16:43:02 -0500734 adminForm = super(ControllerAdmin,self).get_form(request, obj, **kwargs)
Tony Mack528d4222014-12-05 17:13:08 -0500735
736 # from stackexchange: pass the request object into the form
737
738 class AdminFormMetaClass(adminForm):
739 def __new__(cls, *args, **kwargs):
740 kwargs['request'] = request
741 return adminForm(*args, **kwargs)
742
743 return AdminFormMetaClass
744
Siobhan Tullyce652d02013-10-08 21:52:35 -0400745class ServiceAttrAsTabInline(PlStackTabularInline):
746 model = ServiceAttribute
747 fields = ['name','value']
748 extra = 0
749 suit_classes = 'suit-tab suit-tab-serviceattrs'
750
Siobhan Tullyce652d02013-10-08 21:52:35 -0400751class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700752 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
753 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700754 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500755 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
756 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700757 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500758
759 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500760
761 suit_form_tabs =(('general', 'Service Details'),
762 ('slices','Slices'),
763 ('serviceattrs','Additional Attributes'),
764 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400765
Tony Mack0553f282013-06-10 22:54:50 -0400766class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700767 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400768 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500769 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400770 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400771 ]
Scott Baker40c00762014-08-21 16:55:59 -0700772 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500773
774 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500775
Scott Baker63d1a552014-08-21 15:19:07 -0700776 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
777 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400778 filter_horizontal = ('deployments',)
Tony Macka7dbd422015-01-05 22:48:11 -0500779 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteDeploymentInline]
Tony Mack3c01ff92015-01-10 23:08:10 -0500780 admin_inlines = [ControllerSite]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400781 search_fields = ['name']
782
Tony Mack3c01ff92015-01-10 23:08:10 -0500783 @property
784 def suit_form_tabs(self):
785 tabs = [('general', 'Site Details'),
786 ('users','Users'),
787 ('siteprivileges','Privileges'),
788 ('deployments','Deployments'),
789 ('slices','Slices'),
790 #('nodes','Nodes'),
Tony Mack3c01ff92015-01-10 23:08:10 -0500791 ]
792
793 request=getattr(_thread_locals, "request", None)
794 if request and request.user.is_admin:
795 tabs.append( ('admin-only', 'Admin-Only') )
796
797 return tabs
798
Tony Mack04062832013-05-10 08:22:44 -0400799 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500800 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400801
Tony Mack5cd13202013-05-01 21:48:38 -0400802 def get_formsets(self, request, obj=None):
803 for inline in self.get_inline_instances(request, obj):
804 # hide MyInline in the add view
805 if obj is None:
806 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400807 if isinstance(inline, SliverInline):
808 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400809 yield inline.get_formset(request, obj)
810
Scott Baker545db2a2013-12-09 18:44:43 -0800811 def accountLink(self, obj):
812 link_obj = obj.accounts.all()
813 if link_obj:
814 reverse_path = "admin:core_account_change"
815 url = reverse(reverse_path, args =(link_obj[0].id,))
816 return "<a href='%s'>%s</a>" % (url, "view billing details")
817 else:
818 return "no billing data for this site"
819 accountLink.allow_tags = True
820 accountLink.short_description = "Billing"
821
Tony Mack332ee1d2014-02-04 15:33:45 -0500822 def save_model(self, request, obj, form, change):
823 # update openstack connection to use this site/tenant
824 obj.save_by_user(request.user)
825
826 def delete_model(self, request, obj):
827 obj.delete_by_user(request.user)
828
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500829
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400830class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700831 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400832 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500833 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400834 ]
Scott Baker40c00762014-08-21 16:55:59 -0700835 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700836 list_display = ('backend_status_icon', 'user', 'site', 'role')
837 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500838 user_readonly_fields = fieldList
839 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400840
Tony Mackc2835a92013-05-28 09:18:49 -0400841 def formfield_for_foreignkey(self, db_field, request, **kwargs):
842 if db_field.name == 'site':
843 if not request.user.is_admin:
844 # only show sites where user is an admin or pi
845 sites = set()
846 for site_privilege in SitePrivilege.objects.filer(user=request.user):
847 if site_privilege.role.role_type in ['admin', 'pi']:
848 sites.add(site_privilege.site)
849 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
850
851 if db_field.name == 'user':
852 if not request.user.is_admin:
853 # only show users from sites where caller has admin or pi role
854 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
855 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
856 sites = [site_privilege.site for site_privilege in site_privileges]
857 site_privileges = SitePrivilege.objects.filter(site__in=sites)
858 emails = [site_privilege.user.email for site_privilege in site_privileges]
859 users = User.objects.filter(email__in=emails)
860 kwargs['queryset'] = users
861
862 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
863
Tony Mack04062832013-05-10 08:22:44 -0400864 def queryset(self, request):
865 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400866 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400867 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500868 #if not request.user.is_admin:
869 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
870 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
871 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
872 # sites = Site.objects.filter(login_base__in=login_bases)
873 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400874 return qs
875
Siobhan Tullyce652d02013-10-08 21:52:35 -0400876class SliceForm(forms.ModelForm):
877 class Meta:
878 model = Slice
879 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700880 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400881 }
882
Tony Mack2cbd3802014-09-29 16:10:52 -0400883 def clean(self):
884 cleaned_data = super(SliceForm, self).clean()
885 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -0700886 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -0400887 slice_id = self.instance.id
888 if not site and slice_id:
889 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -0700890 if (not isinstance(site,Site)):
891 # previous code indicates 'site' could be a site_id and not a site?
892 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -0400893 if not name.startswith(site.login_base):
894 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
895 return cleaned_data
896
Tony Macka7dbd422015-01-05 22:48:11 -0500897class ControllerSliceInline(PlStackTabularInline):
898 model = ControllerSlice
Scott Bakerc4efdc72014-10-15 16:54:04 -0700899 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500900 verbose_name = "Controller Slices"
901 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -0700902 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -0500903 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack3c01ff92015-01-10 23:08:10 -0500904 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerc4efdc72014-10-15 16:54:04 -0700905
Tony Mack2bd5b412013-06-11 21:05:06 -0400906class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400907 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400908 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500909 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700910 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400911 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
912 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400913 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Macka7dbd422015-01-05 22:48:11 -0500914 admin_inlines = [ControllerSliceInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400915
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500916 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400917
Scott Bakerc4efdc72014-10-15 16:54:04 -0700918 @property
919 def suit_form_tabs(self):
920 tabs =[('general', 'Slice Details'),
921 ('slicenetworks','Networks'),
922 ('sliceprivileges','Privileges'),
923 ('slivers','Slivers'),
Scott Bakerc4efdc72014-10-15 16:54:04 -0700924 ('reservations','Reservations'),
925 ]
926
927 request=getattr(_thread_locals, "request", None)
928 if request and request.user.is_admin:
929 tabs.append( ('admin-only', 'Admin-Only') )
930
931 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -0400932
933 def add_view(self, request, form_url='', extra_context=None):
934 # revert to default read-only fields
935 self.readonly_fields = ('backend_status_text',)
936 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
937
938 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -0400939 # cannot change the site of an existing slice so make the site field read only
940 if object_id:
941 self.readonly_fields = ('backend_status_text','site')
942 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400943
Scott Baker510fdbb2014-08-05 17:19:24 -0700944 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700945 deployment_nodes = []
946 for node in Node.objects.all():
Sapan Bhatia48208cd2014-12-22 01:37:26 -0500947 deployment_nodes.append( (node.site_deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -0700948
Scott Baker7a61dc42014-09-02 17:08:20 -0700949 deployment_flavors = []
950 for flavor in Flavor.objects.all():
951 for deployment in flavor.deployments.all():
952 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
953
Tony Mack93d1b032014-12-08 16:43:02 -0500954 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -0700955 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -0500956 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -0800957 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -0700958
Tony Mackec23b992014-09-02 21:18:45 -0400959 site_login_bases = []
960 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -0700961 site_login_bases.append((site.id, site.login_base))
962
Scott Baker510fdbb2014-08-05 17:19:24 -0700963 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700964 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -0700965 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400966 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700967 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
968
Tony Mackc2835a92013-05-28 09:18:49 -0400969 def formfield_for_foreignkey(self, db_field, request, **kwargs):
970 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500971 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400972 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 -0700973
Tony Mackc2835a92013-05-28 09:18:49 -0400974 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
975
Tony Mack04062832013-05-10 08:22:44 -0400976 def queryset(self, request):
977 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500978 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400979
Tony Mack79748612013-05-01 14:52:03 -0400980 def get_formsets(self, request, obj=None):
981 for inline in self.get_inline_instances(request, obj):
982 # hide MyInline in the add view
983 if obj is None:
984 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400985 if isinstance(inline, SliverInline):
986 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400987 yield inline.get_formset(request, obj)
988
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400989class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400990 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700991 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400992 ]
Scott Baker40c00762014-08-21 16:55:59 -0700993 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700994 list_display = ('backend_status_icon', 'user', 'slice', 'role')
995 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400996
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500997 user_readonly_fields = ['user', 'slice', 'role']
998 user_readonly_inlines = []
999
Tony Mackc2835a92013-05-28 09:18:49 -04001000 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1001 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001002 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001003
1004 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001005 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001006
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001007 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001008
Tony Mack04062832013-05-10 08:22:44 -04001009 def queryset(self, request):
1010 # admins can see all memberships. Users can only see memberships of
1011 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001012 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001013
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001014 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001015 # update openstack connection to use this site/tenant
1016 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001017 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001018 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001019 obj.save()
1020
1021 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001022 # update openstack connection to use this site/tenant
1023 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001024 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001025 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001026 obj.delete()
1027
Siobhan Tully567e3e62013-06-21 18:03:16 -04001028
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001029class ImageAdmin(PlanetStackBaseAdmin):
1030
Scott Baker36f50872014-08-21 13:01:25 -07001031 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001032 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001033 'classes': ['suit-tab suit-tab-general']})
1034 ]
Scott Baker40c00762014-08-21 16:55:59 -07001035 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001036
Tony Mack336e0f92014-11-30 15:53:08 -05001037 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001038
Tony Mack336e0f92014-11-30 15:53:08 -05001039 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001040
Tony Mack32e1ce32014-05-07 13:29:41 -04001041 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001042
Scott Baker63d1a552014-08-21 15:19:07 -07001043 list_display = ['backend_status_icon', 'name']
1044 list_display_links = ('backend_status_icon', 'name', )
1045
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001046class NodeForm(forms.ModelForm):
1047 class Meta:
1048 widgets = {
1049 'site': LinkedSelect,
1050 'deployment': LinkedSelect
1051 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001052
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001053class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001054 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -05001055 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001056 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -05001057 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001058
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001059 inlines = [TagInline,SliverInline]
Tony Mack93d1b032014-12-08 16:43:02 -05001060 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001061 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001062
Tony Mack93d1b032014-12-08 16:43:02 -05001063 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001064 user_readonly_inlines = [TagInline,SliverInline]
1065
Tony Mackab6538f2015-01-12 21:40:09 -05001066 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001067
Siobhan Tully567e3e62013-06-21 18:03:16 -04001068
Tony Mackd90cdbf2013-04-16 22:48:40 -04001069class SliverForm(forms.ModelForm):
1070 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001071 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001072 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001073 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001074 widgets = {
1075 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001076 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001077 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001078 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001079 'node': LinkedSelect,
1080 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001081 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001082
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001083class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001084 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1085 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001086 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1087 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001088
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001089class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001090 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001091 fieldsets = [
Tony Mackbf6aa302014-12-26 13:38:02 -05001092 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001093 ]
Scott Baker40c00762014-08-21 16:55:59 -07001094 readonly_fields = ('backend_status_text', )
Tony Mackbf6aa302014-12-26 13:38:02 -05001095 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Baker63d1a552014-08-21 15:19:07 -07001096 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001097
Tony Mackab6538f2015-01-12 21:40:09 -05001098 suit_form_tabs =(('general', 'Sliver Details'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001099
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001100 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001101
Tony Mackbf6aa302014-12-26 13:38:02 -05001102 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001103
Tony Mackc2835a92013-05-28 09:18:49 -04001104 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1105 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001106 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001107
1108 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1109
Tony Mack04062832013-05-10 08:22:44 -04001110 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001111 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001112 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001113 return Sliver.select_by_user(request.user)
1114
Tony Mack04062832013-05-10 08:22:44 -04001115
Tony Mack1d6b85f2013-05-07 18:49:14 -04001116 def get_formsets(self, request, obj=None):
1117 # make some fields read only if we are updating an existing record
1118 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -07001119 #self.readonly_fields = ('ip', 'instance_name')
Scott Bakerfbb45862014-10-17 16:27:23 -07001120 self.readonly_fields = ('backend_status_text',)
Tony Mack1d6b85f2013-05-07 18:49:14 -04001121 else:
Scott Bakerfbb45862014-10-17 16:27:23 -07001122 self.readonly_fields = ('backend_status_text',)
Scott Baker36f50872014-08-21 13:01:25 -07001123 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001124
1125 for inline in self.get_inline_instances(request, obj):
1126 # hide MyInline in the add view
1127 if obj is None:
1128 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001129 if isinstance(inline, SliverInline):
1130 inline.model.caller = request.user
1131 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001132
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001133 #def save_model(self, request, obj, form, change):
1134 # # update openstack connection to use this site/tenant
1135 # auth = request.session.get('auth', {})
1136 # auth['tenant'] = obj.slice.name
1137 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1138 # obj.creator = request.user
1139 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001140
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001141 #def delete_model(self, request, obj):
1142 # # update openstack connection to use this site/tenant
1143 # auth = request.session.get('auth', {})
1144 # auth['tenant'] = obj.slice.name
1145 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1146 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001147
Siobhan Tully53437282013-04-26 19:30:27 -04001148class UserCreationForm(forms.ModelForm):
1149 """A form for creating new users. Includes all the required
1150 fields, plus a repeated password."""
1151 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1152 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1153
1154 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001155 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001156 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001157
1158 def clean_password2(self):
1159 # Check that the two password entries match
1160 password1 = self.cleaned_data.get("password1")
1161 password2 = self.cleaned_data.get("password2")
1162 if password1 and password2 and password1 != password2:
1163 raise forms.ValidationError("Passwords don't match")
1164 return password2
1165
1166 def save(self, commit=True):
1167 # Save the provided password in hashed format
1168 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001169 user.password = self.cleaned_data["password1"]
1170 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001171 if commit:
1172 user.save()
1173 return user
1174
Siobhan Tully567e3e62013-06-21 18:03:16 -04001175
Siobhan Tully53437282013-04-26 19:30:27 -04001176class UserChangeForm(forms.ModelForm):
1177 """A form for updating users. Includes all the fields on
1178 the user, but replaces the password field with admin's
1179 password hash display field.
1180 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001181 password = ReadOnlyPasswordHashField(label='Password',
1182 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001183
1184 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001185 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001186 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001187
1188 def clean_password(self):
1189 # Regardless of what the user provides, return the initial value.
1190 # This is done here, rather than on the field, because the
1191 # field does not have access to the initial value
1192 return self.initial["password"]
1193
Scott Baker2c3cb642014-05-19 17:55:56 -07001194class UserDashboardViewInline(PlStackTabularInline):
1195 model = UserDashboardView
1196 extra = 0
1197 suit_classes = 'suit-tab suit-tab-dashboards'
1198 fields = ['user', 'dashboardView', 'order']
1199
Tony Mack3c01ff92015-01-10 23:08:10 -05001200class ControllerUserInline(PlStackTabularInline):
1201 model = ControllerUser
1202 extra = 0
1203 suit_classes = 'suit-tab suit-tab-admin-only'
1204 fields = ['controller', 'user', 'kuser_id']
1205 readonly_fields=['controller']
1206
1207
Scott Baker86c83ab2014-10-03 13:10:47 -07001208class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1209 # Note: Make sure PermissionCheckingAdminMixin is listed before
1210 # admin.ModelAdmin in the class declaration.
1211
Siobhan Tully53437282013-04-26 19:30:27 -04001212 class Meta:
1213 app_label = "core"
1214
1215 # The forms to add and change user instances
1216 form = UserChangeForm
1217 add_form = UserCreationForm
1218
1219 # The fields to be used in displaying the User model.
1220 # These override the definitions on the base UserAdmin
1221 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001222 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001223 list_filter = ('site',)
Tony Mackffe6d8b2015-01-06 23:48:02 -05001224 inlines = [SlicePrivilegeInline,SitePrivilegeInline,UserDashboardViewInline]
Tony Mack3c01ff92015-01-10 23:08:10 -05001225 admin_inlines = [ControllerUserInline]
Scott Bakercbfb6002014-10-03 00:32:37 -07001226 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001227 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1228
Siobhan Tully53437282013-04-26 19:30:27 -04001229 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001230 ('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 -04001231 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001232 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001233 #('Important dates', {'fields': ('last_login',)}),
1234 )
1235 add_fieldsets = (
1236 (None, {
1237 'classes': ('wide',),
Scott Baker0a5633b2014-10-06 17:51:20 -07001238 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001239 ),
1240 )
Scott Baker40c00762014-08-21 16:55:59 -07001241 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001242 search_fields = ('email',)
1243 ordering = ('email',)
1244 filter_horizontal = ()
1245
Scott Baker3ca51f62014-05-23 12:05:11 -07001246 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001247
Scott Baker0a5633b2014-10-06 17:51:20 -07001248 @property
1249 def suit_form_tabs(self):
1250 if getattr(_thread_locals, "obj", None) is None:
1251 return []
1252 else:
Tony Mack3c01ff92015-01-10 23:08:10 -05001253 tabs = [('general','Login Details'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001254 ('contact','Contact Information'),
1255 ('sliceprivileges','Slice Privileges'),
1256 ('siteprivileges','Site Privileges'),
Tony Mack3c01ff92015-01-10 23:08:10 -05001257 ('dashboards','Dashboard Views')]
1258
1259 request=getattr(_thread_locals, "request", None)
1260 if request and request.user.is_admin:
1261 tabs.append( ('admin-only', 'Admin-Only') )
1262
1263 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001264
Tony Mackc2835a92013-05-28 09:18:49 -04001265 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1266 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001267 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001268
1269 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1270
Tony Mack5b061472014-02-04 07:57:10 -05001271 def queryset(self, request):
1272 return User.select_by_user(request.user)
1273
Scott Bakera6a0c772014-12-22 17:35:34 -08001274class ControllerDashboardViewInline(PlStackTabularInline):
1275 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001276 extra = 0
1277 fields = ["controller", "url"]
1278 suit_classes = 'suit-tab suit-tab-controllers'
1279
Scott Baker2c3cb642014-05-19 17:55:56 -07001280class DashboardViewAdmin(PlanetStackBaseAdmin):
1281 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001282 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001283 'classes': ['suit-tab suit-tab-general']})
1284 ]
Scott Baker40c00762014-08-21 16:55:59 -07001285 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001286 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001287
Scott Bakereef5a6b2014-12-19 16:41:12 -08001288 suit_form_tabs =(('general','Dashboard View Details'),
1289 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001290
Scott Baker0165fac2014-01-13 11:49:26 -08001291class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001292 model = ServiceResource
1293 extra = 0
1294
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001295class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001296 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1297 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001298 inlines = [ServiceResourceInline]
1299
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001300 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1301 user_readonly_inlines = []
1302
Scott Baker0165fac2014-01-13 11:49:26 -08001303class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001304 model = ReservedResource
1305 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001306 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001307
1308 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1309 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1310
1311 if db_field.name == 'resource':
1312 # restrict resources to those that the slice's service class allows
1313 if request._slice is not None:
1314 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1315 if len(field.queryset) > 0:
1316 field.initial = field.queryset.all()[0]
1317 else:
1318 field.queryset = field.queryset.none()
1319 elif db_field.name == 'sliver':
1320 # restrict slivers to those that belong to the slice
1321 if request._slice is not None:
1322 field.queryset = field.queryset.filter(slice = request._slice)
1323 else:
1324 field.queryset = field.queryset.none()
1325
1326 return field
1327
Tony Mack5b061472014-02-04 07:57:10 -05001328 def queryset(self, request):
1329 return ReservedResource.select_by_user(request.user)
1330
Scott Baker133c9212013-05-17 09:09:11 -07001331class ReservationChangeForm(forms.ModelForm):
1332 class Meta:
1333 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001334 widgets = {
1335 'slice' : LinkedSelect
1336 }
Scott Baker133c9212013-05-17 09:09:11 -07001337
1338class ReservationAddForm(forms.ModelForm):
1339 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1340 refresh = forms.CharField(widget=forms.HiddenInput())
1341
1342 class Media:
1343 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1344
1345 def clean_slice(self):
1346 slice = self.cleaned_data.get("slice")
1347 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1348 if len(x) == 0:
1349 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1350 return slice
1351
1352 class Meta:
1353 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001354 widgets = {
1355 'slice' : LinkedSelect
1356 }
1357
Scott Baker133c9212013-05-17 09:09:11 -07001358
1359class ReservationAddRefreshForm(ReservationAddForm):
1360 """ This form is displayed when the Reservation Form receives an update
1361 from the Slice dropdown onChange handler. It doesn't validate the
1362 data and doesn't save the data. This will cause the form to be
1363 redrawn.
1364 """
1365
Scott Baker8737e5f2013-05-17 09:35:32 -07001366 """ don't validate anything other than slice """
1367 dont_validate_fields = ("startTime", "duration")
1368
Scott Baker133c9212013-05-17 09:09:11 -07001369 def full_clean(self):
1370 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001371
1372 for fieldname in self.dont_validate_fields:
1373 if fieldname in self._errors:
1374 del self._errors[fieldname]
1375
Scott Baker133c9212013-05-17 09:09:11 -07001376 return result
1377
1378 """ don't save anything """
1379 def is_valid(self):
1380 return False
1381
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001382class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001383 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001384 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001385 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001386 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001387 form = ReservationAddForm
1388
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001389 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1390
1391 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001392 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001393
Scott Baker133c9212013-05-17 09:09:11 -07001394 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001395 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001396 request._refresh = False
1397 request._slice = None
1398 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001399 # "refresh" will be set to "1" if the form was submitted due to
1400 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001401 if request.POST.get("refresh","1") == "1":
1402 request._refresh = True
1403 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001404
1405 # Keep track of the slice that was selected, so the
1406 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001407 request._slice = request.POST.get("slice",None)
1408 if (request._slice is not None):
1409 request._slice = Slice.objects.get(id=request._slice)
1410
1411 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1412 return result
1413
Scott Bakeracd45142013-05-19 16:19:16 -07001414 def changelist_view(self, request, extra_context = None):
1415 timezone.activate(request.user.timezone)
1416 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1417
Scott Baker133c9212013-05-17 09:09:11 -07001418 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001419 request._obj_ = obj
1420 if obj is not None:
1421 # For changes, set request._slice to the slice already set in the
1422 # object.
1423 request._slice = obj.slice
1424 self.form = ReservationChangeForm
1425 else:
1426 if getattr(request, "_refresh", False):
1427 self.form = ReservationAddRefreshForm
1428 else:
1429 self.form = ReservationAddForm
1430 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1431
Scott Baker133c9212013-05-17 09:09:11 -07001432 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001433 if (obj is not None):
1434 # Prevent slice from being changed after the reservation has been
1435 # created.
1436 return ['slice']
1437 else:
Scott Baker133c9212013-05-17 09:09:11 -07001438 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001439
Tony Mack5b061472014-02-04 07:57:10 -05001440 def queryset(self, request):
1441 return Reservation.select_by_user(request.user)
1442
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001443class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001444 list_display = ("backend_status_icon", "name", )
1445 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001446 user_readonly_fields = ['name']
1447 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001448
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001449class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001450 list_display = ("backend_status_icon", "name", )
1451 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001452 user_readonly_fields = ['name']
1453 user_readonly_inlines = []
1454
Scott Baker0165fac2014-01-13 11:49:26 -08001455class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001456 model = Router.networks.through
1457 extra = 0
1458 verbose_name_plural = "Routers"
1459 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001460 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001461
Scott Bakerb27b62c2014-08-15 16:29:16 -07001462class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001463 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001464 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001465 verbose_name_plural = "Parameters"
1466 verbose_name = "Parameter"
1467 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001468 fields = ['backend_status_icon', 'parameter', 'value']
1469 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001470
Scott Baker0165fac2014-01-13 11:49:26 -08001471class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001472 fields = ['backend_status_icon', 'network','sliver','ip']
1473 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001474 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001475 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001476 extra = 0
1477 verbose_name_plural = "Slivers"
1478 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001479 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001480
Scott Baker0165fac2014-01-13 11:49:26 -08001481class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001482 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001483 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001484 extra = 0
1485 verbose_name_plural = "Slices"
1486 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001487 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001488 fields = ['backend_status_icon', 'network','slice']
1489 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001490
Tony Macka7dbd422015-01-05 22:48:11 -05001491class ControllerNetworkInline(PlStackTabularInline):
1492 model = ControllerNetwork
Scott Bakerfbb45862014-10-17 16:27:23 -07001493 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001494 verbose_name_plural = "Controller Networks"
1495 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001496 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001497 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001498 readonly_fields = ('backend_status_icon', )
1499
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001500class NetworkForm(forms.ModelForm):
1501 class Meta:
1502 model = Network
1503 widgets = {
1504 'topologyParameters': UploadTextareaWidget,
1505 'controllerParameters': UploadTextareaWidget,
1506 }
1507
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001508class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001509 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1510 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001511 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001512
Scott Bakerd7d2a392013-08-06 08:57:30 -07001513 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Macka7dbd422015-01-05 22:48:11 -05001514 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001515
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001516 form=NetworkForm
1517
Siobhan Tully2d95e482013-09-06 10:56:06 -04001518 fieldsets = [
Scott Baker0451fb62015-01-03 12:29:29 -08001519 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet'],
Scott Baker40248712014-11-17 16:04:45 -08001520 'classes':['suit-tab suit-tab-general']}),
Scott Baker0451fb62015-01-03 12:29:29 -08001521 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker40248712014-11-17 16:04:45 -08001522 'classes':['suit-tab suit-tab-sdn']}),
1523 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001524
Scott Baker40c00762014-08-21 16:55:59 -07001525 readonly_fields = ('backend_status_text', )
Scott Baker0451fb62015-01-03 12:29:29 -08001526 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 -04001527
Scott Bakerfbb45862014-10-17 16:27:23 -07001528 @property
1529 def suit_form_tabs(self):
1530 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001531 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001532 ('netparams', 'Parameters'),
1533 ('networkslivers','Slivers'),
1534 ('networkslices','Slices'),
1535 ('routers','Routers'),
1536 ]
1537
1538 request=getattr(_thread_locals, "request", None)
1539 if request and request.user.is_admin:
1540 tabs.append( ('admin-only', 'Admin-Only') )
1541
1542 return tabs
1543
1544
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001545class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001546 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001547 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001548 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001549 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001550 fieldsets = [
Scott Baker81fa17f2015-01-03 12:03:38 -08001551 (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
Scott Baker40248712014-11-17 16:04:45 -08001552 'classes':['suit-tab suit-tab-general']}),]
1553 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001554
Scott Baker37b47902014-09-02 14:37:41 -07001555class FlavorAdmin(PlanetStackBaseAdmin):
1556 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1557 list_display_links = ("backend_status_icon", "name")
1558 user_readonly_fields = ("name", "flavor")
1559 fields = ("name", "description", "flavor", "order", "default")
1560
Tony Mack31c2b8f2013-04-26 20:01:42 -04001561# register a signal that caches the user's credentials when they log in
1562def cache_credentials(sender, user, request, **kwds):
1563 auth = {'username': request.POST['username'],
1564 'password': request.POST['password']}
1565 request.session['auth'] = auth
1566user_logged_in.connect(cache_credentials)
1567
Scott Baker15cddfa2013-12-09 13:45:19 -08001568def dollar_field(fieldName, short_description):
1569 def newFunc(self, obj):
1570 try:
1571 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1572 except:
1573 x=getattr(obj, fieldName, 0.0)
1574 return x
1575 newFunc.short_description = short_description
1576 return newFunc
1577
1578def right_dollar_field(fieldName, short_description):
1579 def newFunc(self, obj):
1580 try:
1581 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1582 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1583 except:
1584 x=getattr(obj, fieldName, 0.0)
1585 return x
1586 newFunc.short_description = short_description
1587 newFunc.allow_tags = True
1588 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001589
Scott Baker0165fac2014-01-13 11:49:26 -08001590class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001591 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001592 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001593 verbose_name_plural = "Charges"
1594 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001595 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001596 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1597 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1598 can_delete = False
1599 max_num = 0
1600
1601 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001602
1603class InvoiceAdmin(admin.ModelAdmin):
1604 list_display = ("date", "account")
1605
1606 inlines = [InvoiceChargeInline]
1607
Scott Baker9cb88a22013-12-09 18:56:00 -08001608 fields = ["date", "account", "dollar_amount"]
1609 readonly_fields = ["date", "account", "dollar_amount"]
1610
1611 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001612
Scott Baker0165fac2014-01-13 11:49:26 -08001613class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001614 model = Invoice
1615 extra = 0
1616 verbose_name_plural = "Invoices"
1617 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001618 fields = ["date", "dollar_amount"]
1619 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001620 suit_classes = 'suit-tab suit-tab-accountinvoice'
1621 can_delete=False
1622 max_num=0
1623
1624 dollar_amount = right_dollar_field("amount", "Amount")
1625
Scott Baker0165fac2014-01-13 11:49:26 -08001626class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001627 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001628 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001629 verbose_name_plural = "Charges"
1630 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001631 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001632 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1633 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001634 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001635 can_delete=False
1636 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001637
1638 def queryset(self, request):
1639 qs = super(PendingChargeInline, self).queryset(request)
1640 qs = qs.filter(state="pending")
1641 return qs
1642
Scott Baker15cddfa2013-12-09 13:45:19 -08001643 dollar_amount = right_dollar_field("amount", "Amount")
1644
Scott Baker0165fac2014-01-13 11:49:26 -08001645class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001646 model=Payment
1647 extra = 1
1648 verbose_name_plural = "Payments"
1649 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001650 fields = ["date", "dollar_amount"]
1651 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001652 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001653 can_delete=False
1654 max_num=0
1655
1656 dollar_amount = right_dollar_field("amount", "Amount")
1657
Scott Baker43105042013-12-06 23:23:36 -08001658class AccountAdmin(admin.ModelAdmin):
1659 list_display = ("site", "balance_due")
1660
1661 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1662
1663 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001664 (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 -08001665
Scott Baker15cddfa2013-12-09 13:45:19 -08001666 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001667
1668 suit_form_tabs =(
1669 ('general','Account Details'),
1670 ('accountinvoice', 'Invoices'),
1671 ('accountpayments', 'Payments'),
1672 ('accountpendingcharges','Pending Charges'),
1673 )
1674
Scott Baker15cddfa2013-12-09 13:45:19 -08001675 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1676 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1677 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1678
Siobhan Tully53437282013-04-26 19:30:27 -04001679# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001680admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001681# ... and, since we're not using Django's builtin permissions,
1682# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001683#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001684
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001685#Do not show django evolution in the admin interface
1686from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001687#admin.site.unregister(Version)
1688#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001689
1690
1691# When debugging it is often easier to see all the classes, but for regular use
1692# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001693showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001694
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001695admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001696admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001697admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001698admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001699admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001700admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001701admin.site.register(Network, NetworkAdmin)
1702admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001703admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001704admin.site.register(Account, AccountAdmin)
1705admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001706
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001707if True:
1708 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1709 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001710 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001711 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001712 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001713 admin.site.register(SiteRole)
1714 admin.site.register(SliceRole)
1715 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001716 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001717 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1718 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001719 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001720 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001721 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001722 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001723