blob: d027a2ec3834e14e6a15a2cadf7bdea781359ad0 [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Scott Baker9f6b8ed2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Bakercbfb6002014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Scott Bakere2bbf7e2014-01-13 12:09:31 -080017from django.core.urlresolvers import reverse, NoReverseMatch
Scott Baker9f6b8ed2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
20from django.forms.utils import flatatt, to_current_timezone
Scott Baker3cde7372014-10-21 21:03:08 -070021from cgi import escape as html_escape
Tony Mack7130ac32013-03-22 21:58:00 -040022
Scott Baker36f50872014-08-21 13:01:25 -070023import django_evolution
Scott Baker0a5633b2014-10-06 17:51:20 -070024import threading
25
26# thread locals necessary to work around a django-suit issue
27_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070028
Scott Baker40c00762014-08-21 16:55:59 -070029def backend_icon(obj): # backend_status, enacted, updated):
30 #return "%s %s %s" % (str(obj.updated), str(obj.enacted), str(obj.backend_status))
31 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Bakerfbf06642014-09-09 10:38:15 -070032 return '<span style="min-width:16px;"><img src="/static/admin/img/icon_success.gif"></span>'
Scott Baker40c00762014-08-21 16:55:59 -070033 else:
34 if obj.backend_status == "Provisioning in progress" or obj.backend_status=="":
Scott Bakerfbf06642014-09-09 10:38:15 -070035 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_clock.gif"></span>' % obj.backend_status
Scott Baker63d1a552014-08-21 15:19:07 -070036 else:
Scott Baker3cde7372014-10-21 21:03:08 -070037 return '<span style="min-width:16px;" title="%s"><img src="/static/admin/img/icon_error.gif"></span>' % html_escape(obj.backend_status, quote=True)
Scott Baker40c00762014-08-21 16:55:59 -070038
39def backend_text(obj):
40 icon = backend_icon(obj)
41 if (obj.enacted is not None) and obj.enacted >= obj.updated:
Scott Baker3cde7372014-10-21 21:03:08 -070042 return "%s %s" % (icon, "successfully enacted")
Scott Baker40c00762014-08-21 16:55:59 -070043 else:
Scott Baker3cde7372014-10-21 21:03:08 -070044 return "%s %s" % (icon, html_escape(obj.backend_status, quote=True))
Scott Baker63d1a552014-08-21 15:19:07 -070045
Scott Baker9f6b8ed2014-11-17 23:44:03 -080046class UploadTextareaWidget(AdminTextareaWidget):
47 def render(self, name, value, attrs=None):
48 if value is None:
49 value = ''
50 final_attrs = self.build_attrs(attrs, name=name)
51 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
52 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
53 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
54 flatatt(final_attrs),
55 force_text(value))
56
Scott Baker36f50872014-08-21 13:01:25 -070057class PlainTextWidget(forms.HiddenInput):
58 input_type = 'hidden'
59
60 def render(self, name, value, attrs=None):
61 if value is None:
62 value = ''
63 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
64
Scott Baker86c83ab2014-10-03 13:10:47 -070065class PermissionCheckingAdminMixin(object):
Scott Bakercbfb6002014-10-03 00:32:37 -070066 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050067
68 def has_add_permission(self, request, obj=None):
69 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070070
Siobhan Tullycf04fb62014-01-11 11:25:57 -050071 def has_delete_permission(self, request, obj=None):
72 return (not self.__user_is_readonly(request))
73
74 def save_model(self, request, obj, form, change):
75 if self.__user_is_readonly(request):
Scott Bakercbfb6002014-10-03 00:32:37 -070076 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050077 raise PermissionDenied
Scott Bakercbfb6002014-10-03 00:32:37 -070078
79 obj.caller = request.user
80 # update openstack connection to use this site/tenant
81 obj.save_by_user(request.user)
82
83 def delete_model(self, request, obj):
84 obj.delete_by_user(request.user)
85
86 def save_formset(self, request, form, formset, change):
87 instances = formset.save(commit=False)
88 for instance in instances:
89 instance.save_by_user(request.user)
90
91 # BUG in django 1.7? Objects are not deleted by formset.save if
92 # commit is False. So let's delete them ourselves.
93 #
94 # code from forms/models.py save_existing_objects()
95 try:
96 forms_to_delete = formset.deleted_forms
97 except AttributeError:
98 forms_to_delete = []
99 if formset.initial_forms:
100 for form in formset.initial_forms:
101 obj = form.instance
102 if form in forms_to_delete:
103 if obj.pk is None:
104 continue
105 formset.deleted_objects.append(obj)
106 obj.delete()
107
108 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500109
110 def get_actions(self,request):
Scott Baker86c83ab2014-10-03 13:10:47 -0700111 actions = super(PermissionCheckingAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500112
113 if self.__user_is_readonly(request):
114 if 'delete_selected' in actions:
115 del actions['delete_selected']
116
117 return actions
118
119 def change_view(self,request,object_id, extra_context=None):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500120 if self.__user_is_readonly(request):
Scott Bakeraf73e102014-04-22 22:40:07 -0700121 if not hasattr(self, "readonly_save"):
122 # save the original readonly fields
123 self.readonly_save = self.readonly_fields
124 self.inlines_save = self.inlines
Scott Bakere8859f92014-05-23 12:42:40 -0700125 if hasattr(self, "user_readonly_fields"):
126 self.readonly_fields=self.user_readonly_fields
127 if hasattr(self, "user_readonly_inlines"):
128 self.inlines = self.user_readonly_inlines
Scott Bakeraf73e102014-04-22 22:40:07 -0700129 else:
130 if hasattr(self, "readonly_save"):
131 # restore the original readonly fields
132 self.readonly_fields = self.readonly_save
Scott Bakere8859f92014-05-23 12:42:40 -0700133 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700134 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500135
136 try:
Scott Baker86c83ab2014-10-03 13:10:47 -0700137 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500138 except PermissionDenied:
139 pass
140 if request.method == 'POST':
141 raise PermissionDenied
142 request.readonly = True
Scott Baker86c83ab2014-10-03 13:10:47 -0700143 return super(PermissionCheckingAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500144
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500145 def __user_is_readonly(self, request):
146 return request.user.isReadOnlyUser()
147
Scott Baker40c00762014-08-21 16:55:59 -0700148 def backend_status_text(self, obj):
149 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700150
Scott Baker63d1a552014-08-21 15:19:07 -0700151 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700152 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700153 backend_status_icon.short_description = ""
154
Scott Baker24ded6a2014-11-05 09:05:38 -0800155 def get_form(self, request, obj=None, **kwargs):
Scott Baker5c432692014-10-16 00:57:55 -0700156 # Save obj and request in thread-local storage, so suit_form_tabs can
157 # use it to determine whether we're in edit or add mode, and can
158 # determine whether the user is an admin.
159 _thread_locals.request = request
160 _thread_locals.obj = obj
Scott Baker24ded6a2014-11-05 09:05:38 -0800161 return super(PermissionCheckingAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker5c432692014-10-16 00:57:55 -0700162
163 def get_inline_instances(self, request, obj=None):
164 inlines = super(PermissionCheckingAdminMixin, self).get_inline_instances(request, obj)
165
166 # inlines that should only be shown to an admin user
167 if request.user.is_admin:
168 for inline_class in getattr(self, "admin_inlines", []):
169 inlines.append(inline_class(self.model, self.admin_site))
170
171 return inlines
172
Scott Baker86c83ab2014-10-03 13:10:47 -0700173class ReadOnlyAwareAdmin(PermissionCheckingAdminMixin, admin.ModelAdmin):
174 # Note: Make sure PermissionCheckingAdminMixin is listed before
175 # admin.ModelAdmin in the class declaration.
176
Scott Bakercbfb6002014-10-03 00:32:37 -0700177 pass
178
179class PlanetStackBaseAdmin(ReadOnlyAwareAdmin):
180 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700181
Scott Bakere8859f92014-05-23 12:42:40 -0700182class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400183 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700184 if not super(SingletonAdmin, self).has_add_permission(request):
185 return False
186
Siobhan Tullyce652d02013-10-08 21:52:35 -0400187 num_objects = self.model.objects.count()
188 if num_objects >= 1:
189 return False
190 else:
191 return True
192
Siobhan Tullyd3515752013-06-21 16:34:53 -0400193class PlStackTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800194 def __init__(self, *args, **kwargs):
195 super(PlStackTabularInline, self).__init__(*args, **kwargs)
196
197 # InlineModelAdmin as no get_fields() method, so in order to add
198 # the selflink field, we override __init__ to modify self.fields and
199 # self.readonly_fields.
200
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800201 self.setup_selflink()
202
Scott Baker874936e2014-01-13 18:15:34 -0800203 def get_change_url(self, model, id):
204 """ Get the URL to a change form in the admin for this model """
205 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800206 try:
Scott Baker874936e2014-01-13 18:15:34 -0800207 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800208 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800209 return None
210
211 return url
212
213 def setup_selflink(self):
214 if hasattr(self, "selflink_fieldname"):
215 """ self.selflink_model can be defined to punch through a relation
216 to its target object. For example, in SliceNetworkInline, set
217 selflink_model = "network", and the URL will lead to the Network
218 object instead of trying to bring up a change view of the
219 SliceNetwork object.
220 """
221 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
222 else:
223 self.selflink_model = self.model
224
225 url = self.get_change_url(self.selflink_model, 0)
226
227 # We don't have an admin for this object, so don't create the
228 # selflink.
229 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800230 return
231
Scott Baker874936e2014-01-13 18:15:34 -0800232 # Since we need to add "selflink" to the field list, we need to create
233 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800234 if (self.fields is None):
235 self.fields = []
236 for f in self.model._meta.fields:
237 if f.editable and f.name != "id":
238 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800239
Scott Baker874936e2014-01-13 18:15:34 -0800240 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800241
Scott Baker874936e2014-01-13 18:15:34 -0800242 if self.readonly_fields is None:
243 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800244
Scott Baker874936e2014-01-13 18:15:34 -0800245 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800246
247 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800248 if hasattr(self, "selflink_fieldname"):
249 obj = getattr(obj, self.selflink_fieldname)
250
Scott Baker86568322014-01-12 16:53:31 -0800251 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800252 url = self.get_change_url(self.selflink_model, obj.id)
253 return "<a href='%s'>Details</a>" % str(url)
Scott Baker86568322014-01-12 16:53:31 -0800254 else:
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800255 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800256
257 selflink.allow_tags = True
258 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400259
Scott Bakerb27b62c2014-08-15 16:29:16 -0700260 def has_add_permission(self, request):
261 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500262
263 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700264 readonly_fields = list(self.readonly_fields)[:]
265 if request.user.isReadOnlyUser():
266 for field in self.fields:
267 if not field in readonly_fields:
268 readonly_fields.append(field)
269 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500270
Scott Baker40c00762014-08-21 16:55:59 -0700271 def backend_status_icon(self, obj):
272 return mark_safe(backend_icon(obj))
273 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700274
Scott Bakerb27b62c2014-08-15 16:29:16 -0700275class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500276 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700277 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500278
Scott Bakerb27b62c2014-08-15 16:29:16 -0700279 def get_readonly_fields(self, request, obj=None):
280 readonly_fields = list(self.readonly_fields)[:]
281 if request.user.isReadOnlyUser():
282 for field in self.fields:
283 if not field in readonly_fields:
284 readonly_fields.append(field)
285 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500286
Scott Baker40c00762014-08-21 16:55:59 -0700287 def backend_status_icon(self, obj):
288 return mark_safe(backend_icon(obj))
289 backend_status_icon.short_description = ""
290
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400291class ReservationInline(PlStackTabularInline):
292 model = Reservation
293 extra = 0
294 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700295
Tony Mack5b061472014-02-04 07:57:10 -0500296 def queryset(self, request):
297 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400298
Scott Bakerb27b62c2014-08-15 16:29:16 -0700299class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400300 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400301 extra = 0
302 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500303 fields = ['service', 'name', 'value']
304
305 def queryset(self, request):
306 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400307
Scott Baker74d8e622013-07-29 16:04:22 -0700308class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400309 """ This is a callable that looks up a network name in a sliver and returns
310 the ip address for that network.
311 """
312
Scott Baker434ca7e2014-08-15 12:29:20 -0700313 byNetworkName = {} # class variable
314
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400315 def __init__(self, name):
316 self.short_description = name
317 self.__name__ = name
318 self.network_name = name
319
320 def __call__(self, obj):
321 if obj is not None:
322 for nbs in obj.networksliver_set.all():
323 if (nbs.network.name == self.network_name):
324 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700325 return ""
326
327 def __str__(self):
328 return self.network_name
329
Scott Baker434ca7e2014-08-15 12:29:20 -0700330 @staticmethod
331 def get(network_name):
332 """ We want to make sure we alwars return the same NetworkLookerUpper
333 because sometimes django will cause them to be instantiated multiple
334 times (and we don't want different ones in form.fields vs
335 SliverInline.readonly_fields).
336 """
337 if network_name not in NetworkLookerUpper.byNetworkName:
338 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
339 return NetworkLookerUpper.byNetworkName[network_name]
340
Siobhan Tullyd3515752013-06-21 16:34:53 -0400341class SliverInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400342 model = Sliver
Tony Mackbf6aa302014-12-26 13:38:02 -0500343 fields = ['backend_status_icon', 'all_ips_string', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400344 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700345 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400346 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700347
Tony Mack5b061472014-02-04 07:57:10 -0500348 def queryset(self, request):
349 return Sliver.select_by_user(request.user)
350
Scott Bakerb24cc932014-06-09 10:51:16 -0700351 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackbf6aa302014-12-26 13:38:02 -0500352 if db_field.name == 'deployment':
Scott Baker3b678742014-06-09 13:11:54 -0700353 kwargs['queryset'] = Deployment.select_by_acl(request.user)
Scott Baker7a61dc42014-09-02 17:08:20 -0700354 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Tony Mackbf6aa302014-12-26 13:38:02 -0500355 if db_field.name == 'flavor':
Scott Baker32481312014-09-08 12:14:14 -0700356 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700357
358 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700359
360 return field
361
Siobhan Tullyd3515752013-06-21 16:34:53 -0400362class SiteInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400363 model = Site
364 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400365 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400366
Tony Mack5b061472014-02-04 07:57:10 -0500367 def queryset(self, request):
368 return Site.select_by_user(request.user)
369
Siobhan Tullyd3515752013-06-21 16:34:53 -0400370class UserInline(PlStackTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400371 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700372 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
373 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400374 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400375 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400376
Tony Mack5b061472014-02-04 07:57:10 -0500377 def queryset(self, request):
378 return User.select_by_user(request.user)
379
Siobhan Tullyd3515752013-06-21 16:34:53 -0400380class SliceInline(PlStackTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400381 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700382 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
383 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400384 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400385 suit_classes = 'suit-tab suit-tab-slices'
386
Tony Mack5b061472014-02-04 07:57:10 -0500387 def queryset(self, request):
388 return Slice.select_by_user(request.user)
389
Siobhan Tullyd3515752013-06-21 16:34:53 -0400390class NodeInline(PlStackTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400391 model = Node
392 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400393 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack93d1b032014-12-08 16:43:02 -0500394 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700395 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400396
Tony Mack93d1b032014-12-08 16:43:02 -0500397class DeploymentPrivilegeInline(PlStackTabularInline):
398 model = DeploymentPrivilege
399 extra = 0
400 suit_classes = 'suit-tab suit-tab-admin-only'
401 fields = ['backend_status_icon', 'user','role','deployment']
402 readonly_fields = ('backend_status_icon', )
403
404 def queryset(self, request):
405 return DeploymentPrivilege.select_by_user(request.user)
406
Tony Mack336e0f92014-11-30 15:53:08 -0500407class ControllerPrivilegeInline(PlStackTabularInline):
408 model = ControllerPrivilege
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400409 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500410 suit_classes = 'suit-tab suit-tab-admin-only'
411 fields = ['backend_status_icon', 'user','role','controller']
Scott Baker40c00762014-08-21 16:55:59 -0700412 readonly_fields = ('backend_status_icon', )
Tony Mack5b061472014-02-04 07:57:10 -0500413
414 def queryset(self, request):
Tony Mack336e0f92014-11-30 15:53:08 -0500415 return ControllerPrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400416
Tony Mack93d1b032014-12-08 16:43:02 -0500417class ControllerSiteDeploymentsInline(PlStackTabularInline):
418 model = ControllerSiteDeployments
419 extra = 0
420 suit_classes = 'suit-tab suit-tab-admin-only'
421 fields = ['controller', 'site_deployment', 'tenant_id']
422
Siobhan Tullyd3515752013-06-21 16:34:53 -0400423class SitePrivilegeInline(PlStackTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400424 model = SitePrivilege
425 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400426 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700427 fields = ['backend_status_icon', 'user','site', 'role']
428 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400429
Tony Mackc2835a92013-05-28 09:18:49 -0400430 def formfield_for_foreignkey(self, db_field, request, **kwargs):
431 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500432 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400433
434 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500435 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400436 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
437
Tony Mack5b061472014-02-04 07:57:10 -0500438 def queryset(self, request):
439 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400440
Sapan Bhatia39e85582014-11-19 15:07:36 -0500441class SiteDeploymentsInline(PlStackTabularInline):
442 model = SiteDeployments
Tony Macke4be32f2014-03-11 20:45:25 -0400443 extra = 0
444 suit_classes = 'suit-tab suit-tab-deployments'
Tony Mack528d4222014-12-05 17:13:08 -0500445 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700446 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400447
448 def formfield_for_foreignkey(self, db_field, request, **kwargs):
449 if db_field.name == 'site':
450 kwargs['queryset'] = Site.select_by_user(request.user)
451
452 if db_field.name == 'deployment':
453 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mack528d4222014-12-05 17:13:08 -0500454
455 if db_field.name == 'controller':
456 kwargs['queryset'] = Controller.select_by_user(request.user)
457
Sapan Bhatia39e85582014-11-19 15:07:36 -0500458 return super(SiteDeploymentsInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400459
460 def queryset(self, request):
Sapan Bhatia39e85582014-11-19 15:07:36 -0500461 return SiteDeployments.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400462
463
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400464class SlicePrivilegeInline(PlStackTabularInline):
465 model = SlicePrivilege
466 suit_classes = 'suit-tab suit-tab-sliceprivileges'
467 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700468 fields = ('backend_status_icon', 'user', 'slice', 'role')
469 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400470
Tony Mackc2835a92013-05-28 09:18:49 -0400471 def formfield_for_foreignkey(self, db_field, request, **kwargs):
472 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700473 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400474 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700475 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400476
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400477 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400478
Tony Mack5b061472014-02-04 07:57:10 -0500479 def queryset(self, request):
480 return SlicePrivilege.select_by_user(request.user)
481
Scott Bakera0015eb2013-08-14 17:28:14 -0700482class SliceNetworkInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700483 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800484 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700485 extra = 0
486 verbose_name = "Network Connection"
487 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400488 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700489 fields = ['backend_status_icon', 'network']
490 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700491
Sapan Bhatiae9f96f62014-11-19 15:10:16 -0500492class ImageDeploymentsInline(PlStackTabularInline):
493 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700494 extra = 0
495 verbose_name = "Image Deployments"
496 verbose_name_plural = "Image Deployments"
497 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack336e0f92014-11-30 15:53:08 -0500498 fields = ['backend_status_icon', 'image', 'deployment']
499 readonly_fields = ['backend_status_icon']
500
501class ControllerImagesInline(PlStackTabularInline):
502 model = ControllerImages
503 extra = 0
504 verbose_name = "Controller Images"
505 verbose_name_plural = "Controller Images"
506 suit_classes = 'suit-tab suit-tab-admin-only'
507 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700508 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700509
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400510class SliceRoleAdmin(PlanetStackBaseAdmin):
511 model = SliceRole
512 pass
513
514class SiteRoleAdmin(PlanetStackBaseAdmin):
515 model = SiteRole
516 pass
517
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400518class DeploymentAdminForm(forms.ModelForm):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400519 sites = forms.ModelMultipleChoiceField(
520 queryset=Site.objects.all(),
521 required=False,
Scott Baker70983182014-06-09 22:10:00 -0700522 help_text="Select which sites are allowed to host nodes in this deployment",
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400523 widget=FilteredSelectMultiple(
524 verbose_name=('Sites'), is_stacked=False
525 )
526 )
Scott Bakerde0f4412014-06-11 15:40:26 -0700527 images = forms.ModelMultipleChoiceField(
528 queryset=Image.objects.all(),
529 required=False,
530 help_text="Select which images should be deployed on this deployment",
531 widget=FilteredSelectMultiple(
532 verbose_name=('Images'), is_stacked=False
533 )
534 )
Scott Baker37b47902014-09-02 14:37:41 -0700535 flavors = forms.ModelMultipleChoiceField(
536 queryset=Flavor.objects.all(),
537 required=False,
538 help_text="Select which flavors should be usable on this deployment",
539 widget=FilteredSelectMultiple(
540 verbose_name=('Flavors'), is_stacked=False
541 )
542 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400543 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400544 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700545 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400546
Siobhan Tully320b4622014-01-17 15:11:14 -0500547 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700548 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500549 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
550
Scott Baker5380c522014-06-06 14:49:43 -0700551 self.fields['accessControl'].initial = "allow site " + request.user.site.name
552
Siobhan Tully320b4622014-01-17 15:11:14 -0500553 if self.instance and self.instance.pk:
Scott Baker9f6b8ed2014-11-17 23:44:03 -0800554 self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments.all()]
555 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700556 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700557
558 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
559 """ helper function for handling m2m relations from the MultipleChoiceField
560
561 this_obj: the source object we want to link from
562
563 selected_objs: a list of destination objects we want to link to
564
565 all_relations: the full set of relations involving this_obj, including ones we don't want
566
567 relation_class: the class that implements the relation from source to dest
568
569 local_attrname: field name representing this_obj in relation_class
570
571 foreign_attrname: field name representing selected_objs in relation_class
572
573 This function will remove all newobjclass relations from this_obj
574 that are not contained in selected_objs, and add any relations that
575 are in selected_objs but don't exist in the data model yet.
576 """
577
578 existing_dest_objs = []
579 for relation in list(all_relations):
580 if getattr(relation, foreign_attrname) not in selected_objs:
581 #print "deleting site", sdp.site
582 relation.delete()
583 else:
584 existing_dest_objs.append(getattr(relation, foreign_attrname))
585
586 for dest_obj in selected_objs:
587 if dest_obj not in existing_dest_objs:
588 #print "adding site", site
589 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
590 relation = relation_class(**kwargs)
591 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500592
593 def save(self, commit=True):
594 deployment = super(DeploymentAdminForm, self).save(commit=False)
595
596 if commit:
597 deployment.save()
Scott Baker61b6aec2014-10-06 17:17:40 -0700598 # this has to be done after save() if/when a deployment is first created
599 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500600
601 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700602 # save_m2m() doesn't seem to work with 'through' relations. So we
603 # create/destroy the through models ourselves. There has to be
604 # a better way...
605
Sapan Bhatia39e85582014-11-19 15:07:36 -0500606 self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments.all(), SiteDeployments, "deployment", "site")
Tony Mack592aa952014-12-15 11:45:02 -0500607 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
608 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
609 # so well handle that manually here
610 for flavor in deployment.flavors.all():
611 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mack11f4d202014-12-15 12:37:59 -0500612 deployment.flavors.remove(flavor)
Tony Mack592aa952014-12-15 11:45:02 -0500613 for flavor in self.cleaned_data['flavors']:
614 if flavor not in deployment.flavors.all():
615 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700616
Scott Baker37b47902014-09-02 14:37:41 -0700617 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500618
619 return deployment
620
Scott Bakerff5e0f32014-05-22 14:40:27 -0700621class DeploymentAdminROForm(DeploymentAdminForm):
622 def save(self, commit=True):
623 raise PermissionDenied
624
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500625class SiteAssocInline(PlStackTabularInline):
626 model = Site.deployments.through
627 extra = 0
628 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400629
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400630class DeploymentAdmin(PlanetStackBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500631 model = Deployment
Tony Mack93d1b032014-12-08 16:43:02 -0500632 fieldList = ['backend_status_text', 'name', 'sites', 'images', 'flavors', 'accessControl']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500633 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
Tony Mack93d1b032014-12-08 16:43:02 -0500634 # node no longer directly connected to deployment
635 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
636 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700637 list_display = ['backend_status_icon', 'name']
638 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700639 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500640
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500641 user_readonly_fields = ['name']
642
Tony Mack93d1b032014-12-08 16:43:02 -0500643 # nodes no longer direclty connected to deployments
644 #suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
645 suit_form_tabs =(('sites','Deployment Details'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500646
Scott Bakerff5e0f32014-05-22 14:40:27 -0700647 def get_form(self, request, obj=None, **kwargs):
648 if request.user.isReadOnlyUser():
649 kwargs["form"] = DeploymentAdminROForm
650 else:
651 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700652 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
653
654 # from stackexchange: pass the request object into the form
655
656 class AdminFormMetaClass(adminForm):
657 def __new__(cls, *args, **kwargs):
658 kwargs['request'] = request
659 return adminForm(*args, **kwargs)
660
661 return AdminFormMetaClass
662
Tony Mack528d4222014-12-05 17:13:08 -0500663class ControllerAdminForm(forms.ModelForm):
664 site_deployments = forms.ModelMultipleChoiceField(
Tony Mack93d1b032014-12-08 16:43:02 -0500665 queryset=SiteDeployments.objects.all(),
Tony Mack528d4222014-12-05 17:13:08 -0500666 required=False,
667 help_text="Select which sites deployments are managed by this controller",
668 widget=FilteredSelectMultiple(
669 verbose_name=('Site Deployments'), is_stacked=False
670 )
671 )
672
673 class Meta:
674 model = Controller
675
Tony Mack93d1b032014-12-08 16:43:02 -0500676 def __init__(self, *args, **kwargs):
Tony Mack528d4222014-12-05 17:13:08 -0500677 request = kwargs.pop('request', None)
678 super(ControllerAdminForm, self).__init__(*args, **kwargs)
679
680 if self.instance and self.instance.pk:
681 self.fields['site_deployments'].initial = [x.site_deployment for x in self.instance.controllersitedeployments.all()]
682
Tony Mack93d1b032014-12-08 16:43:02 -0500683 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
Tony Mack528d4222014-12-05 17:13:08 -0500684 """ helper function for handling m2m relations from the MultipleChoiceField
685 this_obj: the source object we want to link from
686 selected_objs: a list of destination objects we want to link to
687 all_relations: the full set of relations involving this_obj, including ones we don't want
688 relation_class: the class that implements the relation from source to dest
689 local_attrname: field name representing this_obj in relation_class
690 foreign_attrname: field name representing selected_objs in relation_class
691 This function will remove all newobjclass relations from this_obj
692 that are not contained in selected_objs, and add any relations that
693 are in selected_objs but don't exist in the data model yet.
694 """
695 existing_dest_objs = []
696 for relation in list(all_relations):
697 if getattr(relation, foreign_attrname) not in selected_objs:
698 #print "deleting site", sdp.site
699 relation.delete()
700 else:
701 existing_dest_objs.append(getattr(relation, foreign_attrname))
702
703 for dest_obj in selected_objs:
704 if dest_obj not in existing_dest_objs:
705 #print "adding site", site
706 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
707 relation = relation_class(**kwargs)
708 relation.save()
709
Tony Mack93d1b032014-12-08 16:43:02 -0500710 def save(self, commit=True):
711 controller = super(ControllerAdminForm, self).save(commit=False)
712 if commit:
713 controller.save()
Tony Mack528d4222014-12-05 17:13:08 -0500714
Tony Mack93d1b032014-12-08 16:43:02 -0500715 if controller.pk:
716 # save_m2m() doesn't seem to work with 'through' relations. So we
717 # create/destroy the through models ourselves. There has to be
718 # a better way...
Tony Mack528d4222014-12-05 17:13:08 -0500719 self.manipulate_m2m_objs(controller, self.cleaned_data['site_deployments'], controller.controllersitedeployments.all(), ControllerSiteDeployments, "controller", "site_deployment")
720
Tony Mack93d1b032014-12-08 16:43:02 -0500721 self.save_m2m()
Tony Mack528d4222014-12-05 17:13:08 -0500722
Tony Mack93d1b032014-12-08 16:43:02 -0500723 return controller
Tony Mack528d4222014-12-05 17:13:08 -0500724
725class ControllerAdmin(PlanetStackBaseAdmin):
726 model = Controller
Tony Mack93d1b032014-12-08 16:43:02 -0500727 fieldList = ['name', 'version', 'backend_type', 'auth_url', 'admin_user', 'admin_tenant','admin_password']
728 #fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
729 inlines = [ControllerSiteDeploymentsInline] # ,ControllerImagesInline]
Tony Mack528d4222014-12-05 17:13:08 -0500730 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
731 list_display_links = ('backend_status_icon', 'name', )
732 readonly_fields = ('backend_status_text',)
733
734 user_readonly_fields = []
735
736 def get_form(self, request, obj=None, **kwargs):
Tony Mack93d1b032014-12-08 16:43:02 -0500737 print self.fieldsets
Tony Mack528d4222014-12-05 17:13:08 -0500738 if request.user.isReadOnlyUser():
739 kwargs["form"] = ControllerAdminROForm
740 else:
741 kwargs["form"] = ControllerAdminForm
Tony Mack93d1b032014-12-08 16:43:02 -0500742 adminForm = super(ControllerAdmin,self).get_form(request, obj, **kwargs)
Tony Mack528d4222014-12-05 17:13:08 -0500743
744 # from stackexchange: pass the request object into the form
745
746 class AdminFormMetaClass(adminForm):
747 def __new__(cls, *args, **kwargs):
748 kwargs['request'] = request
749 return adminForm(*args, **kwargs)
750
751 return AdminFormMetaClass
752
Siobhan Tullyce652d02013-10-08 21:52:35 -0400753class ServiceAttrAsTabInline(PlStackTabularInline):
754 model = ServiceAttribute
755 fields = ['name','value']
756 extra = 0
757 suit_classes = 'suit-tab suit-tab-serviceattrs'
758
Siobhan Tullyce652d02013-10-08 21:52:35 -0400759class ServiceAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700760 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
761 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700762 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500763 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
764 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700765 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500766
767 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500768
769 suit_form_tabs =(('general', 'Service Details'),
770 ('slices','Slices'),
771 ('serviceattrs','Additional Attributes'),
772 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400773
Tony Mack0553f282013-06-10 22:54:50 -0400774class SiteAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700775 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400776 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500777 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400778 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400779 ]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400780 suit_form_tabs =(('general', 'Site Details'),
781 ('users','Users'),
Siobhan Tully2d95e482013-09-06 10:56:06 -0400782 ('siteprivileges','Privileges'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400783 ('deployments','Deployments'),
784 ('slices','Slices'),
Tony Mack93d1b032014-12-08 16:43:02 -0500785 #('nodes','Nodes'),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400786 ('tags','Tags'),
787 )
Scott Baker40c00762014-08-21 16:55:59 -0700788 readonly_fields = ['backend_status_text', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500789
790 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500791
Scott Baker63d1a552014-08-21 15:19:07 -0700792 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
793 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400794 filter_horizontal = ('deployments',)
Tony Mack93d1b032014-12-08 16:43:02 -0500795 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteDeploymentsInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400796 search_fields = ['name']
797
Tony Mack04062832013-05-10 08:22:44 -0400798 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500799 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400800
Tony Mack5cd13202013-05-01 21:48:38 -0400801 def get_formsets(self, request, obj=None):
802 for inline in self.get_inline_instances(request, obj):
803 # hide MyInline in the add view
804 if obj is None:
805 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400806 if isinstance(inline, SliverInline):
807 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400808 yield inline.get_formset(request, obj)
809
Scott Baker545db2a2013-12-09 18:44:43 -0800810 def accountLink(self, obj):
811 link_obj = obj.accounts.all()
812 if link_obj:
813 reverse_path = "admin:core_account_change"
814 url = reverse(reverse_path, args =(link_obj[0].id,))
815 return "<a href='%s'>%s</a>" % (url, "view billing details")
816 else:
817 return "no billing data for this site"
818 accountLink.allow_tags = True
819 accountLink.short_description = "Billing"
820
Tony Mack332ee1d2014-02-04 15:33:45 -0500821 def save_model(self, request, obj, form, change):
822 # update openstack connection to use this site/tenant
823 obj.save_by_user(request.user)
824
825 def delete_model(self, request, obj):
826 obj.delete_by_user(request.user)
827
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500828
Tony Mack9bcbe4f2013-04-29 08:13:27 -0400829class SitePrivilegeAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700830 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400831 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500832 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400833 ]
Scott Baker40c00762014-08-21 16:55:59 -0700834 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700835 list_display = ('backend_status_icon', 'user', 'site', 'role')
836 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500837 user_readonly_fields = fieldList
838 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400839
Tony Mackc2835a92013-05-28 09:18:49 -0400840 def formfield_for_foreignkey(self, db_field, request, **kwargs):
841 if db_field.name == 'site':
842 if not request.user.is_admin:
843 # only show sites where user is an admin or pi
844 sites = set()
845 for site_privilege in SitePrivilege.objects.filer(user=request.user):
846 if site_privilege.role.role_type in ['admin', 'pi']:
847 sites.add(site_privilege.site)
848 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
849
850 if db_field.name == 'user':
851 if not request.user.is_admin:
852 # only show users from sites where caller has admin or pi role
853 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
854 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
855 sites = [site_privilege.site for site_privilege in site_privileges]
856 site_privileges = SitePrivilege.objects.filter(site__in=sites)
857 emails = [site_privilege.user.email for site_privilege in site_privileges]
858 users = User.objects.filter(email__in=emails)
859 kwargs['queryset'] = users
860
861 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
862
Tony Mack04062832013-05-10 08:22:44 -0400863 def queryset(self, request):
864 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400865 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400866 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500867 #if not request.user.is_admin:
868 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
869 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
870 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
871 # sites = Site.objects.filter(login_base__in=login_bases)
872 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400873 return qs
874
Siobhan Tullyce652d02013-10-08 21:52:35 -0400875class SliceForm(forms.ModelForm):
876 class Meta:
877 model = Slice
878 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700879 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400880 }
881
Tony Mack2cbd3802014-09-29 16:10:52 -0400882 def clean(self):
883 cleaned_data = super(SliceForm, self).clean()
884 name = cleaned_data.get('name')
Scott Baker6efad462014-10-06 23:09:59 -0700885 site = cleaned_data.get('site')
Tony Mack585cb192014-10-22 12:54:19 -0400886 slice_id = self.instance.id
887 if not site and slice_id:
888 site = Slice.objects.get(id=slice_id).site
Scott Baker6efad462014-10-06 23:09:59 -0700889 if (not isinstance(site,Site)):
890 # previous code indicates 'site' could be a site_id and not a site?
891 site = Slice.objects.get(id=site.id)
Tony Mack2cbd3802014-09-29 16:10:52 -0400892 if not name.startswith(site.login_base):
893 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
894 return cleaned_data
895
Tony Mack336e0f92014-11-30 15:53:08 -0500896class ControllerSlicesInline(PlStackTabularInline):
897 model = ControllerSlices
Scott Bakerc4efdc72014-10-15 16:54:04 -0700898 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -0500899 verbose_name = "Controller Slices"
900 verbose_name_plural = "Controller Slices"
Scott Bakerc4efdc72014-10-15 16:54:04 -0700901 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -0500902 fields = ['backend_status_icon', 'controller', 'tenant_id']
Scott Bakerc4efdc72014-10-15 16:54:04 -0700903 readonly_fields = ('backend_status_icon', )
904
Tony Mack2bd5b412013-06-11 21:05:06 -0400905class SliceAdmin(PlanetStackBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400906 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400907 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500908 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700909 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400910 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
911 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400912 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Mack336e0f92014-11-30 15:53:08 -0500913 admin_inlines = [ControllerSlicesInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400914
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500915 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400916
Scott Bakerc4efdc72014-10-15 16:54:04 -0700917 @property
918 def suit_form_tabs(self):
919 tabs =[('general', 'Slice Details'),
920 ('slicenetworks','Networks'),
921 ('sliceprivileges','Privileges'),
922 ('slivers','Slivers'),
923 ('tags','Tags'),
924 ('reservations','Reservations'),
925 ]
926
927 request=getattr(_thread_locals, "request", None)
928 if request and request.user.is_admin:
929 tabs.append( ('admin-only', 'Admin-Only') )
930
931 return tabs
Tony Mack7b8505a2014-10-22 11:54:29 -0400932
933 def add_view(self, request, form_url='', extra_context=None):
934 # revert to default read-only fields
935 self.readonly_fields = ('backend_status_text',)
936 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
937
938 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack7b8505a2014-10-22 11:54:29 -0400939 # cannot change the site of an existing slice so make the site field read only
940 if object_id:
941 self.readonly_fields = ('backend_status_text','site')
942 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400943
Scott Baker510fdbb2014-08-05 17:19:24 -0700944 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700945 deployment_nodes = []
946 for node in Node.objects.all():
Sapan Bhatia48208cd2014-12-22 01:37:26 -0500947 deployment_nodes.append( (node.site_deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -0700948
Scott Baker7a61dc42014-09-02 17:08:20 -0700949 deployment_flavors = []
950 for flavor in Flavor.objects.all():
951 for deployment in flavor.deployments.all():
952 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
953
Tony Mack93d1b032014-12-08 16:43:02 -0500954 deployment_images = []
Scott Baker93e80cd2014-09-09 09:58:49 -0700955 for image in Image.objects.all():
Tony Mack93d1b032014-12-08 16:43:02 -0500956 for deployment_image in image.imagedeployments.all():
Scott Bakera6a0c772014-12-22 17:35:34 -0800957 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Baker93e80cd2014-09-09 09:58:49 -0700958
Tony Mackec23b992014-09-02 21:18:45 -0400959 site_login_bases = []
960 for site in Site.objects.all():
Scott Baker93e80cd2014-09-09 09:58:49 -0700961 site_login_bases.append((site.id, site.login_base))
962
Scott Baker510fdbb2014-08-05 17:19:24 -0700963 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700964 context["deployment_flavors"] = deployment_flavors
Scott Baker93e80cd2014-09-09 09:58:49 -0700965 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400966 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700967 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
968
Tony Mackc2835a92013-05-28 09:18:49 -0400969 def formfield_for_foreignkey(self, db_field, request, **kwargs):
970 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500971 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400972 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
Scott Baker40c00762014-08-21 16:55:59 -0700973
Tony Mackc2835a92013-05-28 09:18:49 -0400974 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
975
Tony Mack04062832013-05-10 08:22:44 -0400976 def queryset(self, request):
977 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500978 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400979
Tony Mack79748612013-05-01 14:52:03 -0400980 def get_formsets(self, request, obj=None):
981 for inline in self.get_inline_instances(request, obj):
982 # hide MyInline in the add view
983 if obj is None:
984 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400985 if isinstance(inline, SliverInline):
986 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400987 yield inline.get_formset(request, obj)
988
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400989class SlicePrivilegeAdmin(PlanetStackBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400990 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700991 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400992 ]
Scott Baker40c00762014-08-21 16:55:59 -0700993 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700994 list_display = ('backend_status_icon', 'user', 'slice', 'role')
995 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400996
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500997 user_readonly_fields = ['user', 'slice', 'role']
998 user_readonly_inlines = []
999
Tony Mackc2835a92013-05-28 09:18:49 -04001000 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1001 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001002 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001003
1004 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001005 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001006
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001007 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001008
Tony Mack04062832013-05-10 08:22:44 -04001009 def queryset(self, request):
1010 # admins can see all memberships. Users can only see memberships of
1011 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001012 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001013
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001014 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001015 # update openstack connection to use this site/tenant
1016 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001017 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001018 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001019 obj.save()
1020
1021 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001022 # update openstack connection to use this site/tenant
1023 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001024 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001025 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001026 obj.delete()
1027
Siobhan Tully567e3e62013-06-21 18:03:16 -04001028
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001029class ImageAdmin(PlanetStackBaseAdmin):
1030
Scott Baker36f50872014-08-21 13:01:25 -07001031 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001032 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001033 'classes': ['suit-tab suit-tab-general']})
1034 ]
Scott Baker40c00762014-08-21 16:55:59 -07001035 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001036
Tony Mack336e0f92014-11-30 15:53:08 -05001037 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001038
Tony Mack336e0f92014-11-30 15:53:08 -05001039 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001040
Tony Mack32e1ce32014-05-07 13:29:41 -04001041 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001042
Scott Baker63d1a552014-08-21 15:19:07 -07001043 list_display = ['backend_status_icon', 'name']
1044 list_display_links = ('backend_status_icon', 'name', )
1045
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001046class NodeForm(forms.ModelForm):
1047 class Meta:
1048 widgets = {
1049 'site': LinkedSelect,
1050 'deployment': LinkedSelect
1051 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001052
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001053class NodeAdmin(PlanetStackBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001054 form = NodeForm
Tony Mack93d1b032014-12-08 16:43:02 -05001055 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001056 list_display_links = ('backend_status_icon', 'name', )
Tony Mack93d1b032014-12-08 16:43:02 -05001057 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001058
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001059 inlines = [TagInline,SliverInline]
Tony Mack93d1b032014-12-08 16:43:02 -05001060 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001061 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001062
Tony Mack93d1b032014-12-08 16:43:02 -05001063 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001064 user_readonly_inlines = [TagInline,SliverInline]
1065
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001066 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'),('tags','Tags'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001067
Siobhan Tully567e3e62013-06-21 18:03:16 -04001068
Tony Mackd90cdbf2013-04-16 22:48:40 -04001069class SliverForm(forms.ModelForm):
1070 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001071 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001072 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001073 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001074 widgets = {
1075 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001076 'instance_name': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001077 'slice': LinkedSelect,
Tony Mackbf6aa302014-12-26 13:38:02 -05001078 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001079 'node': LinkedSelect,
1080 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001081 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001082
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001083class TagAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001084 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1085 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001086 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1087 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001088
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001089class SliverAdmin(PlanetStackBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001090 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001091 fieldsets = [
Tony Mackbf6aa302014-12-26 13:38:02 -05001092 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image', ], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001093 ]
Scott Baker40c00762014-08-21 16:55:59 -07001094 readonly_fields = ('backend_status_text', )
Tony Mackbf6aa302014-12-26 13:38:02 -05001095 list_display = ['backend_status_icon', 'ip', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Baker63d1a552014-08-21 15:19:07 -07001096 list_display_links = ('backend_status_icon', 'ip',)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001097
1098 suit_form_tabs =(('general', 'Sliver Details'),
1099 ('tags','Tags'),
1100 )
1101
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001102 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001103
Tony Mackbf6aa302014-12-26 13:38:02 -05001104 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001105
Tony Mackc2835a92013-05-28 09:18:49 -04001106 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1107 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001108 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001109
1110 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1111
Tony Mack04062832013-05-10 08:22:44 -04001112 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001113 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001114 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001115 return Sliver.select_by_user(request.user)
1116
Tony Mack04062832013-05-10 08:22:44 -04001117
Tony Mack1d6b85f2013-05-07 18:49:14 -04001118 def get_formsets(self, request, obj=None):
1119 # make some fields read only if we are updating an existing record
1120 if obj == None:
Scott Baker36f50872014-08-21 13:01:25 -07001121 #self.readonly_fields = ('ip', 'instance_name')
Scott Bakerfbb45862014-10-17 16:27:23 -07001122 self.readonly_fields = ('backend_status_text',)
Tony Mack1d6b85f2013-05-07 18:49:14 -04001123 else:
Scott Bakerfbb45862014-10-17 16:27:23 -07001124 self.readonly_fields = ('backend_status_text',)
Scott Baker36f50872014-08-21 13:01:25 -07001125 #self.readonly_fields = ('ip', 'instance_name', 'slice', 'image', 'key')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001126
1127 for inline in self.get_inline_instances(request, obj):
1128 # hide MyInline in the add view
1129 if obj is None:
1130 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001131 if isinstance(inline, SliverInline):
1132 inline.model.caller = request.user
1133 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001134
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001135 #def save_model(self, request, obj, form, change):
1136 # # update openstack connection to use this site/tenant
1137 # auth = request.session.get('auth', {})
1138 # auth['tenant'] = obj.slice.name
1139 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1140 # obj.creator = request.user
1141 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001142
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001143 #def delete_model(self, request, obj):
1144 # # update openstack connection to use this site/tenant
1145 # auth = request.session.get('auth', {})
1146 # auth['tenant'] = obj.slice.name
1147 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1148 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001149
Siobhan Tully53437282013-04-26 19:30:27 -04001150class UserCreationForm(forms.ModelForm):
1151 """A form for creating new users. Includes all the required
1152 fields, plus a repeated password."""
1153 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1154 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1155
1156 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001157 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001158 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001159
1160 def clean_password2(self):
1161 # Check that the two password entries match
1162 password1 = self.cleaned_data.get("password1")
1163 password2 = self.cleaned_data.get("password2")
1164 if password1 and password2 and password1 != password2:
1165 raise forms.ValidationError("Passwords don't match")
1166 return password2
1167
1168 def save(self, commit=True):
1169 # Save the provided password in hashed format
1170 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001171 user.password = self.cleaned_data["password1"]
1172 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001173 if commit:
1174 user.save()
1175 return user
1176
Siobhan Tully567e3e62013-06-21 18:03:16 -04001177
Siobhan Tully53437282013-04-26 19:30:27 -04001178class UserChangeForm(forms.ModelForm):
1179 """A form for updating users. Includes all the fields on
1180 the user, but replaces the password field with admin's
1181 password hash display field.
1182 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001183 password = ReadOnlyPasswordHashField(label='Password',
1184 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001185
1186 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001187 model = User
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001188 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001189
1190 def clean_password(self):
1191 # Regardless of what the user provides, return the initial value.
1192 # This is done here, rather than on the field, because the
1193 # field does not have access to the initial value
1194 return self.initial["password"]
1195
Scott Baker2c3cb642014-05-19 17:55:56 -07001196class UserDashboardViewInline(PlStackTabularInline):
1197 model = UserDashboardView
1198 extra = 0
1199 suit_classes = 'suit-tab suit-tab-dashboards'
1200 fields = ['user', 'dashboardView', 'order']
1201
Scott Baker86c83ab2014-10-03 13:10:47 -07001202class UserAdmin(PermissionCheckingAdminMixin, UserAdmin):
1203 # Note: Make sure PermissionCheckingAdminMixin is listed before
1204 # admin.ModelAdmin in the class declaration.
1205
Siobhan Tully53437282013-04-26 19:30:27 -04001206 class Meta:
1207 app_label = "core"
1208
1209 # The forms to add and change user instances
1210 form = UserChangeForm
1211 add_form = UserCreationForm
1212
1213 # The fields to be used in displaying the User model.
1214 # These override the definitions on the base UserAdmin
1215 # that reference specific fields on auth.User.
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001216 list_display = ('email', 'firstname', 'lastname', 'site', 'last_login')
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001217 list_filter = ('site',)
Tony Mack336e0f92014-11-30 15:53:08 -05001218 inlines = [SlicePrivilegeInline,SitePrivilegeInline,ControllerPrivilegeInline,UserDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001219
Scott Bakercbfb6002014-10-03 00:32:37 -07001220 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001221 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1222
Siobhan Tully53437282013-04-26 19:30:27 -04001223 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001224 ('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 -04001225 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Scott Baker2c3cb642014-05-19 17:55:56 -07001226 #('Dashboard Views', {'fields': ('dashboards',), 'classes':['suit-tab suit-tab-dashboards']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001227 #('Important dates', {'fields': ('last_login',)}),
1228 )
1229 add_fieldsets = (
1230 (None, {
1231 'classes': ('wide',),
Scott Baker0a5633b2014-10-06 17:51:20 -07001232 'fields': ('email', 'firstname', 'lastname', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001233 ),
1234 )
Scott Baker40c00762014-08-21 16:55:59 -07001235 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001236 search_fields = ('email',)
1237 ordering = ('email',)
1238 filter_horizontal = ()
1239
Scott Baker3ca51f62014-05-23 12:05:11 -07001240 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001241
Scott Baker0a5633b2014-10-06 17:51:20 -07001242 @property
1243 def suit_form_tabs(self):
1244 if getattr(_thread_locals, "obj", None) is None:
1245 return []
1246 else:
1247 return (('general','Login Details'),
1248 ('contact','Contact Information'),
1249 ('sliceprivileges','Slice Privileges'),
1250 ('siteprivileges','Site Privileges'),
Tony Mack336e0f92014-11-30 15:53:08 -05001251 ('controllerprivileges','Controller Privileges'),
Scott Baker0a5633b2014-10-06 17:51:20 -07001252 ('dashboards','Dashboard Views'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001253
Tony Mackc2835a92013-05-28 09:18:49 -04001254 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1255 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001256 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001257
1258 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1259
Tony Mack5b061472014-02-04 07:57:10 -05001260 def queryset(self, request):
1261 return User.select_by_user(request.user)
1262
Scott Bakera6a0c772014-12-22 17:35:34 -08001263class ControllerDashboardViewInline(PlStackTabularInline):
1264 model = ControllerDashboardView
Scott Bakereef5a6b2014-12-19 16:41:12 -08001265 extra = 0
1266 fields = ["controller", "url"]
1267 suit_classes = 'suit-tab suit-tab-controllers'
1268
Scott Baker2c3cb642014-05-19 17:55:56 -07001269class DashboardViewAdmin(PlanetStackBaseAdmin):
1270 fieldsets = [('Dashboard View Details',
Scott Baker40c00762014-08-21 16:55:59 -07001271 {'fields': ['backend_status_text', 'name', 'url'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001272 'classes': ['suit-tab suit-tab-general']})
1273 ]
Scott Baker40c00762014-08-21 16:55:59 -07001274 readonly_fields = ('backend_status_text', )
Scott Bakera6a0c772014-12-22 17:35:34 -08001275 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001276
Scott Bakereef5a6b2014-12-19 16:41:12 -08001277 suit_form_tabs =(('general','Dashboard View Details'),
1278 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001279
Scott Baker0165fac2014-01-13 11:49:26 -08001280class ServiceResourceInline(PlStackTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001281 model = ServiceResource
1282 extra = 0
1283
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001284class ServiceClassAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001285 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1286 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001287 inlines = [ServiceResourceInline]
1288
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001289 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1290 user_readonly_inlines = []
1291
Scott Baker0165fac2014-01-13 11:49:26 -08001292class ReservedResourceInline(PlStackTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001293 model = ReservedResource
1294 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001295 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001296
1297 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1298 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1299
1300 if db_field.name == 'resource':
1301 # restrict resources to those that the slice's service class allows
1302 if request._slice is not None:
1303 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1304 if len(field.queryset) > 0:
1305 field.initial = field.queryset.all()[0]
1306 else:
1307 field.queryset = field.queryset.none()
1308 elif db_field.name == 'sliver':
1309 # restrict slivers to those that belong to the slice
1310 if request._slice is not None:
1311 field.queryset = field.queryset.filter(slice = request._slice)
1312 else:
1313 field.queryset = field.queryset.none()
1314
1315 return field
1316
Tony Mack5b061472014-02-04 07:57:10 -05001317 def queryset(self, request):
1318 return ReservedResource.select_by_user(request.user)
1319
Scott Baker133c9212013-05-17 09:09:11 -07001320class ReservationChangeForm(forms.ModelForm):
1321 class Meta:
1322 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001323 widgets = {
1324 'slice' : LinkedSelect
1325 }
Scott Baker133c9212013-05-17 09:09:11 -07001326
1327class ReservationAddForm(forms.ModelForm):
1328 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1329 refresh = forms.CharField(widget=forms.HiddenInput())
1330
1331 class Media:
1332 css = {'all': ('planetstack.css',)} # .field-refresh { display: none; }
1333
1334 def clean_slice(self):
1335 slice = self.cleaned_data.get("slice")
1336 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1337 if len(x) == 0:
1338 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1339 return slice
1340
1341 class Meta:
1342 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001343 widgets = {
1344 'slice' : LinkedSelect
1345 }
1346
Scott Baker133c9212013-05-17 09:09:11 -07001347
1348class ReservationAddRefreshForm(ReservationAddForm):
1349 """ This form is displayed when the Reservation Form receives an update
1350 from the Slice dropdown onChange handler. It doesn't validate the
1351 data and doesn't save the data. This will cause the form to be
1352 redrawn.
1353 """
1354
Scott Baker8737e5f2013-05-17 09:35:32 -07001355 """ don't validate anything other than slice """
1356 dont_validate_fields = ("startTime", "duration")
1357
Scott Baker133c9212013-05-17 09:09:11 -07001358 def full_clean(self):
1359 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001360
1361 for fieldname in self.dont_validate_fields:
1362 if fieldname in self._errors:
1363 del self._errors[fieldname]
1364
Scott Baker133c9212013-05-17 09:09:11 -07001365 return result
1366
1367 """ don't save anything """
1368 def is_valid(self):
1369 return False
1370
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001371class ReservationAdmin(PlanetStackBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001372 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001373 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001374 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001375 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001376 form = ReservationAddForm
1377
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001378 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1379
1380 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001381 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001382
Scott Baker133c9212013-05-17 09:09:11 -07001383 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001384 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001385 request._refresh = False
1386 request._slice = None
1387 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001388 # "refresh" will be set to "1" if the form was submitted due to
1389 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001390 if request.POST.get("refresh","1") == "1":
1391 request._refresh = True
1392 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001393
1394 # Keep track of the slice that was selected, so the
1395 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001396 request._slice = request.POST.get("slice",None)
1397 if (request._slice is not None):
1398 request._slice = Slice.objects.get(id=request._slice)
1399
1400 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1401 return result
1402
Scott Bakeracd45142013-05-19 16:19:16 -07001403 def changelist_view(self, request, extra_context = None):
1404 timezone.activate(request.user.timezone)
1405 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1406
Scott Baker133c9212013-05-17 09:09:11 -07001407 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001408 request._obj_ = obj
1409 if obj is not None:
1410 # For changes, set request._slice to the slice already set in the
1411 # object.
1412 request._slice = obj.slice
1413 self.form = ReservationChangeForm
1414 else:
1415 if getattr(request, "_refresh", False):
1416 self.form = ReservationAddRefreshForm
1417 else:
1418 self.form = ReservationAddForm
1419 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1420
Scott Baker133c9212013-05-17 09:09:11 -07001421 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001422 if (obj is not None):
1423 # Prevent slice from being changed after the reservation has been
1424 # created.
1425 return ['slice']
1426 else:
Scott Baker133c9212013-05-17 09:09:11 -07001427 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001428
Tony Mack5b061472014-02-04 07:57:10 -05001429 def queryset(self, request):
1430 return Reservation.select_by_user(request.user)
1431
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001432class NetworkParameterTypeAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001433 list_display = ("backend_status_icon", "name", )
1434 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001435 user_readonly_fields = ['name']
1436 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001437
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001438class RouterAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001439 list_display = ("backend_status_icon", "name", )
1440 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001441 user_readonly_fields = ['name']
1442 user_readonly_inlines = []
1443
Scott Baker0165fac2014-01-13 11:49:26 -08001444class RouterInline(PlStackTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001445 model = Router.networks.through
1446 extra = 0
1447 verbose_name_plural = "Routers"
1448 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001449 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001450
Scott Bakerb27b62c2014-08-15 16:29:16 -07001451class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001452 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001453 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001454 verbose_name_plural = "Parameters"
1455 verbose_name = "Parameter"
1456 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001457 fields = ['backend_status_icon', 'parameter', 'value']
1458 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001459
Scott Baker0165fac2014-01-13 11:49:26 -08001460class NetworkSliversInline(PlStackTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001461 fields = ['backend_status_icon', 'network','sliver','ip']
1462 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001463 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001464 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001465 extra = 0
1466 verbose_name_plural = "Slivers"
1467 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001468 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001469
Scott Baker0165fac2014-01-13 11:49:26 -08001470class NetworkSlicesInline(PlStackTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001471 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001472 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001473 extra = 0
1474 verbose_name_plural = "Slices"
1475 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001476 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001477 fields = ['backend_status_icon', 'network','slice']
1478 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001479
Tony Mack336e0f92014-11-30 15:53:08 -05001480class ControllerNetworksInline(PlStackTabularInline):
1481 model = ControllerNetworks
Scott Bakerfbb45862014-10-17 16:27:23 -07001482 extra = 0
Tony Mack336e0f92014-11-30 15:53:08 -05001483 verbose_name_plural = "Controller Networks"
1484 verbose_name = "Controller Network"
Scott Bakerfbb45862014-10-17 16:27:23 -07001485 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack336e0f92014-11-30 15:53:08 -05001486 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Bakerfbb45862014-10-17 16:27:23 -07001487 readonly_fields = ('backend_status_icon', )
1488
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001489class NetworkForm(forms.ModelForm):
1490 class Meta:
1491 model = Network
1492 widgets = {
1493 'topologyParameters': UploadTextareaWidget,
1494 'controllerParameters': UploadTextareaWidget,
1495 }
1496
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001497class NetworkAdmin(PlanetStackBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001498 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1499 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001500 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001501
Scott Bakerd7d2a392013-08-06 08:57:30 -07001502 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Mack336e0f92014-11-30 15:53:08 -05001503 admin_inlines = [ControllerNetworksInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001504
Scott Baker9f6b8ed2014-11-17 23:44:03 -08001505 form=NetworkForm
1506
Siobhan Tully2d95e482013-09-06 10:56:06 -04001507 fieldsets = [
Scott Baker40248712014-11-17 16:04:45 -08001508 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet'],
1509 'classes':['suit-tab suit-tab-general']}),
Scott Baker7b6722e2014-11-17 16:18:00 -08001510 (None, {'fields': ['topologyParameters', 'controllerUrl', 'controllerParameters'],
Scott Baker40248712014-11-17 16:04:45 -08001511 'classes':['suit-tab suit-tab-sdn']}),
1512 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001513
Scott Baker40c00762014-08-21 16:55:59 -07001514 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001515 user_readonly_fields = ['name','template','ports','labels','owner','guaranteedBandwidth', 'permitAllSlices','permittedSlices','network_id','router_id','subnet_id','subnet']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001516
Scott Bakerfbb45862014-10-17 16:27:23 -07001517 @property
1518 def suit_form_tabs(self):
1519 tabs=[('general','Network Details'),
Scott Baker40248712014-11-17 16:04:45 -08001520 ('sdn', 'SDN Configuration'),
Scott Bakerfbb45862014-10-17 16:27:23 -07001521 ('netparams', 'Parameters'),
1522 ('networkslivers','Slivers'),
1523 ('networkslices','Slices'),
1524 ('routers','Routers'),
1525 ]
1526
1527 request=getattr(_thread_locals, "request", None)
1528 if request and request.user.is_admin:
1529 tabs.append( ('admin-only', 'Admin-Only') )
1530
1531 return tabs
1532
1533
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001534class NetworkTemplateAdmin(PlanetStackBaseAdmin):
Scott Baker81fa17f2015-01-03 12:03:38 -08001535 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001536 list_display_links = ('backend_status_icon', 'name', )
Scott Baker81fa17f2015-01-03 12:03:38 -08001537 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001538 user_readonly_inlines = []
Scott Baker40248712014-11-17 16:04:45 -08001539 fieldsets = [
Scott Baker81fa17f2015-01-03 12:03:38 -08001540 (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 -08001541 'classes':['suit-tab suit-tab-general']}),]
1542 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001543
Scott Baker37b47902014-09-02 14:37:41 -07001544class FlavorAdmin(PlanetStackBaseAdmin):
1545 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1546 list_display_links = ("backend_status_icon", "name")
1547 user_readonly_fields = ("name", "flavor")
1548 fields = ("name", "description", "flavor", "order", "default")
1549
Tony Mack31c2b8f2013-04-26 20:01:42 -04001550# register a signal that caches the user's credentials when they log in
1551def cache_credentials(sender, user, request, **kwds):
1552 auth = {'username': request.POST['username'],
1553 'password': request.POST['password']}
1554 request.session['auth'] = auth
1555user_logged_in.connect(cache_credentials)
1556
Scott Baker15cddfa2013-12-09 13:45:19 -08001557def dollar_field(fieldName, short_description):
1558 def newFunc(self, obj):
1559 try:
1560 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1561 except:
1562 x=getattr(obj, fieldName, 0.0)
1563 return x
1564 newFunc.short_description = short_description
1565 return newFunc
1566
1567def right_dollar_field(fieldName, short_description):
1568 def newFunc(self, obj):
1569 try:
1570 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1571 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1572 except:
1573 x=getattr(obj, fieldName, 0.0)
1574 return x
1575 newFunc.short_description = short_description
1576 newFunc.allow_tags = True
1577 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001578
Scott Baker0165fac2014-01-13 11:49:26 -08001579class InvoiceChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001580 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001581 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001582 verbose_name_plural = "Charges"
1583 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001584 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001585 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1586 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1587 can_delete = False
1588 max_num = 0
1589
1590 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001591
1592class InvoiceAdmin(admin.ModelAdmin):
1593 list_display = ("date", "account")
1594
1595 inlines = [InvoiceChargeInline]
1596
Scott Baker9cb88a22013-12-09 18:56:00 -08001597 fields = ["date", "account", "dollar_amount"]
1598 readonly_fields = ["date", "account", "dollar_amount"]
1599
1600 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001601
Scott Baker0165fac2014-01-13 11:49:26 -08001602class InvoiceInline(PlStackTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001603 model = Invoice
1604 extra = 0
1605 verbose_name_plural = "Invoices"
1606 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001607 fields = ["date", "dollar_amount"]
1608 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001609 suit_classes = 'suit-tab suit-tab-accountinvoice'
1610 can_delete=False
1611 max_num=0
1612
1613 dollar_amount = right_dollar_field("amount", "Amount")
1614
Scott Baker0165fac2014-01-13 11:49:26 -08001615class PendingChargeInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001616 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001617 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001618 verbose_name_plural = "Charges"
1619 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001620 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001621 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1622 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001623 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001624 can_delete=False
1625 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001626
1627 def queryset(self, request):
1628 qs = super(PendingChargeInline, self).queryset(request)
1629 qs = qs.filter(state="pending")
1630 return qs
1631
Scott Baker15cddfa2013-12-09 13:45:19 -08001632 dollar_amount = right_dollar_field("amount", "Amount")
1633
Scott Baker0165fac2014-01-13 11:49:26 -08001634class PaymentInline(PlStackTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001635 model=Payment
1636 extra = 1
1637 verbose_name_plural = "Payments"
1638 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001639 fields = ["date", "dollar_amount"]
1640 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001641 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001642 can_delete=False
1643 max_num=0
1644
1645 dollar_amount = right_dollar_field("amount", "Amount")
1646
Scott Baker43105042013-12-06 23:23:36 -08001647class AccountAdmin(admin.ModelAdmin):
1648 list_display = ("site", "balance_due")
1649
1650 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1651
1652 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001653 (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 -08001654
Scott Baker15cddfa2013-12-09 13:45:19 -08001655 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001656
1657 suit_form_tabs =(
1658 ('general','Account Details'),
1659 ('accountinvoice', 'Invoices'),
1660 ('accountpayments', 'Payments'),
1661 ('accountpendingcharges','Pending Charges'),
1662 )
1663
Scott Baker15cddfa2013-12-09 13:45:19 -08001664 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1665 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1666 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1667
Siobhan Tully53437282013-04-26 19:30:27 -04001668# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001669admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001670# ... and, since we're not using Django's builtin permissions,
1671# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001672#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001673
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001674#Do not show django evolution in the admin interface
1675from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001676#admin.site.unregister(Version)
1677#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001678
1679
1680# When debugging it is often easier to see all the classes, but for regular use
1681# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001682showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001683
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001684admin.site.register(Deployment, DeploymentAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001685admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001686admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001687admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001688admin.site.register(Service, ServiceAdmin)
smbakera3cf70c2013-06-27 02:01:41 -07001689admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001690admin.site.register(Network, NetworkAdmin)
1691admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001692admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001693admin.site.register(Account, AccountAdmin)
1694admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001695
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001696if True:
1697 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1698 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001699 #admin.site.register(PlanetStack)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001700 admin.site.register(Tag, TagAdmin)
Tony Mack336e0f92014-11-30 15:53:08 -05001701 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001702 admin.site.register(SiteRole)
1703 admin.site.register(SliceRole)
1704 admin.site.register(PlanetStackRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001705 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001706 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1707 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001708 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001709 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001710 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001711 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001712