blob: e0664a5851f9cdf48962ced61de118dcb66b640b [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Scott Baker9f6b8ed2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Bakercbfb6002014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Scott Bakere2bbf7e2014-01-13 12:09:31 -080017from django.core.urlresolvers import reverse, NoReverseMatch
Scott Baker9f6b8ed2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
20from django.forms.utils import flatatt, to_current_timezone
Scott Baker3cde7372014-10-21 21:03:08 -070021from cgi import escape as html_escape
Tony Mack7130ac32013-03-22 21:58:00 -040022
Scott Baker36f50872014-08-21 13:01:25 -070023import django_evolution
Scott Baker0a5633b2014-10-06 17:51:20 -070024import threading
25
26# thread locals necessary to work around a django-suit issue
27_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070028
Scott Baker40c00762014-08-21 16:55:59 -070029def backend_icon(obj): # backend_status, enacted, updated):
30 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
31 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Bakerfbf06642014-09-09 10:38:15 -070032 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
Scott Baker40c00762014-08-21 16:55:59 -070033 else:
34 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
Scott Bakerfbf06642014-09-09 10:38:15 -070035 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
Scott Baker63d1a552014-08-21 15:19:07 -070036 else:
Scott Baker3cde7372014-10-21 21:03:08 -070037 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % html_escape(obj.backend_status, quote=True)
Scott Baker40c00762014-08-21 16:55:59 -070038
39def backend_text(obj):
40 icon = backend_icon(obj)
41 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Baker3cde7372014-10-21 21:03:08 -070042 return "%s %s" % (icon, "successfully enacted")
Scott Baker40c00762014-08-21 16:55:59 -070043 else:
Scott Baker3cde7372014-10-21 21:03:08 -070044 return "%s %s" % (icon, html_escape(obj.backend_status, quote=True))
Scott Baker63d1a552014-08-21 15:19:07 -070045
Scott Baker9f6b8ed2014-11-17 23:44:03 -080046class UploadTextareaWidget(AdminTextareaWidget):
47 def render(self, name, value, attrs=None):
48 if value is None:
49 value = ''
50 final_attrs = self.build_attrs(attrs, name=name)
51 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
52 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
53 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
54 flatatt(final_attrs),
55 force_text(value))
56
Scott Baker36f50872014-08-21 13:01:25 -070057class PlainTextWidget(forms.HiddenInput):
58 input_type = 'hidden'
59
60 def render(self, name, value, attrs=None):
61 if value is None:
62 value = ''
63 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
64
Scott Baker86c83ab2014-10-03 13:10:47 -070065class PermissionCheckingAdminMixin(object):
Scott Bakercbfb6002014-10-03 00:32:37 -070066 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050067
68 def has_add_permission(self, request, obj=None):
69 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070070
Siobhan Tullycf04fb62014-01-11 11:25:57 -050071 def has_delete_permission(self, request, obj=None):
72 return (not self.__user_is_readonly(request))
73
74 def save_model(self, request, obj, form, change):
75 if self.__user_is_readonly(request):
Scott Bakercbfb6002014-10-03 00:32:37 -070076 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050077 raise PermissionDenied
Scott Bakercbfb6002014-10-03 00:32:37 -070078
79 obj.caller = request.user
80 # update openstack connection to use this site/tenant
81 obj.save_by_user(request.user)
82
83 def delete_model(self, request, obj):
84 obj.delete_by_user(request.user)
85
86 def save_formset(self, request, form, formset, change):
87 instances = formset.save(commit=False)
88 for instance in instances:
89 instance.save_by_user(request.user)
90
91 # BUG in django 1.7? Objects are not deleted by formset.save if
92 # commit is False. So let's delete them ourselves.
93 #
94 # code from forms/models.py save_existing_objects()
95 try:
96 forms_to_delete = formset.deleted_forms
97 except AttributeError:
98 forms_to_delete = []
99 if formset.initial_forms:
100 for form in formset.initial_forms:
101 obj = form.instance
102 if form in forms_to_delete:
103 if obj.pk is None:
104 continue
105 formset.deleted_objects.append(obj)
106 obj.delete()
107
108 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500109
110 def get_actions(self,request):
Scott Baker86c83ab2014-10-03 13:10:47 -0700111 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500112
113 if self.__user_is_readonly(request):
114 if 'delete_selected' in actions:
115 del actions['delete_selected']
116
117 return actions
118
119 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500120 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -0700121 if not hasattr(self, "readonly_save"):
122 # save the original readonly fields
123 self.readonly_save = self.readonly_fields
124 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -0700125 if hasattr(self, "user_readonly_fields"):
126 self.readonly_fields=self.user_readonly_fields
127 if hasattr(self, "user_readonly_inlines"):
128 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -0700129 else:
130 if hasattr(self, "readonly_save"):
131 # restore the original readonly fields
132 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -0700133 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700134 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500135
136 try:
Scott Baker86c83ab2014-10-03 13:10:47 -0700137 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500138 except PermissionDenied:
139 pass
140 if request.method == 'POST':
141 raise PermissionDenied
142 request.readonly = True
Scott Baker86c83ab2014-10-03 13:10:47 -0700143 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500144
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500145 def __user_is_readonly(self, request):
146 return request.user.isReadOnlyUser()
147
Scott Baker40c00762014-08-21 16:55:59 -0700148 def backend_status_text(self, obj):
149 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700150
Scott Baker63d1a552014-08-21 15:19:07 -0700151 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700152 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700153 backend_status_icon.short_description = ""
154
Scott Baker24ded6a2014-11-05 09:05:38 -0800155 def get_form(self, request, obj=None, **kwargs):
Scott Baker5c432692014-10-16 00:57:55 -0700156 # Save obj and request in thread-local storage, so suit_form_tabs can
157 # use it to determine whether we're in edit or add mode, and can
158 # determine whether the user is an admin.
159 _thread_locals.request = request
160 _thread_locals.obj = obj
Scott Baker24ded6a2014-11-05 09:05:38 -0800161 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker5c432692014-10-16 00:57:55 -0700162
163 def get_inline_instances(self, request, obj=None):
164 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
165
166 # inlines that should only be shown to an admin user
167 if request.user.is_admin:
168 for inline_class in getattr(self, "admin_inlines", []):
169 inlines.append(inline_class(self.model, self.admin_site))
170
171 return inlines
172
Scott Baker86c83ab2014-10-03 13:10:47 -0700173class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
174 # Note: Make sure PermissionCheckingAdminMixin is listed before
175 # admin.ModelAdmin in the class declaration.
176
Scott Bakercbfb6002014-10-03 00:32:37 -0700177 pass
178
179class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
180 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700181
Scott Bakere8859f92014-05-23 12:42:40 -0700182class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400183 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700184 if not super(SingletonAdmin, self).has_add_permission(request):
185 return False
186
Siobhan Tullyce652d02013-10-08 21:52:35 -0400187 num_objects = self.model.objects.count()
188 if num_objects >= 1:
189 return False
190 else:
191 return True
192
Siobhan Tullyd3515752013-06-21 16:34:53 -0400193class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800194 def __init__(self, *args, **kwargs):
195 super(PlStackTabularInline, self).__init__(*args, **kwargs)
196
197 # InlineModelAdmin as no get_fields() method, so in order to add
198 # the selflink field, we override __init__ to modify self.fields and
199 # self.readonly_fields.
200
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800201 self.setup_selflink()
202
Scott Baker874936e2014-01-13 18:15:34 -0800203 def get_change_url(self, model, id):
204 """ Get the URL to a change form in the admin for this model """
205 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800206 try:
Scott Baker874936e2014-01-13 18:15:34 -0800207 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800208 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800209 return None
210
211 return url
212
213 def setup_selflink(self):
214 if hasattr(self, "selflink_fieldname"):
215 """ self.selflink_model can be defined to punch through a relation
216 to its target object. For example, in SliceNetworkInline, set
217 selflink_model = "network", and the URL will lead to the Network
218 object instead of trying to bring up a change view of the
219 SliceNetwork object.
220 """
221 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
222 else:
223 self.selflink_model = self.model
224
225 url = self.get_change_url(self.selflink_model, 0)
226
227 # We don't have an admin for this object, so don't create the
228 # selflink.
229 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800230 return
231
Scott Baker874936e2014-01-13 18:15:34 -0800232 # Since we need to add "selflink" to the field list, we need to create
233 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800234 if (self.fields is None):
235 self.fields = []
236 for f in self.model._meta.fields:
237 if f.editable and f.name != "id":
238 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800239
Scott Baker874936e2014-01-13 18:15:34 -0800240 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800241
Scott Baker874936e2014-01-13 18:15:34 -0800242 if self.readonly_fields is None:
243 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800244
Scott Baker874936e2014-01-13 18:15:34 -0800245 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800246
247 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800248 if hasattr(self, "selflink_fieldname"):
249 obj = getattr(obj, self.selflink_fieldname)
250
Scott Baker86568322014-01-12 16:53:31 -0800251 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800252 url = self.get_change_url(self.selflink_model, obj.id)
253 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800254 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800255 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800256
257 selflink.allow_tags = True
258 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400259
Scott Bakerb27b62c2014-08-15 16:29:16 -0700260 def has_add_permission(self, request):
261 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500262
263 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700264 readonly_fields = list(self.readonly_fields)[:]
265 if request.user.isReadOnlyUser():
266 for field in self.fields:
267 if not field in readonly_fields:
268 readonly_fields.append(field)
269 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500270
Scott Baker40c00762014-08-21 16:55:59 -0700271 def backend_status_icon(self, obj):
272 return mark_safe(backend_icon(obj))
273 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700274
Scott Bakerb27b62c2014-08-15 16:29:16 -0700275class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500276 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700277 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500278
Scott Bakerb27b62c2014-08-15 16:29:16 -0700279 def get_readonly_fields(self, request, obj=None):
280 readonly_fields = list(self.readonly_fields)[:]
281 if request.user.isReadOnlyUser():
282 for field in self.fields:
283 if not field in readonly_fields:
284 readonly_fields.append(field)
285 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500286
Scott Baker40c00762014-08-21 16:55:59 -0700287 def backend_status_icon(self, obj):
288 return mark_safe(backend_icon(obj))
289 backend_status_icon.short_description = ""
290
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400291class ReservationInline(PlStackTabularInline):
292 model = Reservation
293 extra = 0
294 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700295
Tony Mack5b061472014-02-04 07:57:10 -0500296 def queryset(self, request):
297 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400298
Scott Bakerb27b62c2014-08-15 16:29:16 -0700299class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400300 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400301 extra = 0
302 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500303 fields = ['service', 'name', 'value']
304
305 def queryset(self, request):
306 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400307
Scott Baker74d8e622013-07-29 16:04:22 -0700308class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400309 """ This is a callable that looks up a network name in a sliver and returns
310 the ip address for that network.
311 """
312
Scott Baker434ca7e2014-08-15 12:29:20 -0700313 byNetworkName = {} # class variable
314
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400315 def __init__(self, name):
316 self.short_description = name
317 self.__name__ = name
318 self.network_name = name
319
320 def __call__(self, obj):
321 if obj is not None:
322 for nbs in obj.networksliver_set.all():
323 if (nbs.network.name == self.network_name):
324 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700325 return ""
326
327 def __str__(self):
328 return self.network_name
329
Scott Baker434ca7e2014-08-15 12:29:20 -0700330 @staticmethod
331 def get(network_name):
332 """ We want to make sure we alwars return the same NetworkLookerUpper
333 because sometimes django will cause them to be instantiated multiple
334 times (and we don't want different ones in form.fields vs
335 SliverInline.readonly_fields).
336 """
337 if network_name not in NetworkLookerUpper.byNetworkName:
338 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
339 return NetworkLookerUpper.byNetworkName[network_name]
340
Siobhan Tullyd3515752013-06-21 16:34:53 -0400341class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400342 model = Sliver
Tony Mackbf6aa302014-12-26 13:38:02 -0500343 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400344 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700345 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400346 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700347
Tony Mack5b061472014-02-04 07:57:10 -0500348 def queryset(self, request):
349 return Sliver.select_by_user(request.user)
350
Scott Bakerb24cc932014-06-09 10:51:16 -0700351 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackbf6aa302014-12-26 13:38:02 -0500352 if db_field.name == 'deployment':
Scott Baker3b678742014-06-09 13:11:54 -0700353 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker7a61dc42014-09-02 17:08:20 -0700354 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Tony Mackbf6aa302014-12-26 13:38:02 -0500355 if db_field.name == 'flavor':
Scott Baker32481312014-09-08 12:14:14 -0700356 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700357
358 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700359
360 return field
361
Siobhan Tullyd3515752013-06-21 16:34:53 -0400362class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400363 model = Site
364 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400365 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400366
Tony Mack5b061472014-02-04 07:57:10 -0500367 def queryset(self, request):
368 return Site.select_by_user(request.user)
369
Siobhan Tullyd3515752013-06-21 16:34:53 -0400370class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400371 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700372 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
373 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400374 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400375 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400376
Tony Mack5b061472014-02-04 07:57:10 -0500377 def queryset(self, request):
378 return User.select_by_user(request.user)
379
Siobhan Tullyd3515752013-06-21 16:34:53 -0400380class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400381 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700382 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
383 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400384 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400385 suit_classes = 'suit-tab suit-tab-slices'
386
Tony Mack5b061472014-02-04 07:57:10 -0500387 def queryset(self, request):
388 return Slice.select_by_user(request.user)
389
Siobhan Tullyd3515752013-06-21 16:34:53 -0400390class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400391 model = Node
392 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400393 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack93d1b032014-12-08 16:43:02 -0500394 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700395 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400396
Tony Mack93d1b032014-12-08 16:43:02 -0500397class DeploymentPrivilegeInline(PlStackTabularInline):
398 model = DeploymentPrivilege
399 extra = 0
400 suit_classes = 'suit-tab suit-tab-admin-only'
401 fields = ['backend_status_icon', 'user','role','deployment']
402 readonly_fields = ('backend_status_icon', )
403
404 def queryset(self, request):
405 return DeploymentPrivilege.select_by_user(request.user)
406
Tony Mack336e0f92014-11-30 15:53:08 -0500407class ControllerPrivilegeInline(PlStackTabularInline):
408 model = ControllerPrivilege
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400409 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500410 suit_classes = 'suit-tab suit-tab-admin-only'
411 fields = ['backend_status_icon', 'user','role','controller']
Scott Baker40c00762014-08-21 16:55:59 -0700412 readonly_fields = ('backend_status_icon', )
Tony Mack5b061472014-02-04 07:57:10 -0500413
414 def queryset(self, request):
Tony Mack336e0f92014-11-30 15:53:08 -0500415 return ControllerPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400416
Siobhan Tullyd3515752013-06-21 16:34:53 -0400417class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400418 model = SitePrivilege
419 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400420 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700421 fields = ['backend_status_icon', 'user','site', 'role']
422 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400423
Tony Mackc2835a92013-05-28 09:18:49 -0400424 def formfield_for_foreignkey(self, db_field, request, **kwargs):
425 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500426 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400427
428 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500429 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400430 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
431
Tony Mack5b061472014-02-04 07:57:10 -0500432 def queryset(self, request):
433 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400434
Sapan Bhatia39e85582014-11-19 15:07:36 -0500435class SiteDeploymentsInline(PlStackTabularInline):
436 model = SiteDeployments
Tony Macke4be32f2014-03-11 20:45:25 -0400437 extra = 0
438 suit_classes = 'suit-tab suit-tab-deployments'
Tony Mack528d4222014-12-05 17:13:08 -0500439 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700440 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400441
442 def formfield_for_foreignkey(self, db_field, request, **kwargs):
443 if db_field.name == 'site':
444 kwargs['queryset'] = Site.select_by_user(request.user)
445
446 if db_field.name == 'deployment':
447 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mack528d4222014-12-05 17:13:08 -0500448
449 if db_field.name == 'controller':
450 kwargs['queryset'] = Controller.select_by_user(request.user)
451
Sapan Bhatia39e85582014-11-19 15:07:36 -0500452 return super(SiteDeploymentsInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400453
454 def queryset(self, request):
Sapan Bhatia39e85582014-11-19 15:07:36 -0500455 return SiteDeployments.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400456
457
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400458class SlicePrivilegeInline(PlStackTabularInline):
459 model = SlicePrivilege
460 suit_classes = 'suit-tab suit-tab-sliceprivileges'
461 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700462 fields = ('backend_status_icon', 'user', 'slice', 'role')
463 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400464
Tony Mackc2835a92013-05-28 09:18:49 -0400465 def formfield_for_foreignkey(self, db_field, request, **kwargs):
466 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700467 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400468 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700469 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400470
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400471 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400472
Tony Mack5b061472014-02-04 07:57:10 -0500473 def queryset(self, request):
474 return SlicePrivilege.select_by_user(request.user)
475
Scott Bakera0015eb2013-08-14 17:28:14 -0700476class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700477 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800478 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700479 extra = 0
480 verbose_name = "Network Connection"
481 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400482 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700483 fields = ['backend_status_icon', 'network']
484 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700485
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500486class ImageDeploymentsInline(PlStackTabularInline):
487 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700488 extra = 0
489 verbose_name = "Image Deployments"
490 verbose_name_plural = "Image Deployments"
491 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500492 fields = ['backend_status_icon', 'image', 'deployment']
493 readonly_fields = ['backend_status_icon']
494
495class ControllerImagesInline(PlStackTabularInline):
496 model = ControllerImages
497 extra = 0
498 verbose_name = "Controller Images"
499 verbose_name_plural = "Controller Images"
500 suit_classes = 'suit-tab suit-tab-admin-only'
501 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700502 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700503
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400504class SliceRoleAdmin(PlanetStackBaseAdmin):
505 model = SliceRole
506 pass
507
508class SiteRoleAdmin(PlanetStackBaseAdmin):
509 model = SiteRole
510 pass
511
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400512class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400513 sites = forms.ModelMultipleChoiceField(
514 queryset=Site.objects.all(),
515 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700516 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400517 widget=FilteredSelectMultiple(
518 verbose_name=('Sites'), is_stacked=False
519 )
520 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700521 images = forms.ModelMultipleChoiceField(
522 queryset=Image.objects.all(),
523 required=False,
524 help_text="Select which images should be deployed on this deployment",
525 widget=FilteredSelectMultiple(
526 verbose_name=('Images'), is_stacked=False
527 )
528 )
Scott Baker37b47902014-09-02 14:37:41 -0700529 flavors = forms.ModelMultipleChoiceField(
530 queryset=Flavor.objects.all(),
531 required=False,
532 help_text="Select which flavors should be usable on this deployment",
533 widget=FilteredSelectMultiple(
534 verbose_name=('Flavors'), is_stacked=False
535 )
536 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400537 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400538 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700539 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400540
Siobhan Tully320b4622014-01-17 15:11:14 -0500541 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700542 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500543 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
544
Scott Baker5380c522014-06-06 14:49:43 -0700545 self.fields['accessControl'].initial = "allow site " + request.user.site.name
546
Siobhan Tully320b4622014-01-17 15:11:14 -0500547 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800548 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments.all()]
549 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700550 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700551
552 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
553 """ helper function for handling m2m relations from the MultipleChoiceField
554
555 this_obj: the source object we want to link from
556
557 selected_objs: a list of destination objects we want to link to
558
559 all_relations: the full set of relations involving this_obj, including ones we don't want
560
561 relation_class: the class that implements the relation from source to dest
562
563 local_attrname: field name representing this_obj in relation_class
564
565 foreign_attrname: field name representing selected_objs in relation_class
566
567 This function will remove all newobjclass relations from this_obj
568 that are not contained in selected_objs, and add any relations that
569 are in selected_objs but don't exist in the data model yet.
570 """
571
572 existing_dest_objs = []
573 for relation in list(all_relations):
574 if getattr(relation, foreign_attrname) not in selected_objs:
575 #print "deleting site", sdp.site
576 relation.delete()
577 else:
578 existing_dest_objs.append(getattr(relation, foreign_attrname))
579
580 for dest_obj in selected_objs:
581 if dest_obj not in existing_dest_objs:
582 #print "adding site", site
583 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
584 relation = relation_class(**kwargs)
585 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500586
587 def save(self, commit=True):
588 deployment = super(DeploymentAdminForm, self).save(commit=False)
589
590 if commit:
591 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700592 # this has to be done after save() if/when a deployment is first created
593 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500594
595 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700596 # save_m2m() doesn't seem to work with 'through' relations. So we
597 # create/destroy the through models ourselves. There has to be
598 # a better way...
599
Sapan Bhatia39e85582014-11-19 15:07:36 -0500600 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments.all(), SiteDeployments, "deployment", "site")
Tony Mack592aa952014-12-15 11:45:02 -0500601 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
602 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
603 # so well handle that manually here
604 for flavor in deployment.flavors.all():
605 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mack11f4d202014-12-15 12:37:59 -0500606 deployment.flavors.remove(flavor)
Tony Mack592aa952014-12-15 11:45:02 -0500607 for flavor in self.cleaned_data['flavors']:
608 if flavor not in deployment.flavors.all():
609 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700610
Scott Baker37b47902014-09-02 14:37:41 -0700611 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500612
613 return deployment
614
Scott Bakerff5e0f32014-05-22 14:40:27 -0700615class DeploymentAdminROForm(DeploymentAdminForm):
616 def save(self, commit=True):
617 raise PermissionDenied
618
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500619class SiteAssocInline(PlStackTabularInline):
620 model = Site.deployments.through
621 extra = 0
622 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400623
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400624class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500625 model = Deployment
Tony Mack93d1b032014-12-08 16:43:02 -0500626 fieldList = ['backend_status_text', 'name', 'sites', 'images', 'flavors', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500627 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Tony Mack93d1b032014-12-08 16:43:02 -0500628 # node no longer directly connected to deployment
629 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
630 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700631 list_display = ['backend_status_icon', 'name']
632 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700633 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500634
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500635 user_readonly_fields = ['name']
636
Tony Mack93d1b032014-12-08 16:43:02 -0500637 # nodes no longer direclty connected to deployments
638 #suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
639 suit_form_tabs =(('sites','Deployment Details'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500640
Scott Bakerff5e0f32014-05-22 14:40:27 -0700641 def get_form(self, request, obj=None, **kwargs):
642 if request.user.isReadOnlyUser():
643 kwargs["form"] = DeploymentAdminROForm
644 else:
645 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700646 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
647
648 # from stackexchange: pass the request object into the form
649
650 class AdminFormMetaClass(adminForm):
651 def __new__(cls, *args, **kwargs):
652 kwargs['request'] = request
653 return adminForm(*args, **kwargs)
654
655 return AdminFormMetaClass
656
Tony Mack528d4222014-12-05 17:13:08 -0500657class ControllerAdminForm(forms.ModelForm):
658 site_deployments = forms.ModelMultipleChoiceField(
Tony Mack93d1b032014-12-08 16:43:02 -0500659 queryset=SiteDeployments.objects.all(),
Tony Mack528d4222014-12-05 17:13:08 -0500660 required=False,
661 help_text="Select which sites deployments are managed by this controller",
662 widget=FilteredSelectMultiple(
663 verbose_name=('Site Deployments'), is_stacked=False
664 )
665 )
666
667 class Meta:
668 model = Controller
669
Tony Mack93d1b032014-12-08 16:43:02 -0500670 def __init__(self, *args, **kwargs):
Tony Mack528d4222014-12-05 17:13:08 -0500671 request = kwargs.pop('request', None)
672 super(ControllerAdminForm, self).__init__(*args, **kwargs)
673
674 if self.instance and self.instance.pk:
675 self.fields['site_deployments'].initial = [x.site_deployment for x in self.instance.controllersitedeployments.all()]
676
Tony Mack93d1b032014-12-08 16:43:02 -0500677 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
Tony Mack528d4222014-12-05 17:13:08 -0500678 """ helper function for handling m2m relations from the MultipleChoiceField
679 this_obj: the source object we want to link from
680 selected_objs: a list of destination objects we want to link to
681 all_relations: the full set of relations involving this_obj, including ones we don't want
682 relation_class: the class that implements the relation from source to dest
683 local_attrname: field name representing this_obj in relation_class
684 foreign_attrname: field name representing selected_objs in relation_class
685 This function will remove all newobjclass relations from this_obj
686 that are not contained in selected_objs, and add any relations that
687 are in selected_objs but don't exist in the data model yet.
688 """
689 existing_dest_objs = []
690 for relation in list(all_relations):
691 if getattr(relation, foreign_attrname) not in selected_objs:
692 #print "deleting site", sdp.site
693 relation.delete()
694 else:
695 existing_dest_objs.append(getattr(relation, foreign_attrname))
696
697 for dest_obj in selected_objs:
698 if dest_obj not in existing_dest_objs:
699 #print "adding site", site
700 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
701 relation = relation_class(**kwargs)
702 relation.save()
703
Tony Mack93d1b032014-12-08 16:43:02 -0500704 def save(self, commit=True):
705 controller = super(ControllerAdminForm, self).save(commit=False)
706 if commit:
707 controller.save()
Tony Mack528d4222014-12-05 17:13:08 -0500708
Tony Mack93d1b032014-12-08 16:43:02 -0500709 if controller.pk:
710 # save_m2m() doesn't seem to work with 'through' relations. So we
711 # create/destroy the through models ourselves. There has to be
712 # a better way...
Tony Mack1e828282015-01-03 17:40:42 -0500713 #self.manipulate_m2m_objs(controller, self.cleaned_data['site_deployments'], controller.controllersitedeployments.all(), ControllerSiteDeployments, "controller", "site_deployment")
714 pass
715
Tony Mack93d1b032014-12-08 16:43:02 -0500716 self.save_m2m()
Tony Mack528d4222014-12-05 17:13:08 -0500717
Tony Mack93d1b032014-12-08 16:43:02 -0500718 return controller
Tony Mack528d4222014-12-05 17:13:08 -0500719
720class ControllerAdmin(PlanetStackBaseAdmin):
721 model = Controller
Tony Mack93d1b032014-12-08 16:43:02 -0500722 fieldList = ['name', 'version', 'backend_type', 'auth_url', 'admin_user', 'admin_tenant','admin_password']
723 #fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack1e828282015-01-03 17:40:42 -0500724 inlines = [] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500725 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
726 list_display_links = ('backend_status_icon', 'name', )
727 readonly_fields = ('backend_status_text',)
728
729 user_readonly_fields = []
730
731 def get_form(self, request, obj=None, **kwargs):
Tony Mack93d1b032014-12-08 16:43:02 -0500732 print self.fieldsets
Tony Mack528d4222014-12-05 17:13:08 -0500733 if request.user.isReadOnlyUser():
734 kwargs["form"] = ControllerAdminROForm
735 else:
736 kwargs["form"] = ControllerAdminForm
Tony Mack93d1b032014-12-08 16:43:02 -0500737 adminForm = super(ControllerAdmin,self).get_form(request, obj, **kwargs)
Tony Mack528d4222014-12-05 17:13:08 -0500738
739 # from stackexchange: pass the request object into the form
740
741 class AdminFormMetaClass(adminForm):
742 def __new__(cls, *args, **kwargs):
743 kwargs['request'] = request
744 return adminForm(*args, **kwargs)
745
746 return AdminFormMetaClass
747
Siobhan Tullyce652d02013-10-08 21:52:35 -0400748class ServiceAttrAsTabInline(PlStackTabularInline):
749 model = ServiceAttribute
750 fields = ['name','value']
751 extra = 0
752 suit_classes = 'suit-tab suit-tab-serviceattrs'
753
Siobhan Tullyce652d02013-10-08 21:52:35 -0400754class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700755 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
756 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700757 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500758 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
759 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700760 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500761
762 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500763
764 suit_form_tabs =(('general', 'Service Details'),
765 ('slices','Slices'),
766 ('serviceattrs','Additional Attributes'),
767 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400768
Tony Mack0553f282013-06-10 22:54:50 -0400769class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700770 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400771 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500772 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400773 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400774 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400775 suit_form_tabs =(('general', 'Site Details'),
776 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400777 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400778 ('deployments','Deployments'),
779 ('slices','Slices'),
Tony Mack93d1b032014-12-08 16:43:02 -0500780 #('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400781 ('tags','Tags'),
782 )
Scott Baker40c00762014-08-21 16:55:59 -0700783 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500784
785 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500786
Scott Baker63d1a552014-08-21 15:19:07 -0700787 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
788 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400789 filter_horizontal = ('deployments',)
Tony Mack93d1b032014-12-08 16:43:02 -0500790 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteDeploymentsInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400791 search_fields = ['name']
792
Tony Mack04062832013-05-10 08:22:44 -0400793 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500794 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400795
Tony Mack5cd13202013-05-01 21:48:38 -0400796 def get_formsets(self, request, obj=None):
797 for inline in self.get_inline_instances(request, obj):
798 # hide MyInline in the add view
799 if obj is None:
800 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400801 if isinstance(inline, SliverInline):
802 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400803 yield inline.get_formset(request, obj)
804
Scott Baker545db2a2013-12-09 18:44:43 -0800805 def accountLink(self, obj):
806 link_obj = obj.accounts.all()
807 if link_obj:
808 reverse_path = "admin:core_account_change"
809 url = reverse(reverse_path, args =(link_obj[0].id,))
810 return "<a href='%s'>%s</a>" % (url, "view billing details")
811 else:
812 return "no billing data for this site"
813 accountLink.allow_tags = True
814 accountLink.short_description = "Billing"
815
Tony Mack332ee1d2014-02-04 15:33:45 -0500816 def save_model(self, request, obj, form, change):
817 # update openstack connection to use this site/tenant
818 obj.save_by_user(request.user)
819
820 def delete_model(self, request, obj):
821 obj.delete_by_user(request.user)
822
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500823
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400824class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700825 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400826 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500827 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400828 ]
Scott Baker40c00762014-08-21 16:55:59 -0700829 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700830 list_display = ('backend_status_icon', 'user', 'site', 'role')
831 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500832 user_readonly_fields = fieldList
833 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400834
Tony Mackc2835a92013-05-28 09:18:49 -0400835 def formfield_for_foreignkey(self, db_field, request, **kwargs):
836 if db_field.name == 'site':
837 if not request.user.is_admin:
838 # only show sites where user is an admin or pi
839 sites = set()
840 for site_privilege in SitePrivilege.objects.filer(user=request.user):
841 if site_privilege.role.role_type in ['admin', 'pi']:
842 sites.add(site_privilege.site)
843 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
844
845 if db_field.name == 'user':
846 if not request.user.is_admin:
847 # only show users from sites where caller has admin or pi role
848 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
849 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
850 sites = [site_privilege.site for site_privilege in site_privileges]
851 site_privileges = SitePrivilege.objects.filter(site__in=sites)
852 emails = [site_privilege.user.email for site_privilege in site_privileges]
853 users = User.objects.filter(email__in=emails)
854 kwargs['queryset'] = users
855
856 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
857
Tony Mack04062832013-05-10 08:22:44 -0400858 def queryset(self, request):
859 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400860 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400861 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500862 #if not request.user.is_admin:
863 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
864 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
865 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
866 # sites = Site.objects.filter(login_base__in=login_bases)
867 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400868 return qs
869
Siobhan Tullyce652d02013-10-08 21:52:35 -0400870class SliceForm(forms.ModelForm):
871 class Meta:
872 model = Slice
873 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700874 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400875 }
876
Tony Mack2cbd3802014-09-29 16:10:52 -0400877 def clean(self):
878 cleaned_data = super(SliceForm, self).clean()
879 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -0700880 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -0400881 slice_id = self.instance.id
882 if not site and slice_id:
883 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -0700884 if (not isinstance(site,Site)):
885 # previous code indicates 'site' could be a site_id and not a site?
886 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -0400887 if not name.startswith(site.login_base):
888 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
889 return cleaned_data
890
Tony Mack336e0f92014-11-30 15:53:08 -0500891class ControllerSlicesInline(PlStackTabularInline):
892 model = ControllerSlices
Scott Bakerc4efdc72014-10-15 16:54:04 -0700893 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500894 verbose_name = "Controller Slices"
895 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -0700896 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -0500897 fields = ['backend_status_icon', 'controller', 'tenant_id']
Scott Bakerc4efdc72014-10-15 16:54:04 -0700898 readonly_fields = ('backend_status_icon', )
899
Tony Mack2bd5b412013-06-11 21:05:06 -0400900class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400901 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400902 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500903 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700904 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400905 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
906 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400907 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Mack336e0f92014-11-30 15:53:08 -0500908 admin_inlines = [ControllerSlicesInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400909
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500910 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400911
Scott Bakerc4efdc72014-10-15 16:54:04 -0700912 @property
913 def suit_form_tabs(self):
914 tabs =[('general', 'Slice Details'),
915 ('slicenetworks','Networks'),
916 ('sliceprivileges','Privileges'),
917 ('slivers','Slivers'),
918 ('tags','Tags'),
919 ('reservations','Reservations'),
920 ]
921
922 request=getattr(_thread_locals, "request", None)
923 if request and request.user.is_admin:
924 tabs.append( ('admin-only', 'Admin-Only') )
925
926 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -0400927
928 def add_view(self, request, form_url='', extra_context=None):
929 # revert to default read-only fields
930 self.readonly_fields = ('backend_status_text',)
931 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
932
933 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -0400934 # cannot change the site of an existing slice so make the site field read only
935 if object_id:
936 self.readonly_fields = ('backend_status_text','site')
937 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400938
Scott Baker510fdbb2014-08-05 17:19:24 -0700939 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700940 deployment_nodes = []
941 for node in Node.objects.all():
Sapan Bhatia48208cd2014-12-22 01:37:26 -0500942 deployment_nodes.append( (node.site_deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -0700943
Scott Baker7a61dc42014-09-02 17:08:20 -0700944 deployment_flavors = []
945 for flavor in Flavor.objects.all():
946 for deployment in flavor.deployments.all():
947 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
948
Tony Mack93d1b032014-12-08 16:43:02 -0500949 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -0700950 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -0500951 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -0800952 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -0700953
Tony Mackec23b992014-09-02 21:18:45 -0400954 site_login_bases = []
955 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -0700956 site_login_bases.append((site.id, site.login_base))
957
Scott Baker510fdbb2014-08-05 17:19:24 -0700958 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700959 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -0700960 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400961 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700962 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
963
Tony Mackc2835a92013-05-28 09:18:49 -0400964 def formfield_for_foreignkey(self, db_field, request, **kwargs):
965 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500966 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400967 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 -0700968
Tony Mackc2835a92013-05-28 09:18:49 -0400969 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
970
Tony Mack04062832013-05-10 08:22:44 -0400971 def queryset(self, request):
972 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500973 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400974
Tony Mack79748612013-05-01 14:52:03 -0400975 def get_formsets(self, request, obj=None):
976 for inline in self.get_inline_instances(request, obj):
977 # hide MyInline in the add view
978 if obj is None:
979 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400980 if isinstance(inline, SliverInline):
981 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400982 yield inline.get_formset(request, obj)
983
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400984class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400985 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700986 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400987 ]
Scott Baker40c00762014-08-21 16:55:59 -0700988 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700989 list_display = ('backend_status_icon', 'user', 'slice', 'role')
990 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400991
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500992 user_readonly_fields = ['user', 'slice', 'role']
993 user_readonly_inlines = []
994
Tony Mackc2835a92013-05-28 09:18:49 -0400995 def formfield_for_foreignkey(self, db_field, request, **kwargs):
996 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500997 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400998
999 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001000 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001001
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001002 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001003
Tony Mack04062832013-05-10 08:22:44 -04001004 def queryset(self, request):
1005 # admins can see all memberships. Users can only see memberships of
1006 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001007 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001008
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001009 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001010 # update openstack connection to use this site/tenant
1011 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001012 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001013 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001014 obj.save()
1015
1016 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001017 # update openstack connection to use this site/tenant
1018 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001019 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001020 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001021 obj.delete()
1022
Siobhan Tully567e3e62013-06-21 18:03:16 -04001023
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001024class ImageAdmin(PlanetStackBaseAdmin):
1025
Scott Baker36f50872014-08-21 13:01:25 -07001026 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001027 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001028 'classes': ['suit-tab suit-tab-general']})
1029 ]
Scott Baker40c00762014-08-21 16:55:59 -07001030 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001031
Tony Mack336e0f92014-11-30 15:53:08 -05001032 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001033
Tony Mack336e0f92014-11-30 15:53:08 -05001034 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001035
Tony Mack32e1ce32014-05-07 13:29:41 -04001036 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001037
Scott Baker63d1a552014-08-21 15:19:07 -07001038 list_display = ['backend_status_icon', 'name']
1039 list_display_links = ('backend_status_icon', 'name', )
1040
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001041class NodeForm(forms.ModelForm):
1042 class Meta:
1043 widgets = {
1044 'site': LinkedSelect,
1045 'deployment': LinkedSelect
1046 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001047
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001048class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001049 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -05001050 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001051 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -05001052 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001053
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001054 inlines = [TagInline,SliverInline]
Tony Mack93d1b032014-12-08 16:43:02 -05001055 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001056 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001057
Tony Mack93d1b032014-12-08 16:43:02 -05001058 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001059 user_readonly_inlines = [TagInline,SliverInline]
1060
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001061 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001062
Siobhan Tully567e3e62013-06-21 18:03:16 -04001063
Tony Mackd90cdbf2013-04-16 22:48:40 -04001064class SliverForm(forms.ModelForm):
1065 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001066 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001067 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001068 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001069 widgets = {
1070 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001071 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001072 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001073 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001074 'node': LinkedSelect,
1075 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001076 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001077
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001078class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001079 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1080 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001081 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1082 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001083
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001084class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001085 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001086 fieldsets = [
Tony Mackbf6aa302014-12-26 13:38:02 -05001087 ('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 -04001088 ]
Scott Baker40c00762014-08-21 16:55:59 -07001089 readonly_fields = ('backend_status_text', )
Tony Mackbf6aa302014-12-26 13:38:02 -05001090 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Baker63d1a552014-08-21 15:19:07 -07001091 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001092
1093 suit_form_tabs =(('general', 'Sliver Details'),
1094 ('tags','Tags'),
1095 )
1096
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001097 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001098
Tony Mackbf6aa302014-12-26 13:38:02 -05001099 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001100
Tony Mackc2835a92013-05-28 09:18:49 -04001101 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1102 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001103 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001104
1105 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1106
Tony Mack04062832013-05-10 08:22:44 -04001107 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001108 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001109 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001110 return Sliver.select_by_user(request.user)
1111
Tony Mack04062832013-05-10 08:22:44 -04001112
Tony Mack1d6b85f2013-05-07 18:49:14 -04001113 def get_formsets(self, request, obj=None):
1114 # make some fields read only if we are updating an existing record
1115 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -07001116 #self.readonly_fields = ('ip', 'instance_name')
Scott Bakerfbb45862014-10-17 16:27:23 -07001117 self.readonly_fields = ('backend_status_text',)
Tony Mack1d6b85f2013-05-07 18:49:14 -04001118 else:
Scott Bakerfbb45862014-10-17 16:27:23 -07001119 self.readonly_fields = ('backend_status_text',)
Scott Baker36f50872014-08-21 13:01:25 -07001120 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001121
1122 for inline in self.get_inline_instances(request, obj):
1123 # hide MyInline in the add view
1124 if obj is None:
1125 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001126 if isinstance(inline, SliverInline):
1127 inline.model.caller = request.user
1128 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001129
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001130 #def save_model(self, request, obj, form, change):
1131 # # update openstack connection to use this site/tenant
1132 # auth = request.session.get('auth', {})
1133 # auth['tenant'] = obj.slice.name
1134 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1135 # obj.creator = request.user
1136 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001137
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001138 #def delete_model(self, request, obj):
1139 # # update openstack connection to use this site/tenant
1140 # auth = request.session.get('auth', {})
1141 # auth['tenant'] = obj.slice.name
1142 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1143 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001144
Siobhan Tully53437282013-04-26 19:30:27 -04001145class UserCreationForm(forms.ModelForm):
1146 """A form for creating new users. Includes all the required
1147 fields, plus a repeated password."""
1148 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1149 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1150
1151 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001152 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001153 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001154
1155 def clean_password2(self):
1156 # Check that the two password entries match
1157 password1 = self.cleaned_data.get("password1")
1158 password2 = self.cleaned_data.get("password2")
1159 if password1 and password2 and password1 != password2:
1160 raise forms.ValidationError("Passwords don't match")
1161 return password2
1162
1163 def save(self, commit=True):
1164 # Save the provided password in hashed format
1165 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001166 user.password = self.cleaned_data["password1"]
1167 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001168 if commit:
1169 user.save()
1170 return user
1171
Siobhan Tully567e3e62013-06-21 18:03:16 -04001172
Siobhan Tully53437282013-04-26 19:30:27 -04001173class UserChangeForm(forms.ModelForm):
1174 """A form for updating users. Includes all the fields on
1175 the user, but replaces the password field with admin's
1176 password hash display field.
1177 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001178 password = ReadOnlyPasswordHashField(label='Password',
1179 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001180
1181 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001182 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001183 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001184
1185 def clean_password(self):
1186 # Regardless of what the user provides, return the initial value.
1187 # This is done here, rather than on the field, because the
1188 # field does not have access to the initial value
1189 return self.initial["password"]
1190
Scott Baker2c3cb642014-05-19 17:55:56 -07001191class UserDashboardViewInline(PlStackTabularInline):
1192 model = UserDashboardView
1193 extra = 0
1194 suit_classes = 'suit-tab suit-tab-dashboards'
1195 fields = ['user', 'dashboardView', 'order']
1196
Scott Baker86c83ab2014-10-03 13:10:47 -07001197class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1198 # Note: Make sure PermissionCheckingAdminMixin is listed before
1199 # admin.ModelAdmin in the class declaration.
1200
Siobhan Tully53437282013-04-26 19:30:27 -04001201 class Meta:
1202 app_label = "core"
1203
1204 # The forms to add and change user instances
1205 form = UserChangeForm
1206 add_form = UserCreationForm
1207
1208 # The fields to be used in displaying the User model.
1209 # These override the definitions on the base UserAdmin
1210 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001211 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001212 list_filter = ('site',)
Tony Mack336e0f92014-11-30 15:53:08 -05001213 inlines = [SlicePrivilegeInline,SitePrivilegeInline,ControllerPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001214
Scott Bakercbfb6002014-10-03 00:32:37 -07001215 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001216 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1217
Siobhan Tully53437282013-04-26 19:30:27 -04001218 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001219 ('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 -04001220 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001221 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001222 #('Important dates', {'fields': ('last_login',)}),
1223 )
1224 add_fieldsets = (
1225 (None, {
1226 'classes': ('wide',),
Scott Baker0a5633b2014-10-06 17:51:20 -07001227 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001228 ),
1229 )
Scott Baker40c00762014-08-21 16:55:59 -07001230 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001231 search_fields = ('email',)
1232 ordering = ('email',)
1233 filter_horizontal = ()
1234
Scott Baker3ca51f62014-05-23 12:05:11 -07001235 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001236
Scott Baker0a5633b2014-10-06 17:51:20 -07001237 @property
1238 def suit_form_tabs(self):
1239 if getattr(_thread_locals, "obj", None) is None:
1240 return []
1241 else:
1242 return (('general','Login Details'),
1243 ('contact','Contact Information'),
1244 ('sliceprivileges','Slice Privileges'),
1245 ('siteprivileges','Site Privileges'),
Tony Mack336e0f92014-11-30 15:53:08 -05001246 ('controllerprivileges','Controller Privileges'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001247 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001248
Tony Mackc2835a92013-05-28 09:18:49 -04001249 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1250 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001251 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001252
1253 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1254
Tony Mack5b061472014-02-04 07:57:10 -05001255 def queryset(self, request):
1256 return User.select_by_user(request.user)
1257
Scott Bakera6a0c772014-12-22 17:35:34 -08001258class ControllerDashboardViewInline(PlStackTabularInline):
1259 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001260 extra = 0
1261 fields = ["controller", "url"]
1262 suit_classes = 'suit-tab suit-tab-controllers'
1263
Scott Baker2c3cb642014-05-19 17:55:56 -07001264class DashboardViewAdmin(PlanetStackBaseAdmin):
1265 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001266 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001267 'classes': ['suit-tab suit-tab-general']})
1268 ]
Scott Baker40c00762014-08-21 16:55:59 -07001269 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001270 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001271
Scott Bakereef5a6b2014-12-19 16:41:12 -08001272 suit_form_tabs =(('general','Dashboard View Details'),
1273 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001274
Scott Baker0165fac2014-01-13 11:49:26 -08001275class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001276 model = ServiceResource
1277 extra = 0
1278
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001279class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001280 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1281 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001282 inlines = [ServiceResourceInline]
1283
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001284 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1285 user_readonly_inlines = []
1286
Scott Baker0165fac2014-01-13 11:49:26 -08001287class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001288 model = ReservedResource
1289 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001290 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001291
1292 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1293 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1294
1295 if db_field.name == 'resource':
1296 # restrict resources to those that the slice's service class allows
1297 if request._slice is not None:
1298 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1299 if len(field.queryset) > 0:
1300 field.initial = field.queryset.all()[0]
1301 else:
1302 field.queryset = field.queryset.none()
1303 elif db_field.name == 'sliver':
1304 # restrict slivers to those that belong to the slice
1305 if request._slice is not None:
1306 field.queryset = field.queryset.filter(slice = request._slice)
1307 else:
1308 field.queryset = field.queryset.none()
1309
1310 return field
1311
Tony Mack5b061472014-02-04 07:57:10 -05001312 def queryset(self, request):
1313 return ReservedResource.select_by_user(request.user)
1314
Scott Baker133c9212013-05-17 09:09:11 -07001315class ReservationChangeForm(forms.ModelForm):
1316 class Meta:
1317 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001318 widgets = {
1319 'slice' : LinkedSelect
1320 }
Scott Baker133c9212013-05-17 09:09:11 -07001321
1322class ReservationAddForm(forms.ModelForm):
1323 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1324 refresh = forms.CharField(widget=forms.HiddenInput())
1325
1326 class Media:
1327 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1328
1329 def clean_slice(self):
1330 slice = self.cleaned_data.get("slice")
1331 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1332 if len(x) == 0:
1333 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1334 return slice
1335
1336 class Meta:
1337 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001338 widgets = {
1339 'slice' : LinkedSelect
1340 }
1341
Scott Baker133c9212013-05-17 09:09:11 -07001342
1343class ReservationAddRefreshForm(ReservationAddForm):
1344 """ This form is displayed when the Reservation Form receives an update
1345 from the Slice dropdown onChange handler. It doesn't validate the
1346 data and doesn't save the data. This will cause the form to be
1347 redrawn.
1348 """
1349
Scott Baker8737e5f2013-05-17 09:35:32 -07001350 """ don't validate anything other than slice """
1351 dont_validate_fields = ("startTime", "duration")
1352
Scott Baker133c9212013-05-17 09:09:11 -07001353 def full_clean(self):
1354 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001355
1356 for fieldname in self.dont_validate_fields:
1357 if fieldname in self._errors:
1358 del self._errors[fieldname]
1359
Scott Baker133c9212013-05-17 09:09:11 -07001360 return result
1361
1362 """ don't save anything """
1363 def is_valid(self):
1364 return False
1365
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001366class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001367 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001368 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001369 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001370 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001371 form = ReservationAddForm
1372
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001373 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1374
1375 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001376 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001377
Scott Baker133c9212013-05-17 09:09:11 -07001378 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001379 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001380 request._refresh = False
1381 request._slice = None
1382 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001383 # "refresh" will be set to "1" if the form was submitted due to
1384 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001385 if request.POST.get("refresh","1") == "1":
1386 request._refresh = True
1387 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001388
1389 # Keep track of the slice that was selected, so the
1390 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001391 request._slice = request.POST.get("slice",None)
1392 if (request._slice is not None):
1393 request._slice = Slice.objects.get(id=request._slice)
1394
1395 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1396 return result
1397
Scott Bakeracd45142013-05-19 16:19:16 -07001398 def changelist_view(self, request, extra_context = None):
1399 timezone.activate(request.user.timezone)
1400 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1401
Scott Baker133c9212013-05-17 09:09:11 -07001402 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001403 request._obj_ = obj
1404 if obj is not None:
1405 # For changes, set request._slice to the slice already set in the
1406 # object.
1407 request._slice = obj.slice
1408 self.form = ReservationChangeForm
1409 else:
1410 if getattr(request, "_refresh", False):
1411 self.form = ReservationAddRefreshForm
1412 else:
1413 self.form = ReservationAddForm
1414 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1415
Scott Baker133c9212013-05-17 09:09:11 -07001416 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001417 if (obj is not None):
1418 # Prevent slice from being changed after the reservation has been
1419 # created.
1420 return ['slice']
1421 else:
Scott Baker133c9212013-05-17 09:09:11 -07001422 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001423
Tony Mack5b061472014-02-04 07:57:10 -05001424 def queryset(self, request):
1425 return Reservation.select_by_user(request.user)
1426
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001427class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001428 list_display = ("backend_status_icon", "name", )
1429 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001430 user_readonly_fields = ['name']
1431 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001432
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001433class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001434 list_display = ("backend_status_icon", "name", )
1435 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001436 user_readonly_fields = ['name']
1437 user_readonly_inlines = []
1438
Scott Baker0165fac2014-01-13 11:49:26 -08001439class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001440 model = Router.networks.through
1441 extra = 0
1442 verbose_name_plural = "Routers"
1443 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001444 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001445
Scott Bakerb27b62c2014-08-15 16:29:16 -07001446class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001447 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001448 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001449 verbose_name_plural = "Parameters"
1450 verbose_name = "Parameter"
1451 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001452 fields = ['backend_status_icon', 'parameter', 'value']
1453 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001454
Scott Baker0165fac2014-01-13 11:49:26 -08001455class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001456 fields = ['backend_status_icon', 'network','sliver','ip']
1457 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001458 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001459 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001460 extra = 0
1461 verbose_name_plural = "Slivers"
1462 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001463 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001464
Scott Baker0165fac2014-01-13 11:49:26 -08001465class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001466 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001467 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001468 extra = 0
1469 verbose_name_plural = "Slices"
1470 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001471 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001472 fields = ['backend_status_icon', 'network','slice']
1473 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001474
Tony Mack336e0f92014-11-30 15:53:08 -05001475class ControllerNetworksInline(PlStackTabularInline):
1476 model = ControllerNetworks
Scott Bakerfbb45862014-10-17 16:27:23 -07001477 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001478 verbose_name_plural = "Controller Networks"
1479 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001480 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001481 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001482 readonly_fields = ('backend_status_icon', )
1483
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001484class NetworkForm(forms.ModelForm):
1485 class Meta:
1486 model = Network
1487 widgets = {
1488 'topologyParameters': UploadTextareaWidget,
1489 'controllerParameters': UploadTextareaWidget,
1490 }
1491
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001492class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001493 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1494 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001495 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001496
Scott Bakerd7d2a392013-08-06 08:57:30 -07001497 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Mack336e0f92014-11-30 15:53:08 -05001498 admin_inlines = [ControllerNetworksInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001499
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001500 form=NetworkForm
1501
Siobhan Tully2d95e482013-09-06 10:56:06 -04001502 fieldsets = [
Scott Baker0451fb62015-01-03 12:29:29 -08001503 (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 -08001504 'classes':['suit-tab suit-tab-general']}),
Scott Baker0451fb62015-01-03 12:29:29 -08001505 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker40248712014-11-17 16:04:45 -08001506 'classes':['suit-tab suit-tab-sdn']}),
1507 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001508
Scott Baker40c00762014-08-21 16:55:59 -07001509 readonly_fields = ('backend_status_text', )
Scott Baker0451fb62015-01-03 12:29:29 -08001510 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 -04001511
Scott Bakerfbb45862014-10-17 16:27:23 -07001512 @property
1513 def suit_form_tabs(self):
1514 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001515 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001516 ('netparams', 'Parameters'),
1517 ('networkslivers','Slivers'),
1518 ('networkslices','Slices'),
1519 ('routers','Routers'),
1520 ]
1521
1522 request=getattr(_thread_locals, "request", None)
1523 if request and request.user.is_admin:
1524 tabs.append( ('admin-only', 'Admin-Only') )
1525
1526 return tabs
1527
1528
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001529class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001530 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001531 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001532 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001533 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001534 fieldsets = [
Scott Baker81fa17f2015-01-03 12:03:38 -08001535 (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 -08001536 'classes':['suit-tab suit-tab-general']}),]
1537 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001538
Scott Baker37b47902014-09-02 14:37:41 -07001539class FlavorAdmin(PlanetStackBaseAdmin):
1540 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1541 list_display_links = ("backend_status_icon", "name")
1542 user_readonly_fields = ("name", "flavor")
1543 fields = ("name", "description", "flavor", "order", "default")
1544
Tony Mack31c2b8f2013-04-26 20:01:42 -04001545# register a signal that caches the user's credentials when they log in
1546def cache_credentials(sender, user, request, **kwds):
1547 auth = {'username': request.POST['username'],
1548 'password': request.POST['password']}
1549 request.session['auth'] = auth
1550user_logged_in.connect(cache_credentials)
1551
Scott Baker15cddfa2013-12-09 13:45:19 -08001552def dollar_field(fieldName, short_description):
1553 def newFunc(self, obj):
1554 try:
1555 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1556 except:
1557 x=getattr(obj, fieldName, 0.0)
1558 return x
1559 newFunc.short_description = short_description
1560 return newFunc
1561
1562def right_dollar_field(fieldName, short_description):
1563 def newFunc(self, obj):
1564 try:
1565 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1566 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1567 except:
1568 x=getattr(obj, fieldName, 0.0)
1569 return x
1570 newFunc.short_description = short_description
1571 newFunc.allow_tags = True
1572 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001573
Scott Baker0165fac2014-01-13 11:49:26 -08001574class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001575 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001576 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001577 verbose_name_plural = "Charges"
1578 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001579 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001580 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1581 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1582 can_delete = False
1583 max_num = 0
1584
1585 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001586
1587class InvoiceAdmin(admin.ModelAdmin):
1588 list_display = ("date", "account")
1589
1590 inlines = [InvoiceChargeInline]
1591
Scott Baker9cb88a22013-12-09 18:56:00 -08001592 fields = ["date", "account", "dollar_amount"]
1593 readonly_fields = ["date", "account", "dollar_amount"]
1594
1595 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001596
Scott Baker0165fac2014-01-13 11:49:26 -08001597class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001598 model = Invoice
1599 extra = 0
1600 verbose_name_plural = "Invoices"
1601 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001602 fields = ["date", "dollar_amount"]
1603 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001604 suit_classes = 'suit-tab suit-tab-accountinvoice'
1605 can_delete=False
1606 max_num=0
1607
1608 dollar_amount = right_dollar_field("amount", "Amount")
1609
Scott Baker0165fac2014-01-13 11:49:26 -08001610class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001611 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001612 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001613 verbose_name_plural = "Charges"
1614 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001615 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001616 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1617 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001618 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001619 can_delete=False
1620 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001621
1622 def queryset(self, request):
1623 qs = super(PendingChargeInline, self).queryset(request)
1624 qs = qs.filter(state="pending")
1625 return qs
1626
Scott Baker15cddfa2013-12-09 13:45:19 -08001627 dollar_amount = right_dollar_field("amount", "Amount")
1628
Scott Baker0165fac2014-01-13 11:49:26 -08001629class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001630 model=Payment
1631 extra = 1
1632 verbose_name_plural = "Payments"
1633 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001634 fields = ["date", "dollar_amount"]
1635 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001636 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001637 can_delete=False
1638 max_num=0
1639
1640 dollar_amount = right_dollar_field("amount", "Amount")
1641
Scott Baker43105042013-12-06 23:23:36 -08001642class AccountAdmin(admin.ModelAdmin):
1643 list_display = ("site", "balance_due")
1644
1645 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1646
1647 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001648 (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 -08001649
Scott Baker15cddfa2013-12-09 13:45:19 -08001650 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001651
1652 suit_form_tabs =(
1653 ('general','Account Details'),
1654 ('accountinvoice', 'Invoices'),
1655 ('accountpayments', 'Payments'),
1656 ('accountpendingcharges','Pending Charges'),
1657 )
1658
Scott Baker15cddfa2013-12-09 13:45:19 -08001659 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1660 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1661 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1662
Siobhan Tully53437282013-04-26 19:30:27 -04001663# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001664admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001665# ... and, since we're not using Django's builtin permissions,
1666# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001667#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001668
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001669#Do not show django evolution in the admin interface
1670from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001671#admin.site.unregister(Version)
1672#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001673
1674
1675# When debugging it is often easier to see all the classes, but for regular use
1676# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001677showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001678
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001679admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001680admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001681admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001682admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001683admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001684admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001685admin.site.register(Network, NetworkAdmin)
1686admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001687admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001688admin.site.register(Account, AccountAdmin)
1689admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001690
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001691if True:
1692 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1693 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001694 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001695 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001696 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001697 admin.site.register(SiteRole)
1698 admin.site.register(SliceRole)
1699 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001700 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001701 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1702 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001703 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001704 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001705 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001706 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001707