blob: e6911be511fec18b99709133084315a7ed75ea38 [file] [log] [blame]
Siobhan Tully30fd4292013-05-10 08:59:56 -04001from core.models import Site
2from core.models import *
3from openstack.manager import OpenStackManager
Tony Macke59a7c82013-04-27 11:08:10 -04004
Tony Mack7130ac32013-03-22 21:58:00 -04005from django.contrib import admin
Siobhan Tully53437282013-04-26 19:30:27 -04006from django.contrib.auth.models import Group
Siobhan Tully4bc09f22013-04-10 21:15:21 -04007from django import forms
Tony Mackd90cdbf2013-04-16 22:48:40 -04008from django.utils.safestring import mark_safe
Tony Mack7130ac32013-03-22 21:58:00 -04009from django.contrib.auth.admin import UserAdmin
Scott Baker69e045d2014-11-17 23:44:03 -080010from django.contrib.admin.widgets import FilteredSelectMultiple, AdminTextareaWidget
Scott Baker1a6a3902014-10-03 00:32:37 -070011from django.contrib.auth.forms import ReadOnlyPasswordHashField, AdminPasswordChangeForm
Scott Bakeracd45142013-05-19 16:19:16 -070012from django.contrib.auth.signals import user_logged_in
13from django.utils import timezone
Siobhan Tullyde5450d2013-06-21 11:35:33 -040014from django.contrib.contenttypes import generic
Siobhan Tullybfd11dc2013-09-03 12:59:24 -040015from suit.widgets import LinkedSelect
Siobhan Tullycf04fb62014-01-11 11:25:57 -050016from django.core.exceptions import PermissionDenied
Tony Mack5817cb42015-02-16 19:54:24 -050017from django.core.urlresolvers import reverse, resolve, NoReverseMatch
Scott Baker69e045d2014-11-17 23:44:03 -080018from django.utils.encoding import force_text, python_2_unicode_compatible
19from django.utils.html import conditional_escape, format_html
Scott Bakera9b8f612015-02-26 20:42:11 -080020from django.utils.text import capfirst
Scott Baker69e045d2014-11-17 23:44:03 -080021from django.forms.utils import flatatt, to_current_timezone
Scott Baker92d22172014-10-21 21:03:08 -070022from cgi import escape as html_escape
Tony Mack7130ac32013-03-22 21:58:00 -040023
Scott Baker36f50872014-08-21 13:01:25 -070024import django_evolution
Scott Baker6a995352014-10-06 17:51:20 -070025import threading
26
27# thread locals necessary to work around a django-suit issue
28_thread_locals = threading.local()
Scott Baker36f50872014-08-21 13:01:25 -070029
Scott Bakere5f9d7d2015-02-10 18:24:20 -080030ICON_URLS = {"success": "/static/admin/img/icon_success.gif",
31 "clock": "/static/admin/img/icon_clock.gif",
32 "error": "/static/admin/img/icon_error.gif"}
33
34def backend_icon(obj):
35 (icon, tooltip) = obj.get_backend_icon()
36 icon_url = ICON_URLS.get(icon, "unknown")
37
38 if tooltip:
39 return '<span style="min-width:16px;" title="%s"><img src="%s"></span>' % (tooltip, icon_url)
Scott Baker40c00762014-08-21 16:55:59 -070040 else:
Scott Bakere5f9d7d2015-02-10 18:24:20 -080041 return '<span style="min-width:16px;"><img src="%s"></span>' % icon_url
Scott Baker40c00762014-08-21 16:55:59 -070042
43def backend_text(obj):
Scott Bakere5f9d7d2015-02-10 18:24:20 -080044 (icon, tooltip) = obj.get_backend_icon()
45 icon_url = ICON_URLS.get(icon, "unknown")
46
47 return '<img src="%s"> %s' % (icon_url, tooltip)
Scott Baker63d1a552014-08-21 15:19:07 -070048
Scott Baker69e045d2014-11-17 23:44:03 -080049class UploadTextareaWidget(AdminTextareaWidget):
50 def render(self, name, value, attrs=None):
51 if value is None:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -050052 value = ''
53 final_attrs = self.build_attrs(attrs, name=name)
54 return format_html('<input type="file" style="width: 0; height: 0" id="btn_upload_%s" onChange="uploadTextarea(event,\'%s\');">' \
55 '<button onClick="$(\'#btn_upload_%s\').click(); return false;">Upload</button>' \
56 '<br><textarea{0}>\r\n{1}</textarea>' % (attrs["id"], attrs["id"], attrs["id"]),
57 flatatt(final_attrs),
Scott Baker69e045d2014-11-17 23:44:03 -080058 force_text(value))
59
Scott Baker36f50872014-08-21 13:01:25 -070060class PlainTextWidget(forms.HiddenInput):
61 input_type = 'hidden'
62
63 def render(self, name, value, attrs=None):
64 if value is None:
65 value = ''
66 return mark_safe(str(value) + super(PlainTextWidget, self).render(name, value, attrs))
67
Scott Baker3a8aed62015-02-27 12:21:22 -080068class XOSAdminMixin(object):
Scott Baker1a6a3902014-10-03 00:32:37 -070069 # call save_by_user and delete_by_user instead of save and delete
Siobhan Tullycf04fb62014-01-11 11:25:57 -050070
71 def has_add_permission(self, request, obj=None):
72 return (not self.__user_is_readonly(request))
Scott Baker36f50872014-08-21 13:01:25 -070073
Siobhan Tullycf04fb62014-01-11 11:25:57 -050074 def has_delete_permission(self, request, obj=None):
75 return (not self.__user_is_readonly(request))
76
77 def save_model(self, request, obj, form, change):
78 if self.__user_is_readonly(request):
Scott Baker1a6a3902014-10-03 00:32:37 -070079 # this 'if' might be redundant if save_by_user is implemented right
Siobhan Tullycf04fb62014-01-11 11:25:57 -050080 raise PermissionDenied
Scott Baker1a6a3902014-10-03 00:32:37 -070081
82 obj.caller = request.user
83 # update openstack connection to use this site/tenant
84 obj.save_by_user(request.user)
85
86 def delete_model(self, request, obj):
87 obj.delete_by_user(request.user)
88
89 def save_formset(self, request, form, formset, change):
90 instances = formset.save(commit=False)
91 for instance in instances:
92 instance.save_by_user(request.user)
93
94 # BUG in django 1.7? Objects are not deleted by formset.save if
95 # commit is False. So let's delete them ourselves.
96 #
97 # code from forms/models.py save_existing_objects()
98 try:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -050099 forms_to_delete = formset.deleted_forms
100 except AttributeError:
Scott Baker1a6a3902014-10-03 00:32:37 -0700101 forms_to_delete = []
102 if formset.initial_forms:
103 for form in formset.initial_forms:
104 obj = form.instance
105 if form in forms_to_delete:
106 if obj.pk is None:
107 continue
108 formset.deleted_objects.append(obj)
109 obj.delete()
110
111 formset.save_m2m()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500112
113 def get_actions(self,request):
Scott Baker3a8aed62015-02-27 12:21:22 -0800114 actions = super(XOSAdminMixin,self).get_actions(request)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500115
116 if self.__user_is_readonly(request):
117 if 'delete_selected' in actions:
118 del actions['delete_selected']
119
120 return actions
121
Scott Bakerfbe0f652015-04-03 17:44:31 -0700122 def url_for_model_changelist(self, request, model):
123 # used in add_extra_context
124 return reverse('admin:%s_%s_changelist' % (model._meta.app_label, model._meta.model_name), current_app=model._meta.app_label)
125
Scott Bakera8ef2742015-04-02 22:32:40 -0700126 def add_extra_context(self, request, extra_context):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800127 # allow custom application breadcrumb url and name
128 extra_context["custom_app_breadcrumb_url"] = getattr(self, "custom_app_breadcrumb_url", None)
129 extra_context["custom_app_breadcrumb_name"] = getattr(self, "custom_app_breadcrumb_name", None)
Scott Bakerf85c0092015-04-02 22:07:18 -0700130 extra_context["custom_changelist_breadcrumb_url"] = getattr(self, "custom_changelist_breadcrumb_url", None)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800131
132 # for Service admins to render their Administration page
133 if getattr(self, "extracontext_registered_admins", False):
134 admins=[]
135 for model, model_admin in admin.site._registry.items():
136 if model == self.model:
137 continue
138 if model._meta.app_label == self.model._meta.app_label:
139 info = {"app": model._meta.app_label,
140 "model": model._meta.model_name,
141 "name": capfirst(model._meta.verbose_name_plural),
Scott Bakerfbe0f652015-04-03 17:44:31 -0700142 "url": self.url_for_model_changelist(request,model) }
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800143 admins.append(info)
144 extra_context["registered_admins"] = admins
145
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500146 def change_view(self,request,object_id, extra_context=None):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800147 extra_context = extra_context or {}
148
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500149 if self.__user_is_readonly(request):
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500150 if not hasattr(self, "readonly_save"):
151 # save the original readonly fields
152 self.readonly_save = self.readonly_fields
153 self.inlines_save = self.inlines
154 if hasattr(self, "user_readonly_fields"):
155 self.readonly_fields=self.user_readonly_fields
156 if hasattr(self, "user_readonly_inlines"):
157 self.inlines = self.user_readonly_inlines
158 else:
159 if hasattr(self, "readonly_save"):
160 # restore the original readonly fields
161 self.readonly_fields = self.readonly_save
162 if hasattr(self, "inlines_save"):
Scott Bakeraf73e102014-04-22 22:40:07 -0700163 self.inlines = self.inlines_save
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500164
Scott Bakera8ef2742015-04-02 22:32:40 -0700165 self.add_extra_context(request, extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800166
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500167 try:
Scott Baker3a8aed62015-02-27 12:21:22 -0800168 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500169 except PermissionDenied:
170 pass
171 if request.method == 'POST':
172 raise PermissionDenied
173 request.readonly = True
Scott Baker3a8aed62015-02-27 12:21:22 -0800174 return super(XOSAdminMixin, self).change_view(request, object_id, extra_context=extra_context)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500175
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800176 def changelist_view(self, request, extra_context = None):
177 extra_context = extra_context or {}
178
Scott Bakera8ef2742015-04-02 22:32:40 -0700179 self.add_extra_context(request, extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800180
Scott Baker3a8aed62015-02-27 12:21:22 -0800181 return super(XOSAdminMixin, self).changelist_view(request, extra_context=extra_context)
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800182
Scott Bakerfbe0f652015-04-03 17:44:31 -0700183 def add_view(self, request, extra_context = None):
184 extra_context = extra_context or {}
185
186 self.add_extra_context(request, extra_context)
187
188 return super(XOSAdminMixin, self).add_view(request, extra_context=extra_context)
189
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500190 def __user_is_readonly(self, request):
191 return request.user.isReadOnlyUser()
192
Scott Baker40c00762014-08-21 16:55:59 -0700193 def backend_status_text(self, obj):
194 return mark_safe(backend_text(obj))
Scott Baker36f50872014-08-21 13:01:25 -0700195
Scott Baker63d1a552014-08-21 15:19:07 -0700196 def backend_status_icon(self, obj):
Scott Baker40c00762014-08-21 16:55:59 -0700197 return mark_safe(backend_icon(obj))
Scott Baker63d1a552014-08-21 15:19:07 -0700198 backend_status_icon.short_description = ""
199
Scott Bakerdc4724c2014-11-05 09:05:38 -0800200 def get_form(self, request, obj=None, **kwargs):
Scott Baker9b3c1af2014-10-16 00:57:55 -0700201 # Save obj and request in thread-local storage, so suit_form_tabs can
202 # use it to determine whether we're in edit or add mode, and can
203 # determine whether the user is an admin.
204 _thread_locals.request = request
205 _thread_locals.obj = obj
Scott Baker3a8aed62015-02-27 12:21:22 -0800206 return super(XOSAdminMixin, self).get_form(request, obj, **kwargs)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700207
208 def get_inline_instances(self, request, obj=None):
Scott Baker3a8aed62015-02-27 12:21:22 -0800209 inlines = super(XOSAdminMixin, self).get_inline_instances(request, obj)
Scott Baker9b3c1af2014-10-16 00:57:55 -0700210
211 # inlines that should only be shown to an admin user
212 if request.user.is_admin:
213 for inline_class in getattr(self, "admin_inlines", []):
214 inlines.append(inline_class(self.model, self.admin_site))
215
216 return inlines
217
Scott Baker3a8aed62015-02-27 12:21:22 -0800218class ReadOnlyAwareAdmin(XOSAdminMixin, admin.ModelAdmin):
219 # Note: Make sure XOSAdminMixin is listed before
Scott Bakerf4aeedc2014-10-03 13:10:47 -0700220 # admin.ModelAdmin in the class declaration.
221
Scott Baker1a6a3902014-10-03 00:32:37 -0700222 pass
223
Scott Baker67db95f2015-02-18 15:50:11 -0800224class XOSBaseAdmin(ReadOnlyAwareAdmin):
Scott Baker1a6a3902014-10-03 00:32:37 -0700225 save_on_top = False
Scott Baker36f50872014-08-21 13:01:25 -0700226
Scott Bakere8859f92014-05-23 12:42:40 -0700227class SingletonAdmin (ReadOnlyAwareAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400228 def has_add_permission(self, request):
Scott Bakere8859f92014-05-23 12:42:40 -0700229 if not super(SingletonAdmin, self).has_add_permission(request):
230 return False
231
Siobhan Tullyce652d02013-10-08 21:52:35 -0400232 num_objects = self.model.objects.count()
233 if num_objects >= 1:
234 return False
235 else:
236 return True
237
Scott Bakera9b8f612015-02-26 20:42:11 -0800238class ServiceAppAdmin (SingletonAdmin):
Scott Bakerfbe38ee2015-02-27 12:12:14 -0800239 extracontext_registered_admins = True
Scott Bakera9b8f612015-02-26 20:42:11 -0800240
Scott Baker67db95f2015-02-18 15:50:11 -0800241class XOSTabularInline(admin.TabularInline):
Scott Baker86568322014-01-12 16:53:31 -0800242 def __init__(self, *args, **kwargs):
Scott Baker67db95f2015-02-18 15:50:11 -0800243 super(XOSTabularInline, self).__init__(*args, **kwargs)
Scott Baker86568322014-01-12 16:53:31 -0800244
245 # InlineModelAdmin as no get_fields() method, so in order to add
246 # the selflink field, we override __init__ to modify self.fields and
247 # self.readonly_fields.
248
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800249 self.setup_selflink()
250
Scott Baker874936e2014-01-13 18:15:34 -0800251 def get_change_url(self, model, id):
252 """ Get the URL to a change form in the admin for this model """
253 reverse_path = "admin:%s_change" % (model._meta.db_table)
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800254 try:
Scott Baker874936e2014-01-13 18:15:34 -0800255 url = reverse(reverse_path, args=(id,))
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800256 except NoReverseMatch:
Scott Baker874936e2014-01-13 18:15:34 -0800257 return None
258
259 return url
260
261 def setup_selflink(self):
262 if hasattr(self, "selflink_fieldname"):
263 """ self.selflink_model can be defined to punch through a relation
264 to its target object. For example, in SliceNetworkInline, set
265 selflink_model = "network", and the URL will lead to the Network
266 object instead of trying to bring up a change view of the
267 SliceNetwork object.
268 """
269 self.selflink_model = getattr(self.model,self.selflink_fieldname).field.rel.to
270 else:
271 self.selflink_model = self.model
272
273 url = self.get_change_url(self.selflink_model, 0)
274
275 # We don't have an admin for this object, so don't create the
276 # selflink.
277 if (url == None):
Scott Bakere2bbf7e2014-01-13 12:09:31 -0800278 return
279
Scott Baker874936e2014-01-13 18:15:34 -0800280 # Since we need to add "selflink" to the field list, we need to create
281 # self.fields if it is None.
Scott Baker0165fac2014-01-13 11:49:26 -0800282 if (self.fields is None):
283 self.fields = []
284 for f in self.model._meta.fields:
285 if f.editable and f.name != "id":
286 self.fields.append(f.name)
Scott Baker86568322014-01-12 16:53:31 -0800287
Scott Baker874936e2014-01-13 18:15:34 -0800288 self.fields = tuple(self.fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800289
Scott Baker874936e2014-01-13 18:15:34 -0800290 if self.readonly_fields is None:
291 self.readonly_fields = ()
Scott Baker86568322014-01-12 16:53:31 -0800292
Scott Baker874936e2014-01-13 18:15:34 -0800293 self.readonly_fields = tuple(self.readonly_fields) + ("selflink", )
Scott Baker86568322014-01-12 16:53:31 -0800294
295 def selflink(self, obj):
Scott Baker874936e2014-01-13 18:15:34 -0800296 if hasattr(self, "selflink_fieldname"):
297 obj = getattr(obj, self.selflink_fieldname)
298
Scott Baker86568322014-01-12 16:53:31 -0800299 if obj.id:
Scott Baker874936e2014-01-13 18:15:34 -0800300 url = self.get_change_url(self.selflink_model, obj.id)
301 return "<a href='%s'>Details</a>" % str(url)
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -0500302 else:
303 return "Not present"
Scott Baker86568322014-01-12 16:53:31 -0800304
305 selflink.allow_tags = True
306 selflink.short_description = "Details"
Siobhan Tullyd3515752013-06-21 16:34:53 -0400307
Scott Bakerb27b62c2014-08-15 16:29:16 -0700308 def has_add_permission(self, request):
309 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500310
311 def get_readonly_fields(self, request, obj=None):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700312 readonly_fields = list(self.readonly_fields)[:]
313 if request.user.isReadOnlyUser():
314 for field in self.fields:
315 if not field in readonly_fields:
316 readonly_fields.append(field)
317 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500318
Scott Baker40c00762014-08-21 16:55:59 -0700319 def backend_status_icon(self, obj):
320 return mark_safe(backend_icon(obj))
321 backend_status_icon.short_description = ""
Scott Baker36f50872014-08-21 13:01:25 -0700322
Scott Bakerb27b62c2014-08-15 16:29:16 -0700323class PlStackGenericTabularInline(generic.GenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500324 def has_add_permission(self, request):
Scott Bakerb27b62c2014-08-15 16:29:16 -0700325 return not request.user.isReadOnlyUser()
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500326
Scott Bakerb27b62c2014-08-15 16:29:16 -0700327 def get_readonly_fields(self, request, obj=None):
328 readonly_fields = list(self.readonly_fields)[:]
329 if request.user.isReadOnlyUser():
330 for field in self.fields:
331 if not field in readonly_fields:
332 readonly_fields.append(field)
333 return readonly_fields
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500334
Scott Baker40c00762014-08-21 16:55:59 -0700335 def backend_status_icon(self, obj):
336 return mark_safe(backend_icon(obj))
337 backend_status_icon.short_description = ""
338
Scott Baker67db95f2015-02-18 15:50:11 -0800339class ReservationInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400340 model = Reservation
341 extra = 0
342 suit_classes = 'suit-tab suit-tab-reservations'
Scott Baker36f50872014-08-21 13:01:25 -0700343
Tony Mack5b061472014-02-04 07:57:10 -0500344 def queryset(self, request):
345 return Reservation.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400346
Scott Bakerb27b62c2014-08-15 16:29:16 -0700347class TagInline(PlStackGenericTabularInline):
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400348 model = Tag
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400349 extra = 0
350 suit_classes = 'suit-tab suit-tab-tags'
Tony Mack5b061472014-02-04 07:57:10 -0500351 fields = ['service', 'name', 'value']
352
353 def queryset(self, request):
354 return Tag.select_by_user(request.user)
Siobhan Tullyde5450d2013-06-21 11:35:33 -0400355
Scott Baker74d8e622013-07-29 16:04:22 -0700356class NetworkLookerUpper:
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400357 """ This is a callable that looks up a network name in a sliver and returns
358 the ip address for that network.
359 """
360
Scott Baker434ca7e2014-08-15 12:29:20 -0700361 byNetworkName = {} # class variable
362
Siobhan Tully2c780ad2013-09-06 11:22:40 -0400363 def __init__(self, name):
364 self.short_description = name
365 self.__name__ = name
366 self.network_name = name
367
368 def __call__(self, obj):
369 if obj is not None:
370 for nbs in obj.networksliver_set.all():
371 if (nbs.network.name == self.network_name):
372 return nbs.ip
Scott Baker74d8e622013-07-29 16:04:22 -0700373 return ""
374
375 def __str__(self):
376 return self.network_name
377
Scott Baker434ca7e2014-08-15 12:29:20 -0700378 @staticmethod
379 def get(network_name):
380 """ We want to make sure we alwars return the same NetworkLookerUpper
381 because sometimes django will cause them to be instantiated multiple
382 times (and we don't want different ones in form.fields vs
383 SliverInline.readonly_fields).
384 """
385 if network_name not in NetworkLookerUpper.byNetworkName:
386 NetworkLookerUpper.byNetworkName[network_name] = NetworkLookerUpper(network_name)
387 return NetworkLookerUpper.byNetworkName[network_name]
388
Scott Baker67db95f2015-02-18 15:50:11 -0800389class SliverInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400390 model = Sliver
Scott Baker9d856052015-01-19 11:32:20 -0800391 fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'deployment', 'flavor', 'image', 'node']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400392 extra = 0
Scott Baker9d856052015-01-19 11:32:20 -0800393 readonly_fields = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name']
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400394 suit_classes = 'suit-tab suit-tab-slivers'
Scott Baker74d8e622013-07-29 16:04:22 -0700395
Tony Mack5b061472014-02-04 07:57:10 -0500396 def queryset(self, request):
397 return Sliver.select_by_user(request.user)
398
Scott Bakerb24cc932014-06-09 10:51:16 -0700399 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
Tony Mackb2dba4b2014-12-26 13:38:02 -0500400 if db_field.name == 'deployment':
Tony Mack1b8975c2015-02-24 15:41:49 -0500401
402 kwargs['queryset'] = Deployment.select_by_acl(request.user).filter(sitedeployments__nodes__isnull=False).distinct()
Scott Baker7a61dc42014-09-02 17:08:20 -0700403 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_deployment_changed(this);"})
Tony Mackb2dba4b2014-12-26 13:38:02 -0500404 if db_field.name == 'flavor':
Scott Baker4b6d9442014-09-08 12:14:14 -0700405 kwargs['widget'] = forms.Select(attrs={'onChange': "sliver_flavor_changed(this);"})
Scott Baker3b678742014-06-09 13:11:54 -0700406
407 field = super(SliverInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Scott Bakerb24cc932014-06-09 10:51:16 -0700408
409 return field
410
Scott Baker67db95f2015-02-18 15:50:11 -0800411class SiteInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400412 model = Site
413 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400414 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400415
Tony Mack5b061472014-02-04 07:57:10 -0500416 def queryset(self, request):
417 return Site.select_by_user(request.user)
418
Scott Baker67db95f2015-02-18 15:50:11 -0800419class UserInline(XOSTabularInline):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400420 model = User
Scott Baker40c00762014-08-21 16:55:59 -0700421 fields = ['backend_status_icon', 'email', 'firstname', 'lastname']
422 readonly_fields = ('backend_status_icon', )
Siobhan Tully30fd4292013-05-10 08:59:56 -0400423 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400424 suit_classes = 'suit-tab suit-tab-users'
Siobhan Tully30fd4292013-05-10 08:59:56 -0400425
Tony Mack5b061472014-02-04 07:57:10 -0500426 def queryset(self, request):
427 return User.select_by_user(request.user)
428
Scott Baker67db95f2015-02-18 15:50:11 -0800429class SliceInline(XOSTabularInline):
Tony Mack00d361f2013-04-28 10:28:42 -0400430 model = Slice
Scott Baker40c00762014-08-21 16:55:59 -0700431 fields = ['backend_status_icon', 'name', 'site', 'serviceClass', 'service']
432 readonly_fields = ('backend_status_icon', )
Tony Mack00d361f2013-04-28 10:28:42 -0400433 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400434 suit_classes = 'suit-tab suit-tab-slices'
435
Tony Mack5b061472014-02-04 07:57:10 -0500436 def queryset(self, request):
437 return Slice.select_by_user(request.user)
438
Scott Baker67db95f2015-02-18 15:50:11 -0800439class NodeInline(XOSTabularInline):
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400440 model = Node
441 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400442 suit_classes = 'suit-tab suit-tab-nodes'
Tony Mack68a1e422014-12-08 16:43:02 -0500443 fields = ['backend_status_icon', 'name', 'site_deployment']
Scott Baker40c00762014-08-21 16:55:59 -0700444 readonly_fields = ('backend_status_icon', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400445
Scott Baker67db95f2015-02-18 15:50:11 -0800446class DeploymentPrivilegeInline(XOSTabularInline):
Tony Mack68a1e422014-12-08 16:43:02 -0500447 model = DeploymentPrivilege
448 extra = 0
Tony Mack88c89902015-02-09 21:41:57 -0500449 suit_classes = 'suit-tab suit-tab-deploymentprivileges'
Tony Mack68a1e422014-12-08 16:43:02 -0500450 fields = ['backend_status_icon', 'user','role','deployment']
451 readonly_fields = ('backend_status_icon', )
452
453 def queryset(self, request):
454 return DeploymentPrivilege.select_by_user(request.user)
455
Scott Baker67db95f2015-02-18 15:50:11 -0800456class ControllerSiteInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500457 model = ControllerSite
458 extra = 0
459 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Macke2363c12015-01-06 15:08:20 -0500460 fields = ['controller', 'site', 'tenant_id']
Tony Mack3066a952015-01-05 22:48:11 -0500461
462
Scott Baker67db95f2015-02-18 15:50:11 -0800463class SitePrivilegeInline(XOSTabularInline):
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400464 model = SitePrivilege
465 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400466 suit_classes = 'suit-tab suit-tab-siteprivileges'
Scott Baker40c00762014-08-21 16:55:59 -0700467 fields = ['backend_status_icon', 'user','site', 'role']
468 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400469
Tony Mackc2835a92013-05-28 09:18:49 -0400470 def formfield_for_foreignkey(self, db_field, request, **kwargs):
471 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500472 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400473
474 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -0500475 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400476 return super(SitePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
477
Tony Mack5b061472014-02-04 07:57:10 -0500478 def queryset(self, request):
479 return SitePrivilege.select_by_user(request.user)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400480
Scott Baker67db95f2015-02-18 15:50:11 -0800481class SiteDeploymentInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500482 model = SiteDeployment
Tony Macke4be32f2014-03-11 20:45:25 -0400483 extra = 0
Tony Mackb81d5e42015-01-30 10:58:29 -0500484 suit_classes = 'suit-tab suit-tab-sitedeployments'
Tony Mackd14d48f2014-12-05 17:13:08 -0500485 fields = ['backend_status_icon', 'deployment','site', 'controller']
Scott Baker40c00762014-08-21 16:55:59 -0700486 readonly_fields = ('backend_status_icon', )
Tony Macke4be32f2014-03-11 20:45:25 -0400487
488 def formfield_for_foreignkey(self, db_field, request, **kwargs):
489 if db_field.name == 'site':
490 kwargs['queryset'] = Site.select_by_user(request.user)
491
492 if db_field.name == 'deployment':
493 kwargs['queryset'] = Deployment.select_by_user(request.user)
Tony Mackd14d48f2014-12-05 17:13:08 -0500494
495 if db_field.name == 'controller':
Tony Mack5817cb42015-02-16 19:54:24 -0500496 kwargs['queryset'] = Controller.select_by_user(request.user).filter(deployment__id=int(resolve(request.path).args[0]))
Tony Mackd14d48f2014-12-05 17:13:08 -0500497
Tony Mack3066a952015-01-05 22:48:11 -0500498 return super(SiteDeploymentInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Macke4be32f2014-03-11 20:45:25 -0400499
500 def queryset(self, request):
Tony Mack3066a952015-01-05 22:48:11 -0500501 return SiteDeployment.select_by_user(request.user)
Tony Macke4be32f2014-03-11 20:45:25 -0400502
503
Scott Baker67db95f2015-02-18 15:50:11 -0800504class SlicePrivilegeInline(XOSTabularInline):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400505 model = SlicePrivilege
506 suit_classes = 'suit-tab suit-tab-sliceprivileges'
507 extra = 0
Scott Baker40c00762014-08-21 16:55:59 -0700508 fields = ('backend_status_icon', 'user', 'slice', 'role')
509 readonly_fields = ('backend_status_icon', )
Siobhan Tullyaa1bcd52013-05-10 12:43:09 -0400510
Tony Mackc2835a92013-05-28 09:18:49 -0400511 def formfield_for_foreignkey(self, db_field, request, **kwargs):
512 if db_field.name == 'slice':
Scott Baker36f50872014-08-21 13:01:25 -0700513 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400514 if db_field.name == 'user':
Scott Baker36f50872014-08-21 13:01:25 -0700515 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400516
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400517 return super(SlicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -0400518
Tony Mack5b061472014-02-04 07:57:10 -0500519 def queryset(self, request):
520 return SlicePrivilege.select_by_user(request.user)
521
Scott Baker67db95f2015-02-18 15:50:11 -0800522class SliceNetworkInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -0700523 model = Network.slices.through
Scott Baker874936e2014-01-13 18:15:34 -0800524 selflink_fieldname = "network"
Scott Baker74d8e622013-07-29 16:04:22 -0700525 extra = 0
526 verbose_name = "Network Connection"
527 verbose_name_plural = "Network Connections"
Siobhan Tully2d95e482013-09-06 10:56:06 -0400528 suit_classes = 'suit-tab suit-tab-slicenetworks'
Scott Baker40c00762014-08-21 16:55:59 -0700529 fields = ['backend_status_icon', 'network']
530 readonly_fields = ('backend_status_icon', )
Scott Baker2170b972014-06-03 12:14:07 -0700531
Scott Baker67db95f2015-02-18 15:50:11 -0800532class ImageDeploymentsInline(XOSTabularInline):
Sapan Bhatia1b6bba22014-11-19 15:10:16 -0500533 model = ImageDeployments
Scott Baker2170b972014-06-03 12:14:07 -0700534 extra = 0
535 verbose_name = "Image Deployments"
536 verbose_name_plural = "Image Deployments"
537 suit_classes = 'suit-tab suit-tab-imagedeployments'
Tony Mack06c8e472014-11-30 15:53:08 -0500538 fields = ['backend_status_icon', 'image', 'deployment']
539 readonly_fields = ['backend_status_icon']
540
Scott Baker67db95f2015-02-18 15:50:11 -0800541class ControllerImagesInline(XOSTabularInline):
Tony Mack06c8e472014-11-30 15:53:08 -0500542 model = ControllerImages
543 extra = 0
544 verbose_name = "Controller Images"
545 verbose_name_plural = "Controller Images"
546 suit_classes = 'suit-tab suit-tab-admin-only'
547 fields = ['backend_status_icon', 'image', 'controller', 'glance_image_id']
Scott Baker40c00762014-08-21 16:55:59 -0700548 readonly_fields = ['backend_status_icon', 'glance_image_id']
Scott Baker74d8e622013-07-29 16:04:22 -0700549
Scott Baker67db95f2015-02-18 15:50:11 -0800550class SliceRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400551 model = SliceRole
552 pass
553
Scott Baker67db95f2015-02-18 15:50:11 -0800554class SiteRoleAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400555 model = SiteRole
556 pass
557
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400558class DeploymentAdminForm(forms.ModelForm):
Scott Bakerde0f4412014-06-11 15:40:26 -0700559 images = forms.ModelMultipleChoiceField(
560 queryset=Image.objects.all(),
561 required=False,
562 help_text="Select which images should be deployed on this deployment",
563 widget=FilteredSelectMultiple(
564 verbose_name=('Images'), is_stacked=False
565 )
566 )
Scott Baker37b47902014-09-02 14:37:41 -0700567 flavors = forms.ModelMultipleChoiceField(
568 queryset=Flavor.objects.all(),
569 required=False,
570 help_text="Select which flavors should be usable on this deployment",
571 widget=FilteredSelectMultiple(
572 verbose_name=('Flavors'), is_stacked=False
573 )
574 )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400575 class Meta:
Siobhan Tullybf1153a2013-05-27 20:53:48 -0400576 model = Deployment
Scott Baker37b47902014-09-02 14:37:41 -0700577 many_to_many = ["flavors",]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400578
Siobhan Tully320b4622014-01-17 15:11:14 -0500579 def __init__(self, *args, **kwargs):
Scott Baker5380c522014-06-06 14:49:43 -0700580 request = kwargs.pop('request', None)
Siobhan Tully320b4622014-01-17 15:11:14 -0500581 super(DeploymentAdminForm, self).__init__(*args, **kwargs)
582
Scott Baker5380c522014-06-06 14:49:43 -0700583 self.fields['accessControl'].initial = "allow site " + request.user.site.name
584
Siobhan Tully320b4622014-01-17 15:11:14 -0500585 if self.instance and self.instance.pk:
Scott Baker69e045d2014-11-17 23:44:03 -0800586 self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
Scott Baker37b47902014-09-02 14:37:41 -0700587 self.fields['flavors'].initial = self.instance.flavors.all()
Scott Bakerde0f4412014-06-11 15:40:26 -0700588
589 def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
590 """ helper function for handling m2m relations from the MultipleChoiceField
591
592 this_obj: the source object we want to link from
593
594 selected_objs: a list of destination objects we want to link to
595
596 all_relations: the full set of relations involving this_obj, including ones we don't want
597
598 relation_class: the class that implements the relation from source to dest
599
600 local_attrname: field name representing this_obj in relation_class
601
602 foreign_attrname: field name representing selected_objs in relation_class
603
604 This function will remove all newobjclass relations from this_obj
605 that are not contained in selected_objs, and add any relations that
606 are in selected_objs but don't exist in the data model yet.
607 """
608
609 existing_dest_objs = []
610 for relation in list(all_relations):
611 if getattr(relation, foreign_attrname) not in selected_objs:
612 #print "deleting site", sdp.site
613 relation.delete()
614 else:
615 existing_dest_objs.append(getattr(relation, foreign_attrname))
616
617 for dest_obj in selected_objs:
618 if dest_obj not in existing_dest_objs:
619 #print "adding site", site
620 kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
621 relation = relation_class(**kwargs)
622 relation.save()
Siobhan Tully320b4622014-01-17 15:11:14 -0500623
624 def save(self, commit=True):
625 deployment = super(DeploymentAdminForm, self).save(commit=False)
626
627 if commit:
628 deployment.save()
Scott Baker0057d052014-10-06 17:17:40 -0700629 # this has to be done after save() if/when a deployment is first created
630 deployment.flavors = self.cleaned_data['flavors']
Siobhan Tully320b4622014-01-17 15:11:14 -0500631
632 if deployment.pk:
Scott Bakerc9b14f72014-05-22 13:44:20 -0700633 # save_m2m() doesn't seem to work with 'through' relations. So we
634 # create/destroy the through models ourselves. There has to be
635 # a better way...
636
Tony Mackb2fde612014-12-15 11:45:02 -0500637 self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
638 # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
639 # so well handle that manually here
640 for flavor in deployment.flavors.all():
641 if getattr(flavor, 'name') not in self.cleaned_data['flavors']:
Tony Mackd4ab7822014-12-15 12:37:59 -0500642 deployment.flavors.remove(flavor)
Tony Mackb2fde612014-12-15 11:45:02 -0500643 for flavor in self.cleaned_data['flavors']:
644 if flavor not in deployment.flavors.all():
645 flavor.deployments.add(deployment)
Scott Bakerc9b14f72014-05-22 13:44:20 -0700646
Scott Baker37b47902014-09-02 14:37:41 -0700647 self.save_m2m()
Siobhan Tully320b4622014-01-17 15:11:14 -0500648
649 return deployment
650
Scott Bakerff5e0f32014-05-22 14:40:27 -0700651class DeploymentAdminROForm(DeploymentAdminForm):
652 def save(self, commit=True):
653 raise PermissionDenied
654
Scott Baker67db95f2015-02-18 15:50:11 -0800655class SiteAssocInline(XOSTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500656 model = Site.deployments.through
657 extra = 0
658 suit_classes = 'suit-tab suit-tab-sites'
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400659
Scott Baker67db95f2015-02-18 15:50:11 -0800660class DeploymentAdmin(XOSBaseAdmin):
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500661 model = Deployment
Scott Baker622bcf02015-02-10 08:40:34 -0800662 fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
663 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack68a1e422014-12-08 16:43:02 -0500664 # node no longer directly connected to deployment
665 #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
Tony Mackb81d5e42015-01-30 10:58:29 -0500666 inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
Scott Baker63d1a552014-08-21 15:19:07 -0700667 list_display = ['backend_status_icon', 'name']
668 list_display_links = ('backend_status_icon', 'name', )
Scott Baker40c00762014-08-21 16:55:59 -0700669 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500670
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500671 user_readonly_fields = ['name']
672
Tony Mack68a1e422014-12-08 16:43:02 -0500673 # nodes no longer direclty connected to deployments
Scott Baker622bcf02015-02-10 08:40:34 -0800674 suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500675
Scott Bakerff5e0f32014-05-22 14:40:27 -0700676 def get_form(self, request, obj=None, **kwargs):
Tony Mackcf29cfa2015-02-05 06:13:04 -0500677 if request.user.isReadOnlyUser() or not request.user.is_admin:
Scott Bakerff5e0f32014-05-22 14:40:27 -0700678 kwargs["form"] = DeploymentAdminROForm
679 else:
680 kwargs["form"] = DeploymentAdminForm
Scott Baker5380c522014-06-06 14:49:43 -0700681 adminForm = super(DeploymentAdmin,self).get_form(request, obj, **kwargs)
682
683 # from stackexchange: pass the request object into the form
684
685 class AdminFormMetaClass(adminForm):
686 def __new__(cls, *args, **kwargs):
687 kwargs['request'] = request
688 return adminForm(*args, **kwargs)
689
690 return AdminFormMetaClass
691
Scott Baker67db95f2015-02-18 15:50:11 -0800692class ControllerAdmin(XOSBaseAdmin):
Scott Baker622bcf02015-02-10 08:40:34 -0800693 model = Controller
Scott Baker180148a2015-02-16 11:55:09 -0800694 fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
Scott Baker622bcf02015-02-10 08:40:34 -0800695 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
Tony Mack3066a952015-01-05 22:48:11 -0500696 inlines = [ControllerSiteInline] # ,ControllerImagesInline]
Tony Mackd14d48f2014-12-05 17:13:08 -0500697 list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
698 list_display_links = ('backend_status_icon', 'name', )
699 readonly_fields = ('backend_status_text',)
700
701 user_readonly_fields = []
702
Tony Mack2e897fa2015-01-13 17:33:08 -0500703 def save_model(self, request, obj, form, change):
704 # update openstack connection to use this site/tenant
705 obj.save_by_user(request.user)
706
707 def delete_model(self, request, obj):
Scott Baker622bcf02015-02-10 08:40:34 -0800708 obj.delete_by_user(request.user)
709
Tony Mack78fc1362015-02-18 11:41:36 -0500710 def queryset(self, request):
711 return Controller.select_by_user(request.user)
712
Scott Baker622bcf02015-02-10 08:40:34 -0800713 @property
714 def suit_form_tabs(self):
715 tabs = [('general', 'Controller Details'),
716 ]
717
718 request=getattr(_thread_locals, "request", None)
719 if request and request.user.is_admin:
720 tabs.append( ('admin-only', 'Admin-Only') )
721
722 return tabs
Tony Mack2e897fa2015-01-13 17:33:08 -0500723
Scott Baker67db95f2015-02-18 15:50:11 -0800724class ServiceAttrAsTabInline(XOSTabularInline):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400725 model = ServiceAttribute
726 fields = ['name','value']
727 extra = 0
728 suit_classes = 'suit-tab suit-tab-serviceattrs'
729
Scott Baker67db95f2015-02-18 15:50:11 -0800730class ServiceAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -0700731 list_display = ("backend_status_icon","name","description","versionNumber","enabled","published")
732 list_display_links = ('backend_status_icon', 'name', )
Scott Bakera3cd2612015-02-26 17:06:46 -0800733 fieldList = ["backend_status_text","name","description","versionNumber","enabled","published","view_url","icon_url"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500734 fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
735 inlines = [ServiceAttrAsTabInline,SliceInline]
Scott Baker40c00762014-08-21 16:55:59 -0700736 readonly_fields = ('backend_status_text', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500737
738 user_readonly_fields = fieldList
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500739
740 suit_form_tabs =(('general', 'Service Details'),
741 ('slices','Slices'),
742 ('serviceattrs','Additional Attributes'),
743 )
Siobhan Tullyce652d02013-10-08 21:52:35 -0400744
Scott Baker67db95f2015-02-18 15:50:11 -0800745class SiteNodeInline(XOSTabularInline):
Tony Mack4f134e62015-01-14 20:58:38 -0500746 model = Node
747 fields = ['name', 'site_deployment']
748 extra = 0
749 suit_classes = 'suit-tab suit-tab-nodes'
750
Tony Mackc2a0d312015-02-25 11:39:34 -0500751 def formfield_for_foreignkey(self, db_field, request, **kwargs):
752 # only display site deployments associated with this site
753 if db_field.name == 'site_deployment':
754 kwargs['queryset'] = SiteDeployment.objects.filter(site__id=int(request.path.split('/')[-2]))
755
756 return super(SiteNodeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
757
Scott Baker67db95f2015-02-18 15:50:11 -0800758class SiteAdmin(XOSBaseAdmin):
Tony Mack450b6e02015-01-25 12:35:29 -0500759 #fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'accountLink','location']
760 fieldList = ['backend_status_text', 'name', 'site_url', 'enabled', 'is_public', 'login_base', 'location']
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400761 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500762 (None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),
Tony Macke4be32f2014-03-11 20:45:25 -0400763 #('Deployment Networks', {'fields': ['deployments'], 'classes':['suit-tab suit-tab-deployments']}),
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400764 ]
Tony Mack450b6e02015-01-25 12:35:29 -0500765 #readonly_fields = ['backend_status_text', 'accountLink']
766 readonly_fields = ['backend_status_text']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500767
Tony Mack450b6e02015-01-25 12:35:29 -0500768 #user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base', 'accountLink']
769 user_readonly_fields = ['name', 'deployments','site_url', 'enabled', 'is_public', 'login_base']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500770
Scott Baker63d1a552014-08-21 15:19:07 -0700771 list_display = ('backend_status_icon', 'name', 'login_base','site_url', 'enabled')
772 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400773 filter_horizontal = ('deployments',)
Tony Mackb81d5e42015-01-30 10:58:29 -0500774 inlines = [SliceInline,UserInline,TagInline, SitePrivilegeInline, SiteNodeInline]
Tony Mackde100182015-01-14 12:11:05 -0500775 admin_inlines = [ControllerSiteInline]
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400776 search_fields = ['name']
777
Tony Mack30dfcd72015-01-10 23:08:10 -0500778 @property
779 def suit_form_tabs(self):
780 tabs = [('general', 'Site Details'),
781 ('users','Users'),
782 ('siteprivileges','Privileges'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500783 ('slices','Slices'),
Tony Mack4f134e62015-01-14 20:58:38 -0500784 ('nodes','Nodes'),
Tony Mack30dfcd72015-01-10 23:08:10 -0500785 ]
786
787 request=getattr(_thread_locals, "request", None)
788 if request and request.user.is_admin:
789 tabs.append( ('admin-only', 'Admin-Only') )
790
791 return tabs
792
Tony Mack04062832013-05-10 08:22:44 -0400793 def queryset(self, request):
Tony Mack5b061472014-02-04 07:57:10 -0500794 return Site.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400795
Tony Mack5cd13202013-05-01 21:48:38 -0400796 def get_formsets(self, request, obj=None):
797 for inline in self.get_inline_instances(request, obj):
798 # hide MyInline in the add view
799 if obj is None:
800 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400801 if isinstance(inline, SliverInline):
802 inline.model.caller = request.user
Tony Mack5cd13202013-05-01 21:48:38 -0400803 yield inline.get_formset(request, obj)
804
Scott Baker545db2a2013-12-09 18:44:43 -0800805 def accountLink(self, obj):
806 link_obj = obj.accounts.all()
807 if link_obj:
808 reverse_path = "admin:core_account_change"
809 url = reverse(reverse_path, args =(link_obj[0].id,))
810 return "<a href='%s'>%s</a>" % (url, "view billing details")
811 else:
812 return "no billing data for this site"
813 accountLink.allow_tags = True
814 accountLink.short_description = "Billing"
815
Tony Mack332ee1d2014-02-04 15:33:45 -0500816 def save_model(self, request, obj, form, change):
817 # update openstack connection to use this site/tenant
818 obj.save_by_user(request.user)
819
820 def delete_model(self, request, obj):
821 obj.delete_by_user(request.user)
822
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500823
Scott Baker67db95f2015-02-18 15:50:11 -0800824class SitePrivilegeAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -0700825 fieldList = ['backend_status_text', 'user', 'site', 'role']
Tony Mack00d361f2013-04-28 10:28:42 -0400826 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500827 (None, {'fields': fieldList, 'classes':['collapse']})
Tony Mack00d361f2013-04-28 10:28:42 -0400828 ]
Scott Baker40c00762014-08-21 16:55:59 -0700829 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700830 list_display = ('backend_status_icon', 'user', 'site', 'role')
831 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500832 user_readonly_fields = fieldList
833 user_readonly_inlines = []
Tony Mack00d361f2013-04-28 10:28:42 -0400834
Tony Mackc2835a92013-05-28 09:18:49 -0400835 def formfield_for_foreignkey(self, db_field, request, **kwargs):
836 if db_field.name == 'site':
837 if not request.user.is_admin:
838 # only show sites where user is an admin or pi
839 sites = set()
840 for site_privilege in SitePrivilege.objects.filer(user=request.user):
841 if site_privilege.role.role_type in ['admin', 'pi']:
842 sites.add(site_privilege.site)
843 kwargs['queryset'] = Site.objects.filter(site__in=list(sites))
844
845 if db_field.name == 'user':
846 if not request.user.is_admin:
847 # only show users from sites where caller has admin or pi role
848 roles = Role.objects.filter(role_type__in=['admin', 'pi'])
849 site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
850 sites = [site_privilege.site for site_privilege in site_privileges]
851 site_privileges = SitePrivilege.objects.filter(site__in=sites)
852 emails = [site_privilege.user.email for site_privilege in site_privileges]
853 users = User.objects.filter(email__in=emails)
854 kwargs['queryset'] = users
855
856 return super(SitePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
857
Tony Mack04062832013-05-10 08:22:44 -0400858 def queryset(self, request):
859 # admins can see all privileges. Users can only see privileges at sites
Tony Mackc2835a92013-05-28 09:18:49 -0400860 # where they have the admin role or pi role.
Tony Mack04062832013-05-10 08:22:44 -0400861 qs = super(SitePrivilegeAdmin, self).queryset(request)
Tony Mack5b061472014-02-04 07:57:10 -0500862 #if not request.user.is_admin:
863 # roles = Role.objects.filter(role_type__in=['admin', 'pi'])
864 # site_privileges = SitePrivilege.objects.filter(user=request.user).filter(role__in=roles)
865 # login_bases = [site_privilege.site.login_base for site_privilege in site_privileges]
866 # sites = Site.objects.filter(login_base__in=login_bases)
867 # qs = qs.filter(site__in=sites)
Tony Mack04062832013-05-10 08:22:44 -0400868 return qs
869
Siobhan Tullyce652d02013-10-08 21:52:35 -0400870class SliceForm(forms.ModelForm):
871 class Meta:
872 model = Slice
873 widgets = {
Scott Baker36f50872014-08-21 13:01:25 -0700874 'service': LinkedSelect
Siobhan Tullyce652d02013-10-08 21:52:35 -0400875 }
876
Tony Macke75841e2014-09-29 16:10:52 -0400877 def clean(self):
878 cleaned_data = super(SliceForm, self).clean()
879 name = cleaned_data.get('name')
Scott Baker3cb382c2014-10-06 23:09:59 -0700880 site = cleaned_data.get('site')
Tony Mackcc9e2592014-10-22 12:54:19 -0400881 slice_id = self.instance.id
882 if not site and slice_id:
883 site = Slice.objects.get(id=slice_id).site
Scott Baker3cb382c2014-10-06 23:09:59 -0700884 if (not isinstance(site,Site)):
885 # previous code indicates 'site' could be a site_id and not a site?
886 site = Slice.objects.get(id=site.id)
Tony Macke75841e2014-09-29 16:10:52 -0400887 if not name.startswith(site.login_base):
888 raise forms.ValidationError('slice name must begin with %s' % site.login_base)
889 return cleaned_data
890
Scott Baker67db95f2015-02-18 15:50:11 -0800891class ControllerSliceInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -0500892 model = ControllerSlice
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700893 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -0500894 verbose_name = "Controller Slices"
895 verbose_name_plural = "Controller Slices"
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700896 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -0500897 fields = ['backend_status_icon', 'controller', 'tenant_id']
Tony Mack30dfcd72015-01-10 23:08:10 -0500898 readonly_fields = ('backend_status_icon', 'controller' )
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700899
Scott Baker67db95f2015-02-18 15:50:11 -0800900class SliceAdmin(XOSBaseAdmin):
Siobhan Tullyce652d02013-10-08 21:52:35 -0400901 form = SliceForm
Tony Mackfbb26fc2014-09-02 07:03:27 -0400902 fieldList = ['backend_status_text', 'site', 'name', 'serviceClass', 'enabled','description', 'service', 'slice_url', 'max_slivers']
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500903 fieldsets = [('Slice Details', {'fields': fieldList, 'classes':['suit-tab suit-tab-general']}),]
Scott Baker40c00762014-08-21 16:55:59 -0700904 readonly_fields = ('backend_status_text', )
Tony Mack7d459902014-09-03 13:18:57 -0400905 list_display = ('backend_status_icon', 'name', 'site','serviceClass', 'slice_url', 'max_slivers')
906 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tully2d95e482013-09-06 10:56:06 -0400907 inlines = [SlicePrivilegeInline,SliverInline, TagInline, ReservationInline,SliceNetworkInline]
Tony Mack3066a952015-01-05 22:48:11 -0500908 admin_inlines = [ControllerSliceInline]
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400909
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500910 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -0400911
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700912 @property
913 def suit_form_tabs(self):
914 tabs =[('general', 'Slice Details'),
915 ('slicenetworks','Networks'),
916 ('sliceprivileges','Privileges'),
917 ('slivers','Slivers'),
Tony Mack450b6e02015-01-25 12:35:29 -0500918 #('reservations','Reservations'),
Tony Mackd2433382015-01-15 14:44:06 -0500919 ('tags','Tags'),
Scott Bakerf9f1ef42014-10-15 16:54:04 -0700920 ]
921
922 request=getattr(_thread_locals, "request", None)
923 if request and request.user.is_admin:
924 tabs.append( ('admin-only', 'Admin-Only') )
925
926 return tabs
Tony Mack0aa732a2014-10-22 11:54:29 -0400927
928 def add_view(self, request, form_url='', extra_context=None):
929 # revert to default read-only fields
930 self.readonly_fields = ('backend_status_text',)
931 return super(SliceAdmin, self).add_view(request, form_url, extra_context=extra_context)
932
933 def change_view(self, request, object_id, form_url='', extra_context=None):
Tony Mack0aa732a2014-10-22 11:54:29 -0400934 # cannot change the site of an existing slice so make the site field read only
935 if object_id:
936 self.readonly_fields = ('backend_status_text','site')
937 return super(SliceAdmin, self).change_view(request, object_id, form_url)
Siobhan Tully4bc09f22013-04-10 21:15:21 -0400938
Scott Baker510fdbb2014-08-05 17:19:24 -0700939 def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
Scott Baker510fdbb2014-08-05 17:19:24 -0700940 deployment_nodes = []
941 for node in Node.objects.all():
Scott Baker66b11e22015-01-21 16:24:07 -0800942 deployment_nodes.append( (node.site_deployment.deployment.id, node.id, node.name) )
Scott Baker510fdbb2014-08-05 17:19:24 -0700943
Scott Baker7a61dc42014-09-02 17:08:20 -0700944 deployment_flavors = []
945 for flavor in Flavor.objects.all():
946 for deployment in flavor.deployments.all():
947 deployment_flavors.append( (deployment.id, flavor.id, flavor.name) )
948
Tony Mack68a1e422014-12-08 16:43:02 -0500949 deployment_images = []
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700950 for image in Image.objects.all():
Tony Mack68a1e422014-12-08 16:43:02 -0500951 for deployment_image in image.imagedeployments.all():
Scott Bakerf2c0c512014-12-22 17:35:34 -0800952 deployment_images.append( (deployment_image.deployment.id, image.id, image.name) )
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700953
Tony Mackec23b992014-09-02 21:18:45 -0400954 site_login_bases = []
955 for site in Site.objects.all():
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700956 site_login_bases.append((site.id, site.login_base))
957
Scott Baker510fdbb2014-08-05 17:19:24 -0700958 context["deployment_nodes"] = deployment_nodes
Scott Baker7a61dc42014-09-02 17:08:20 -0700959 context["deployment_flavors"] = deployment_flavors
Scott Bakeraf36c4d2014-09-09 09:58:49 -0700960 context["deployment_images"] = deployment_images
Tony Mackec23b992014-09-02 21:18:45 -0400961 context["site_login_bases"] = site_login_bases
Scott Baker510fdbb2014-08-05 17:19:24 -0700962 return super(SliceAdmin, self).render_change_form(request, context, add, change, form_url, obj)
963
Tony Mackc2835a92013-05-28 09:18:49 -0400964 def formfield_for_foreignkey(self, db_field, request, **kwargs):
965 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -0500966 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackec23b992014-09-02 21:18:45 -0400967 kwargs['widget'] = forms.Select(attrs={'onChange': "update_slice_prefix(this, $($(this).closest('fieldset')[0]).find('.field-name input')[0].id)"})
Scott Baker40c00762014-08-21 16:55:59 -0700968
Tony Mackc2835a92013-05-28 09:18:49 -0400969 return super(SliceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
970
Tony Mack04062832013-05-10 08:22:44 -0400971 def queryset(self, request):
972 # admins can see all keys. Users can only see slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -0500973 return Slice.select_by_user(request.user)
Tony Mack04062832013-05-10 08:22:44 -0400974
Tony Mack79748612013-05-01 14:52:03 -0400975 def get_formsets(self, request, obj=None):
976 for inline in self.get_inline_instances(request, obj):
977 # hide MyInline in the add view
978 if obj is None:
979 continue
Tony Mack2bd5b412013-06-11 21:05:06 -0400980 if isinstance(inline, SliverInline):
981 inline.model.caller = request.user
Tony Mack79748612013-05-01 14:52:03 -0400982 yield inline.get_formset(request, obj)
983
Scott Baker67db95f2015-02-18 15:50:11 -0800984class SlicePrivilegeAdmin(XOSBaseAdmin):
Tony Mack00d361f2013-04-28 10:28:42 -0400985 fieldsets = [
Scott Baker40c00762014-08-21 16:55:59 -0700986 (None, {'fields': ['backend_status_text', 'user', 'slice', 'role']})
Tony Mack00d361f2013-04-28 10:28:42 -0400987 ]
Scott Baker40c00762014-08-21 16:55:59 -0700988 readonly_fields = ('backend_status_text', )
Scott Baker63d1a552014-08-21 15:19:07 -0700989 list_display = ('backend_status_icon', 'user', 'slice', 'role')
990 list_display_links = list_display
Tony Mack00d361f2013-04-28 10:28:42 -0400991
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500992 user_readonly_fields = ['user', 'slice', 'role']
993 user_readonly_inlines = []
994
Tony Mackc2835a92013-05-28 09:18:49 -0400995 def formfield_for_foreignkey(self, db_field, request, **kwargs):
996 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -0500997 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -0400998
999 if db_field.name == 'user':
Tony Mack5b061472014-02-04 07:57:10 -05001000 kwargs['queryset'] = User.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001001
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001002 return super(SlicePrivilegeAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
Tony Mackc2835a92013-05-28 09:18:49 -04001003
Tony Mack04062832013-05-10 08:22:44 -04001004 def queryset(self, request):
1005 # admins can see all memberships. Users can only see memberships of
1006 # slices where they have the admin role.
Tony Mack5b061472014-02-04 07:57:10 -05001007 return SlicePrivilege.select_by_user(request.user)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001008
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001009 def save_model(self, request, obj, form, change):
Tony Mack951dab42013-05-02 19:51:45 -04001010 # update openstack connection to use this site/tenant
1011 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001012 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001013 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001014 obj.save()
1015
1016 def delete_model(self, request, obj):
Tony Mack951dab42013-05-02 19:51:45 -04001017 # update openstack connection to use this site/tenant
1018 auth = request.session.get('auth', {})
Tony Mackf7f79a12014-08-11 11:21:42 -04001019 auth['tenant'] = obj.slice.slicename
Tony Mack951dab42013-05-02 19:51:45 -04001020 obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
Tony Mack9bcbe4f2013-04-29 08:13:27 -04001021 obj.delete()
1022
Siobhan Tully567e3e62013-06-21 18:03:16 -04001023
Scott Baker67db95f2015-02-18 15:50:11 -08001024class ImageAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001025
Scott Baker36f50872014-08-21 13:01:25 -07001026 fieldsets = [('Image Details',
Scott Baker40c00762014-08-21 16:55:59 -07001027 {'fields': ['backend_status_text', 'name', 'disk_format', 'container_format'],
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001028 'classes': ['suit-tab suit-tab-general']})
1029 ]
Scott Baker40c00762014-08-21 16:55:59 -07001030 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001031
Tony Mack06c8e472014-11-30 15:53:08 -05001032 suit_form_tabs =(('general','Image Details'),('slivers','Slivers'),('imagedeployments','Deployments'), ('controllerimages', 'Controllers'))
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001033
Tony Mack06c8e472014-11-30 15:53:08 -05001034 inlines = [SliverInline, ControllerImagesInline]
Scott Bakerb6f99242014-06-11 11:34:44 -07001035
Tony Mack32e1ce32014-05-07 13:29:41 -04001036 user_readonly_fields = ['name', 'disk_format', 'container_format']
Scott Bakerb27b62c2014-08-15 16:29:16 -07001037
Scott Baker63d1a552014-08-21 15:19:07 -07001038 list_display = ['backend_status_icon', 'name']
1039 list_display_links = ('backend_status_icon', 'name', )
1040
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001041class NodeForm(forms.ModelForm):
1042 class Meta:
1043 widgets = {
1044 'site': LinkedSelect,
1045 'deployment': LinkedSelect
1046 }
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001047
Scott Baker67db95f2015-02-18 15:50:11 -08001048class NodeAdmin(XOSBaseAdmin):
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001049 form = NodeForm
Tony Mack68a1e422014-12-08 16:43:02 -05001050 list_display = ('backend_status_icon', 'name', 'site_deployment')
Scott Baker63d1a552014-08-21 15:19:07 -07001051 list_display_links = ('backend_status_icon', 'name', )
Tony Mack68a1e422014-12-08 16:43:02 -05001052 list_filter = ('site_deployment',)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001053
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001054 inlines = [TagInline,SliverInline]
Tony Mack68a1e422014-12-08 16:43:02 -05001055 fieldsets = [('Node Details', {'fields': ['backend_status_text', 'name','site_deployment'], 'classes':['suit-tab suit-tab-details']})]
Scott Baker40c00762014-08-21 16:55:59 -07001056 readonly_fields = ('backend_status_text', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001057
Tony Mack68a1e422014-12-08 16:43:02 -05001058 user_readonly_fields = ['name','site_deployment']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001059 user_readonly_inlines = [TagInline,SliverInline]
1060
Tony Mack5fecf712015-01-12 21:40:09 -05001061 suit_form_tabs =(('details','Node Details'),('slivers','Slivers'))
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001062
Siobhan Tully567e3e62013-06-21 18:03:16 -04001063
Tony Mackd90cdbf2013-04-16 22:48:40 -04001064class SliverForm(forms.ModelForm):
1065 class Meta:
Tony Mack1d6b85f2013-05-07 18:49:14 -04001066 model = Sliver
Tony Mackd90cdbf2013-04-16 22:48:40 -04001067 ip = forms.CharField(widget=PlainTextWidget)
Tony Mack18261812013-05-02 16:39:20 -04001068 instance_name = forms.CharField(widget=PlainTextWidget)
Tony Mackd90cdbf2013-04-16 22:48:40 -04001069 widgets = {
1070 'ip': PlainTextWidget(),
Tony Mack18261812013-05-02 16:39:20 -04001071 'instance_name': PlainTextWidget(),
Scott Baker9d856052015-01-19 11:32:20 -08001072 'instance_id': PlainTextWidget(),
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001073 'slice': LinkedSelect,
Tony Mackb2dba4b2014-12-26 13:38:02 -05001074 'deployment': LinkedSelect,
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001075 'node': LinkedSelect,
1076 'image': LinkedSelect
Siobhan Tully53437282013-04-26 19:30:27 -04001077 }
Tony Mackd90cdbf2013-04-16 22:48:40 -04001078
Scott Baker67db95f2015-02-18 15:50:11 -08001079class TagAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001080 list_display = ['backend_status_icon', 'service', 'name', 'value', 'content_type', 'content_object',]
1081 list_display_links = list_display
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001082 user_readonly_fields = ['service', 'name', 'value', 'content_type', 'content_object',]
1083 user_readonly_inlines = []
Siobhan Tullyd3515752013-06-21 16:34:53 -04001084
Scott Baker67db95f2015-02-18 15:50:11 -08001085class SliverAdmin(XOSBaseAdmin):
Tony Mackd90cdbf2013-04-16 22:48:40 -04001086 form = SliverForm
Tony Mackcdec0902013-04-15 00:38:49 -04001087 fieldsets = [
Tony Mack707f7d72015-01-30 12:52:46 -05001088 ('Sliver Details', {'fields': ['backend_status_text', 'slice', 'deployment', 'node', 'all_ips_string', 'instance_id', 'instance_name', 'flavor', 'image', 'ssh_command'], 'classes': ['suit-tab suit-tab-general'], })
Tony Mackcdec0902013-04-15 00:38:49 -04001089 ]
Tony Mackdb8580b2015-01-30 17:20:46 -05001090 readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack707f7d72015-01-30 12:52:46 -05001091 list_display = ['backend_status_icon', 'all_ips_string', 'instance_id', 'instance_name', 'slice', 'flavor', 'image', 'node', 'deployment']
Scott Baker2f295402015-02-13 14:38:21 -08001092 list_display_links = ('backend_status_icon', 'all_ips_string', 'instance_id', )
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001093
Scott Bakerc72997a2015-01-19 08:24:08 -08001094 suit_form_tabs =(('general', 'Sliver Details'),)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001095
Siobhan Tullyde5450d2013-06-21 11:35:33 -04001096 inlines = [TagInline]
Tony Mack53106f32013-04-27 16:43:01 -04001097
Tony Mackb2dba4b2014-12-26 13:38:02 -05001098 user_readonly_fields = ['slice', 'deployment', 'node', 'ip', 'instance_name', 'flavor', 'image']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001099
Scott Baker7ccc6ad2015-01-25 22:16:13 -08001100 def ssh_command(self, obj):
1101 ssh_command = obj.get_ssh_command()
1102 if ssh_command:
1103 return ssh_command
1104 else:
1105 return "(not available)"
1106
Tony Mackc2835a92013-05-28 09:18:49 -04001107 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1108 if db_field.name == 'slice':
Tony Mack5b061472014-02-04 07:57:10 -05001109 kwargs['queryset'] = Slice.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001110
1111 return super(SliverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1112
Tony Mack04062832013-05-10 08:22:44 -04001113 def queryset(self, request):
Scott Baker36f50872014-08-21 13:01:25 -07001114 # admins can see all slivers. Users can only see slivers of
Tony Mack04062832013-05-10 08:22:44 -04001115 # the slices they belong to.
Tony Mack5b061472014-02-04 07:57:10 -05001116 return Sliver.select_by_user(request.user)
1117
Tony Mack04062832013-05-10 08:22:44 -04001118
Tony Mack1d6b85f2013-05-07 18:49:14 -04001119 def get_formsets(self, request, obj=None):
1120 # make some fields read only if we are updating an existing record
1121 if obj == None:
Tony Mackb428feb2015-01-30 17:42:10 -05001122 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001123 else:
Tony Mackb428feb2015-01-30 17:42:10 -05001124 self.readonly_fields = ('backend_status_text', 'ssh_command', 'all_ips_string', 'slice', 'flavor', 'image', 'node')
Tony Mack1d6b85f2013-05-07 18:49:14 -04001125
1126 for inline in self.get_inline_instances(request, obj):
1127 # hide MyInline in the add view
1128 if obj is None:
1129 continue
Scott Baker526b71e2014-05-13 13:18:01 -07001130 if isinstance(inline, SliverInline):
1131 inline.model.caller = request.user
1132 yield inline.get_formset(request, obj)
Tony Mack53106f32013-04-27 16:43:01 -04001133
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001134 #def save_model(self, request, obj, form, change):
1135 # # update openstack connection to use this site/tenant
1136 # auth = request.session.get('auth', {})
1137 # auth['tenant'] = obj.slice.name
1138 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1139 # obj.creator = request.user
1140 # obj.save()
Tony Mack53106f32013-04-27 16:43:01 -04001141
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001142 #def delete_model(self, request, obj):
1143 # # update openstack connection to use this site/tenant
1144 # auth = request.session.get('auth', {})
1145 # auth['tenant'] = obj.slice.name
1146 # obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
1147 # obj.delete()
Tony Mackcdec0902013-04-15 00:38:49 -04001148
Siobhan Tully53437282013-04-26 19:30:27 -04001149class UserCreationForm(forms.ModelForm):
1150 """A form for creating new users. Includes all the required
1151 fields, plus a repeated password."""
1152 password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
1153 password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
1154
1155 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001156 model = User
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001157 fields = ('email', 'firstname', 'lastname', 'phone', 'public_key')
Siobhan Tully53437282013-04-26 19:30:27 -04001158
1159 def clean_password2(self):
1160 # Check that the two password entries match
1161 password1 = self.cleaned_data.get("password1")
1162 password2 = self.cleaned_data.get("password2")
1163 if password1 and password2 and password1 != password2:
1164 raise forms.ValidationError("Passwords don't match")
1165 return password2
1166
1167 def save(self, commit=True):
1168 # Save the provided password in hashed format
1169 user = super(UserCreationForm, self).save(commit=False)
Tony Mackf9f4afb2013-05-01 21:02:12 -04001170 user.password = self.cleaned_data["password1"]
1171 #user.set_password(self.cleaned_data["password1"])
Siobhan Tully53437282013-04-26 19:30:27 -04001172 if commit:
1173 user.save()
1174 return user
1175
Siobhan Tully567e3e62013-06-21 18:03:16 -04001176
Siobhan Tully53437282013-04-26 19:30:27 -04001177class UserChangeForm(forms.ModelForm):
1178 """A form for updating users. Includes all the fields on
1179 the user, but replaces the password field with admin's
1180 password hash display field.
1181 """
Siobhan Tully63b7ba42014-01-12 10:35:11 -05001182 password = ReadOnlyPasswordHashField(label='Password',
1183 help_text= '<a href=\"password/\">Change Password</a>.')
Siobhan Tully53437282013-04-26 19:30:27 -04001184
1185 class Meta:
Siobhan Tully30fd4292013-05-10 08:59:56 -04001186 model = User
Scott Baker69e045d2014-11-17 23:44:03 -08001187 widgets = { 'public_key': UploadTextareaWidget, }
Siobhan Tully53437282013-04-26 19:30:27 -04001188
1189 def clean_password(self):
1190 # Regardless of what the user provides, return the initial value.
1191 # This is done here, rather than on the field, because the
1192 # field does not have access to the initial value
1193 return self.initial["password"]
1194
Scott Baker67db95f2015-02-18 15:50:11 -08001195class UserDashboardViewInline(XOSTabularInline):
Scott Baker2c3cb642014-05-19 17:55:56 -07001196 model = UserDashboardView
1197 extra = 0
1198 suit_classes = 'suit-tab suit-tab-dashboards'
1199 fields = ['user', 'dashboardView', 'order']
1200
Scott Baker67db95f2015-02-18 15:50:11 -08001201class ControllerUserInline(XOSTabularInline):
Tony Mack30dfcd72015-01-10 23:08:10 -05001202 model = ControllerUser
1203 extra = 0
1204 suit_classes = 'suit-tab suit-tab-admin-only'
1205 fields = ['controller', 'user', 'kuser_id']
1206 readonly_fields=['controller']
1207
1208
Scott Baker3a8aed62015-02-27 12:21:22 -08001209class UserAdmin(XOSAdminMixin, UserAdmin):
1210 # Note: Make sure XOSAdminMixin is listed before
Scott Bakerf4aeedc2014-10-03 13:10:47 -07001211 # admin.ModelAdmin in the class declaration.
1212
Siobhan Tully53437282013-04-26 19:30:27 -04001213 class Meta:
1214 app_label = "core"
1215
1216 # The forms to add and change user instances
1217 form = UserChangeForm
1218 add_form = UserCreationForm
1219
1220 # The fields to be used in displaying the User model.
1221 # These override the definitions on the base UserAdmin
1222 # that reference specific fields on auth.User.
Scott Bakera111f442015-01-24 13:33:26 -08001223 list_display = ('backend_status_icon', 'email', 'firstname', 'lastname', 'site', 'last_login')
1224 list_display_links = ("email",)
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001225 list_filter = ('site',)
Scott Baker90472612015-01-29 10:55:53 -08001226 inlines = [SlicePrivilegeInline,SitePrivilegeInline]
Tony Mack30dfcd72015-01-10 23:08:10 -05001227 admin_inlines = [ControllerUserInline]
Scott Baker1a6a3902014-10-03 00:32:37 -07001228 fieldListLoginDetails = ['backend_status_text', 'email','site','password','is_active','is_readonly','is_admin','public_key']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001229 fieldListContactInfo = ['firstname','lastname','phone','timezone']
1230
Siobhan Tully53437282013-04-26 19:30:27 -04001231 fieldsets = (
Scott Baker40c00762014-08-21 16:55:59 -07001232 ('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 -04001233 ('Contact Information', {'fields': ('firstname','lastname','phone', 'timezone'), 'classes':['suit-tab suit-tab-contact']}),
Siobhan Tully53437282013-04-26 19:30:27 -04001234 #('Important dates', {'fields': ('last_login',)}),
1235 )
1236 add_fieldsets = (
1237 (None, {
1238 'classes': ('wide',),
Tony Mack365545d2015-01-28 12:03:15 -05001239 'fields': ('site', 'email', 'firstname', 'lastname', 'is_admin', 'is_readonly', 'phone', 'public_key','password1', 'password2')},
Siobhan Tully53437282013-04-26 19:30:27 -04001240 ),
1241 )
Scott Baker40c00762014-08-21 16:55:59 -07001242 readonly_fields = ('backend_status_text', )
Siobhan Tully53437282013-04-26 19:30:27 -04001243 search_fields = ('email',)
1244 ordering = ('email',)
1245 filter_horizontal = ()
1246
Scott Baker3ca51f62014-05-23 12:05:11 -07001247 user_readonly_fields = fieldListLoginDetails + fieldListContactInfo
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001248
Scott Baker6a995352014-10-06 17:51:20 -07001249 @property
1250 def suit_form_tabs(self):
1251 if getattr(_thread_locals, "obj", None) is None:
1252 return []
1253 else:
Tony Mack30dfcd72015-01-10 23:08:10 -05001254 tabs = [('general','Login Details'),
Scott Baker6a995352014-10-06 17:51:20 -07001255 ('contact','Contact Information'),
1256 ('sliceprivileges','Slice Privileges'),
Scott Baker90472612015-01-29 10:55:53 -08001257 ('siteprivileges','Site Privileges')]
Tony Mack30dfcd72015-01-10 23:08:10 -05001258
1259 request=getattr(_thread_locals, "request", None)
1260 if request and request.user.is_admin:
1261 tabs.append( ('admin-only', 'Admin-Only') )
1262
1263 return tabs
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001264
Tony Mackc2835a92013-05-28 09:18:49 -04001265 def formfield_for_foreignkey(self, db_field, request, **kwargs):
1266 if db_field.name == 'site':
Tony Mack5b061472014-02-04 07:57:10 -05001267 kwargs['queryset'] = Site.select_by_user(request.user)
Tony Mackc2835a92013-05-28 09:18:49 -04001268
1269 return super(UserAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
1270
Tony Mack5b061472014-02-04 07:57:10 -05001271 def queryset(self, request):
1272 return User.select_by_user(request.user)
1273
Tony Mackc8f443d2015-01-25 21:58:30 -05001274 def get_form(self, request, obj=None, **kwargs):
Tony Mack03b92292015-01-28 12:37:12 -05001275 # copy login details list
1276 login_details_fields = list(self.fieldListLoginDetails)
Tony Mack933b2912015-01-28 12:49:58 -05001277 if not request.user.is_admin:
Scott Baker90472612015-01-29 10:55:53 -08001278 # only admins can see 'is_admin' and 'is_readonly' fields
Tony Mack03b92292015-01-28 12:37:12 -05001279 if 'is_admin' in login_details_fields:
1280 login_details_fields.remove('is_admin')
1281 if 'is_readonly' in login_details_fields:
1282 login_details_fields.remove('is_readonly')
Tony Mack933b2912015-01-28 12:49:58 -05001283 #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
Tony Mack03b92292015-01-28 12:37:12 -05001284 # only admins and pis can change a user's site
Tony Mack933b2912015-01-28 12:49:58 -05001285 # self.readonly_fields = ('backend_status_text', 'site')
Tony Mack03b92292015-01-28 12:37:12 -05001286 self.fieldsets = (
1287 ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
1288 ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
1289 )
Tony Mackc8f443d2015-01-25 21:58:30 -05001290 return super(UserAdmin, self).get_form(request, obj, **kwargs)
1291
Scott Baker67db95f2015-02-18 15:50:11 -08001292class ControllerDashboardViewInline(XOSTabularInline):
Scott Bakerf2c0c512014-12-22 17:35:34 -08001293 model = ControllerDashboardView
Scott Baker786a9c12014-12-19 16:41:12 -08001294 extra = 0
1295 fields = ["controller", "url"]
1296 suit_classes = 'suit-tab suit-tab-controllers'
1297
Scott Baker67db95f2015-02-18 15:50:11 -08001298class DashboardViewAdmin(XOSBaseAdmin):
Scott Baker2c3cb642014-05-19 17:55:56 -07001299 fieldsets = [('Dashboard View Details',
Scott Baker59248182015-02-17 13:34:32 -08001300 {'fields': ['backend_status_text', 'name', 'url', 'enabled', 'deployments'],
Scott Baker2c3cb642014-05-19 17:55:56 -07001301 'classes': ['suit-tab suit-tab-general']})
1302 ]
Scott Baker2c44e6e2015-01-18 16:46:26 -08001303 list_display = ["name", "enabled", "url"]
Scott Baker40c00762014-08-21 16:55:59 -07001304 readonly_fields = ('backend_status_text', )
Scott Bakerf2c0c512014-12-22 17:35:34 -08001305 inlines = [ControllerDashboardViewInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001306
Scott Baker786a9c12014-12-19 16:41:12 -08001307 suit_form_tabs =(('general','Dashboard View Details'),
1308 ('controllers', 'Per-controller Dashboard Details'))
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001309
Scott Baker67db95f2015-02-18 15:50:11 -08001310class ServiceResourceInline(XOSTabularInline):
Scott Baker3de3e372013-05-10 16:50:44 -07001311 model = ServiceResource
1312 extra = 0
1313
Scott Baker67db95f2015-02-18 15:50:11 -08001314class ServiceClassAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001315 list_display = ('backend_status_icon', 'name', 'commitment', 'membershipFee')
1316 list_display_links = ('backend_status_icon', 'name', )
Scott Baker3de3e372013-05-10 16:50:44 -07001317 inlines = [ServiceResourceInline]
1318
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001319 user_readonly_fields = ['name', 'commitment', 'membershipFee']
1320 user_readonly_inlines = []
1321
Scott Baker67db95f2015-02-18 15:50:11 -08001322class ReservedResourceInline(XOSTabularInline):
Scott Baker133c9212013-05-17 09:09:11 -07001323 model = ReservedResource
1324 extra = 0
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001325 suit_classes = 'suit-tab suit-tab-reservedresources'
Scott Baker133c9212013-05-17 09:09:11 -07001326
1327 def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
1328 field = super(ReservedResourceInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
1329
1330 if db_field.name == 'resource':
1331 # restrict resources to those that the slice's service class allows
1332 if request._slice is not None:
1333 field.queryset = field.queryset.filter(serviceClass = request._slice.serviceClass, calendarReservable=True)
1334 if len(field.queryset) > 0:
1335 field.initial = field.queryset.all()[0]
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001336 else:
1337 field.queryset = field.queryset.none()
1338 elif db_field.name == 'sliver':
1339 # restrict slivers to those that belong to the slice
1340 if request._slice is not None:
Scott Baker133c9212013-05-17 09:09:11 -07001341 field.queryset = field.queryset.filter(slice = request._slice)
1342 else:
S.Çağlar Onurb6e63f02015-02-24 17:28:09 -05001343 field.queryset = field.queryset.none()
1344
Scott Baker133c9212013-05-17 09:09:11 -07001345 return field
1346
Tony Mack5b061472014-02-04 07:57:10 -05001347 def queryset(self, request):
1348 return ReservedResource.select_by_user(request.user)
1349
Scott Baker133c9212013-05-17 09:09:11 -07001350class ReservationChangeForm(forms.ModelForm):
1351 class Meta:
1352 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001353 widgets = {
1354 'slice' : LinkedSelect
1355 }
Scott Baker133c9212013-05-17 09:09:11 -07001356
1357class ReservationAddForm(forms.ModelForm):
1358 slice = forms.ModelChoiceField(queryset=Slice.objects.all(), widget=forms.Select(attrs={"onChange":"document.getElementById('id_refresh').value=1; submit()"}))
1359 refresh = forms.CharField(widget=forms.HiddenInput())
1360
1361 class Media:
Scott Baker06868952015-02-18 15:15:58 -08001362 css = {'all': ('xos.css',)} # .field-refresh { display: none; }
Scott Baker133c9212013-05-17 09:09:11 -07001363
1364 def clean_slice(self):
1365 slice = self.cleaned_data.get("slice")
1366 x = ServiceResource.objects.filter(serviceClass = slice.serviceClass, calendarReservable=True)
1367 if len(x) == 0:
1368 raise forms.ValidationError("The slice you selected does not have a service class that allows reservations")
1369 return slice
1370
1371 class Meta:
1372 model = Reservation
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001373 widgets = {
1374 'slice' : LinkedSelect
1375 }
1376
Scott Baker133c9212013-05-17 09:09:11 -07001377
1378class ReservationAddRefreshForm(ReservationAddForm):
1379 """ This form is displayed when the Reservation Form receives an update
1380 from the Slice dropdown onChange handler. It doesn't validate the
1381 data and doesn't save the data. This will cause the form to be
1382 redrawn.
1383 """
1384
Scott Baker8737e5f2013-05-17 09:35:32 -07001385 """ don't validate anything other than slice """
1386 dont_validate_fields = ("startTime", "duration")
1387
Scott Baker133c9212013-05-17 09:09:11 -07001388 def full_clean(self):
1389 result = super(ReservationAddForm, self).full_clean()
Scott Baker8737e5f2013-05-17 09:35:32 -07001390
1391 for fieldname in self.dont_validate_fields:
1392 if fieldname in self._errors:
1393 del self._errors[fieldname]
1394
Scott Baker133c9212013-05-17 09:09:11 -07001395 return result
1396
1397 """ don't save anything """
1398 def is_valid(self):
1399 return False
1400
Scott Baker67db95f2015-02-18 15:50:11 -08001401class ReservationAdmin(XOSBaseAdmin):
Scott Baker40c00762014-08-21 16:55:59 -07001402 fieldList = ['backend_status_text', 'slice', 'startTime', 'duration']
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001403 fieldsets = [('Reservation Details', {'fields': fieldList, 'classes': ['suit-tab suit-tab-general']})]
Scott Baker40c00762014-08-21 16:55:59 -07001404 readonly_fields = ('backend_status_text', )
Scott Baker133c9212013-05-17 09:09:11 -07001405 list_display = ('startTime', 'duration')
Scott Baker133c9212013-05-17 09:09:11 -07001406 form = ReservationAddForm
1407
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001408 suit_form_tabs = (('general','Reservation Details'), ('reservedresources','Reserved Resources'))
1409
1410 inlines = [ReservedResourceInline]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001411 user_readonly_fields = fieldList
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001412
Scott Baker133c9212013-05-17 09:09:11 -07001413 def add_view(self, request, form_url='', extra_context=None):
Scott Bakeracd45142013-05-19 16:19:16 -07001414 timezone.activate(request.user.timezone)
Scott Baker133c9212013-05-17 09:09:11 -07001415 request._refresh = False
1416 request._slice = None
1417 if request.method == 'POST':
Scott Baker8737e5f2013-05-17 09:35:32 -07001418 # "refresh" will be set to "1" if the form was submitted due to
1419 # a change in the Slice dropdown.
Scott Baker133c9212013-05-17 09:09:11 -07001420 if request.POST.get("refresh","1") == "1":
1421 request._refresh = True
1422 request.POST["refresh"] = "0"
Scott Baker8737e5f2013-05-17 09:35:32 -07001423
1424 # Keep track of the slice that was selected, so the
1425 # reservedResource inline can filter items for the slice.
Scott Baker133c9212013-05-17 09:09:11 -07001426 request._slice = request.POST.get("slice",None)
1427 if (request._slice is not None):
1428 request._slice = Slice.objects.get(id=request._slice)
1429
1430 result = super(ReservationAdmin, self).add_view(request, form_url, extra_context)
1431 return result
1432
Scott Bakeracd45142013-05-19 16:19:16 -07001433 def changelist_view(self, request, extra_context = None):
1434 timezone.activate(request.user.timezone)
1435 return super(ReservationAdmin, self).changelist_view(request, extra_context)
1436
Scott Baker133c9212013-05-17 09:09:11 -07001437 def get_form(self, request, obj=None, **kwargs):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001438 request._obj_ = obj
1439 if obj is not None:
1440 # For changes, set request._slice to the slice already set in the
1441 # object.
1442 request._slice = obj.slice
1443 self.form = ReservationChangeForm
1444 else:
1445 if getattr(request, "_refresh", False):
1446 self.form = ReservationAddRefreshForm
1447 else:
1448 self.form = ReservationAddForm
1449 return super(ReservationAdmin, self).get_form(request, obj, **kwargs)
1450
Scott Baker133c9212013-05-17 09:09:11 -07001451 def get_readonly_fields(self, request, obj=None):
Siobhan Tullyd3515752013-06-21 16:34:53 -04001452 if (obj is not None):
1453 # Prevent slice from being changed after the reservation has been
1454 # created.
1455 return ['slice']
1456 else:
Scott Baker133c9212013-05-17 09:09:11 -07001457 return []
Scott Baker3de3e372013-05-10 16:50:44 -07001458
Tony Mack5b061472014-02-04 07:57:10 -05001459 def queryset(self, request):
1460 return Reservation.select_by_user(request.user)
1461
Scott Baker67db95f2015-02-18 15:50:11 -08001462class NetworkParameterTypeAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001463 list_display = ("backend_status_icon", "name", )
1464 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001465 user_readonly_fields = ['name']
1466 user_readonly_inlines = []
Scott Baker74d8e622013-07-29 16:04:22 -07001467
Scott Baker67db95f2015-02-18 15:50:11 -08001468class RouterAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001469 list_display = ("backend_status_icon", "name", )
1470 list_display_links = ('backend_status_icon', 'name', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001471 user_readonly_fields = ['name']
1472 user_readonly_inlines = []
1473
Scott Baker67db95f2015-02-18 15:50:11 -08001474class RouterInline(XOSTabularInline):
Scott Baker74d8e622013-07-29 16:04:22 -07001475 model = Router.networks.through
1476 extra = 0
1477 verbose_name_plural = "Routers"
1478 verbose_name = "Router"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001479 suit_classes = 'suit-tab suit-tab-routers'
Scott Baker74d8e622013-07-29 16:04:22 -07001480
Scott Bakerb27b62c2014-08-15 16:29:16 -07001481class NetworkParameterInline(PlStackGenericTabularInline):
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001482 model = NetworkParameter
Scott Baker618e3792014-08-15 13:42:29 -07001483 extra = 0
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001484 verbose_name_plural = "Parameters"
1485 verbose_name = "Parameter"
1486 suit_classes = 'suit-tab suit-tab-netparams'
Scott Baker40c00762014-08-21 16:55:59 -07001487 fields = ['backend_status_icon', 'parameter', 'value']
1488 readonly_fields = ('backend_status_icon', )
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001489
Scott Baker67db95f2015-02-18 15:50:11 -08001490class NetworkSliversInline(XOSTabularInline):
Scott Baker40c00762014-08-21 16:55:59 -07001491 fields = ['backend_status_icon', 'network','sliver','ip']
1492 readonly_fields = ("backend_status_icon", "ip", )
Scott Baker74d8e622013-07-29 16:04:22 -07001493 model = NetworkSliver
Scott Baker874936e2014-01-13 18:15:34 -08001494 selflink_fieldname = "sliver"
Scott Baker74d8e622013-07-29 16:04:22 -07001495 extra = 0
1496 verbose_name_plural = "Slivers"
1497 verbose_name = "Sliver"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001498 suit_classes = 'suit-tab suit-tab-networkslivers'
Scott Baker74d8e622013-07-29 16:04:22 -07001499
Scott Baker67db95f2015-02-18 15:50:11 -08001500class NetworkSlicesInline(XOSTabularInline):
Scott Bakerd7d2a392013-08-06 08:57:30 -07001501 model = NetworkSlice
Scott Baker874936e2014-01-13 18:15:34 -08001502 selflink_fieldname = "slice"
Scott Bakerd7d2a392013-08-06 08:57:30 -07001503 extra = 0
1504 verbose_name_plural = "Slices"
1505 verbose_name = "Slice"
Siobhan Tully2d95e482013-09-06 10:56:06 -04001506 suit_classes = 'suit-tab suit-tab-networkslices'
Scott Baker40c00762014-08-21 16:55:59 -07001507 fields = ['backend_status_icon', 'network','slice']
1508 readonly_fields = ('backend_status_icon', )
Scott Bakerd7d2a392013-08-06 08:57:30 -07001509
Scott Baker67db95f2015-02-18 15:50:11 -08001510class ControllerNetworkInline(XOSTabularInline):
Tony Mack3066a952015-01-05 22:48:11 -05001511 model = ControllerNetwork
Scott Baker8806cdf2014-10-17 16:27:23 -07001512 extra = 0
Tony Mack06c8e472014-11-30 15:53:08 -05001513 verbose_name_plural = "Controller Networks"
1514 verbose_name = "Controller Network"
Scott Baker8806cdf2014-10-17 16:27:23 -07001515 suit_classes = 'suit-tab suit-tab-admin-only'
Tony Mack06c8e472014-11-30 15:53:08 -05001516 fields = ['backend_status_icon', 'controller','net_id','subnet_id']
Scott Baker8806cdf2014-10-17 16:27:23 -07001517 readonly_fields = ('backend_status_icon', )
1518
Scott Baker69e045d2014-11-17 23:44:03 -08001519class NetworkForm(forms.ModelForm):
1520 class Meta:
1521 model = Network
1522 widgets = {
1523 'topologyParameters': UploadTextareaWidget,
1524 'controllerParameters': UploadTextareaWidget,
1525 }
1526
Scott Baker67db95f2015-02-18 15:50:11 -08001527class NetworkAdmin(XOSBaseAdmin):
Scott Baker63d1a552014-08-21 15:19:07 -07001528 list_display = ("backend_status_icon", "name", "subnet", "ports", "labels")
1529 list_display_links = ('backend_status_icon', 'name', )
Scott Baker74d8e622013-07-29 16:04:22 -07001530 readonly_fields = ("subnet", )
Siobhan Tully2d95e482013-09-06 10:56:06 -04001531
Scott Bakerd7d2a392013-08-06 08:57:30 -07001532 inlines = [NetworkParameterInline, NetworkSliversInline, NetworkSlicesInline, RouterInline]
Tony Mack3066a952015-01-05 22:48:11 -05001533 admin_inlines = [ControllerNetworkInline]
Scott Baker74d8e622013-07-29 16:04:22 -07001534
Scott Baker69e045d2014-11-17 23:44:03 -08001535 form=NetworkForm
1536
Siobhan Tully2d95e482013-09-06 10:56:06 -04001537 fieldsets = [
Scott Baker549aa252015-01-03 12:29:29 -08001538 (None, {'fields': ['backend_status_text', 'name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001539 'classes':['suit-tab suit-tab-general']}),
Scott Baker549aa252015-01-03 12:29:29 -08001540 (None, {'fields': ['topology_parameters', 'controller_url', 'controller_parameters'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001541 'classes':['suit-tab suit-tab-sdn']}),
1542 ]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001543
Scott Baker40c00762014-08-21 16:55:59 -07001544 readonly_fields = ('backend_status_text', )
Scott Baker549aa252015-01-03 12:29:29 -08001545 user_readonly_fields = ['name','template','ports','labels','owner','guaranteed_bandwidth', 'permit_all_slices','permitted_slices','network_id','router_id','subnet_id','subnet']
Siobhan Tully2d95e482013-09-06 10:56:06 -04001546
Scott Baker8806cdf2014-10-17 16:27:23 -07001547 @property
1548 def suit_form_tabs(self):
1549 tabs=[('general','Network Details'),
Scott Baker3e28dd72014-11-17 16:04:45 -08001550 ('sdn', 'SDN Configuration'),
Scott Baker8806cdf2014-10-17 16:27:23 -07001551 ('netparams', 'Parameters'),
1552 ('networkslivers','Slivers'),
1553 ('networkslices','Slices'),
1554 ('routers','Routers'),
1555 ]
1556
1557 request=getattr(_thread_locals, "request", None)
1558 if request and request.user.is_admin:
1559 tabs.append( ('admin-only', 'Admin-Only') )
1560
1561 return tabs
1562
1563
Scott Baker67db95f2015-02-18 15:50:11 -08001564class NetworkTemplateAdmin(XOSBaseAdmin):
Scott Baker369f9b92015-01-03 12:03:38 -08001565 list_display = ("backend_status_icon", "name", "guaranteed_bandwidth", "visibility")
Scott Baker63d1a552014-08-21 15:19:07 -07001566 list_display_links = ('backend_status_icon', 'name', )
Scott Baker369f9b92015-01-03 12:03:38 -08001567 user_readonly_fields = ["name", "guaranteed_bandwidth", "visibility"]
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001568 user_readonly_inlines = []
Scott Baker3e28dd72014-11-17 16:04:45 -08001569 fieldsets = [
Scott Baker369f9b92015-01-03 12:03:38 -08001570 (None, {'fields': ['name', 'description', 'guaranteed_bandwidth', 'visibility', 'translation', 'shared_network_name', 'shared_network_id', 'topology_kind', 'controller_kind'],
Scott Baker3e28dd72014-11-17 16:04:45 -08001571 'classes':['suit-tab suit-tab-general']}),]
1572 suit_form_tabs = (('general','Network Template Details'), )
Scott Baker74d8e622013-07-29 16:04:22 -07001573
Scott Baker67db95f2015-02-18 15:50:11 -08001574class FlavorAdmin(XOSBaseAdmin):
Scott Baker37b47902014-09-02 14:37:41 -07001575 list_display = ("backend_status_icon", "name", "flavor", "order", "default")
1576 list_display_links = ("backend_status_icon", "name")
1577 user_readonly_fields = ("name", "flavor")
1578 fields = ("name", "description", "flavor", "order", "default")
1579
Tony Mack31c2b8f2013-04-26 20:01:42 -04001580# register a signal that caches the user's credentials when they log in
1581def cache_credentials(sender, user, request, **kwds):
1582 auth = {'username': request.POST['username'],
1583 'password': request.POST['password']}
1584 request.session['auth'] = auth
1585user_logged_in.connect(cache_credentials)
1586
Scott Baker15cddfa2013-12-09 13:45:19 -08001587def dollar_field(fieldName, short_description):
1588 def newFunc(self, obj):
1589 try:
1590 x= "$ %0.2f" % float(getattr(obj, fieldName, 0.0))
1591 except:
1592 x=getattr(obj, fieldName, 0.0)
1593 return x
1594 newFunc.short_description = short_description
1595 return newFunc
1596
1597def right_dollar_field(fieldName, short_description):
1598 def newFunc(self, obj):
1599 try:
1600 #x= '<div align=right style="width:6em">$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1601 x= '<div align=right>$ %0.2f</div>' % float(getattr(obj, fieldName, 0.0))
1602 except:
1603 x=getattr(obj, fieldName, 0.0)
1604 return x
1605 newFunc.short_description = short_description
1606 newFunc.allow_tags = True
1607 return newFunc
Scott Baker43105042013-12-06 23:23:36 -08001608
Scott Baker67db95f2015-02-18 15:50:11 -08001609class InvoiceChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001610 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001611 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001612 verbose_name_plural = "Charges"
1613 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001614 exclude = ['account']
Scott Baker9cb88a22013-12-09 18:56:00 -08001615 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1616 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1617 can_delete = False
1618 max_num = 0
1619
1620 dollar_amount = right_dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001621
1622class InvoiceAdmin(admin.ModelAdmin):
1623 list_display = ("date", "account")
1624
1625 inlines = [InvoiceChargeInline]
1626
Scott Baker9cb88a22013-12-09 18:56:00 -08001627 fields = ["date", "account", "dollar_amount"]
1628 readonly_fields = ["date", "account", "dollar_amount"]
1629
1630 dollar_amount = dollar_field("amount", "Amount")
Scott Baker43105042013-12-06 23:23:36 -08001631
Scott Baker67db95f2015-02-18 15:50:11 -08001632class InvoiceInline(XOSTabularInline):
Scott Baker15cddfa2013-12-09 13:45:19 -08001633 model = Invoice
1634 extra = 0
1635 verbose_name_plural = "Invoices"
1636 verbose_name = "Invoice"
Scott Baker0165fac2014-01-13 11:49:26 -08001637 fields = ["date", "dollar_amount"]
1638 readonly_fields = ["date", "dollar_amount"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001639 suit_classes = 'suit-tab suit-tab-accountinvoice'
1640 can_delete=False
1641 max_num=0
1642
1643 dollar_amount = right_dollar_field("amount", "Amount")
1644
Scott Baker67db95f2015-02-18 15:50:11 -08001645class PendingChargeInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001646 model = Charge
Scott Baker15cddfa2013-12-09 13:45:19 -08001647 extra = 0
Scott Baker43105042013-12-06 23:23:36 -08001648 verbose_name_plural = "Charges"
1649 verbose_name = "Charge"
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001650 exclude = ["invoice"]
Scott Baker15cddfa2013-12-09 13:45:19 -08001651 fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
1652 readonly_fields = ["date", "kind", "state", "object", "coreHours", "dollar_amount", "slice"]
Scott Baker43105042013-12-06 23:23:36 -08001653 suit_classes = 'suit-tab suit-tab-accountpendingcharges'
Scott Baker15cddfa2013-12-09 13:45:19 -08001654 can_delete=False
1655 max_num=0
Scott Baker43105042013-12-06 23:23:36 -08001656
1657 def queryset(self, request):
1658 qs = super(PendingChargeInline, self).queryset(request)
1659 qs = qs.filter(state="pending")
1660 return qs
1661
Scott Baker15cddfa2013-12-09 13:45:19 -08001662 dollar_amount = right_dollar_field("amount", "Amount")
1663
Scott Baker67db95f2015-02-18 15:50:11 -08001664class PaymentInline(XOSTabularInline):
Scott Baker43105042013-12-06 23:23:36 -08001665 model=Payment
1666 extra = 1
1667 verbose_name_plural = "Payments"
1668 verbose_name = "Payment"
Scott Baker15cddfa2013-12-09 13:45:19 -08001669 fields = ["date", "dollar_amount"]
1670 readonly_fields = ["date", "dollar_amount"]
Scott Baker43105042013-12-06 23:23:36 -08001671 suit_classes = 'suit-tab suit-tab-accountpayments'
Scott Baker15cddfa2013-12-09 13:45:19 -08001672 can_delete=False
1673 max_num=0
1674
1675 dollar_amount = right_dollar_field("amount", "Amount")
1676
Scott Baker43105042013-12-06 23:23:36 -08001677class AccountAdmin(admin.ModelAdmin):
1678 list_display = ("site", "balance_due")
1679
1680 inlines = [InvoiceInline, PaymentInline, PendingChargeInline]
1681
1682 fieldsets = [
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001683 (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 -08001684
Scott Baker15cddfa2013-12-09 13:45:19 -08001685 readonly_fields = ['site', 'dollar_balance_due', 'dollar_total_invoices', 'dollar_total_payments']
Scott Baker43105042013-12-06 23:23:36 -08001686
1687 suit_form_tabs =(
1688 ('general','Account Details'),
1689 ('accountinvoice', 'Invoices'),
1690 ('accountpayments', 'Payments'),
1691 ('accountpendingcharges','Pending Charges'),
1692 )
1693
Scott Baker15cddfa2013-12-09 13:45:19 -08001694 dollar_balance_due = dollar_field("balance_due", "Balance Due")
1695 dollar_total_invoices = dollar_field("total_invoices", "Total Invoices")
1696 dollar_total_payments = dollar_field("total_payments", "Total Payments")
1697
Siobhan Tully53437282013-04-26 19:30:27 -04001698# Now register the new UserAdmin...
Siobhan Tully30fd4292013-05-10 08:59:56 -04001699admin.site.register(User, UserAdmin)
Siobhan Tully53437282013-04-26 19:30:27 -04001700# ... and, since we're not using Django's builtin permissions,
1701# unregister the Group model from admin.
Siobhan Tullyce652d02013-10-08 21:52:35 -04001702#admin.site.unregister(Group)
Siobhan Tully53437282013-04-26 19:30:27 -04001703
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001704#Do not show django evolution in the admin interface
1705from django_evolution.models import Version, Evolution
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001706#admin.site.unregister(Version)
1707#admin.site.unregister(Evolution)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001708
1709
1710# When debugging it is often easier to see all the classes, but for regular use
1711# only the top-levels should be displayed
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001712showAll = False
Scott Baker43105042013-12-06 23:23:36 -08001713
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001714admin.site.register(Deployment, DeploymentAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05001715admin.site.register(Controller, ControllerAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001716admin.site.register(Site, SiteAdmin)
Siobhan Tully4bc09f22013-04-10 21:15:21 -04001717admin.site.register(Slice, SliceAdmin)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001718admin.site.register(Service, ServiceAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05001719#admin.site.register(Reservation, ReservationAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001720admin.site.register(Network, NetworkAdmin)
1721admin.site.register(Router, RouterAdmin)
Scott Baker74d8e622013-07-29 16:04:22 -07001722admin.site.register(NetworkTemplate, NetworkTemplateAdmin)
Tony Mack450b6e02015-01-25 12:35:29 -05001723#admin.site.register(Account, AccountAdmin)
1724#admin.site.register(Invoice, InvoiceAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001725
Siobhan Tullycf04fb62014-01-11 11:25:57 -05001726if True:
1727 admin.site.register(NetworkParameterType, NetworkParameterTypeAdmin)
1728 admin.site.register(ServiceClass, ServiceClassAdmin)
Siobhan Tullyd3515752013-06-21 16:34:53 -04001729 admin.site.register(Tag, TagAdmin)
Tony Mack06c8e472014-11-30 15:53:08 -05001730 admin.site.register(ControllerRole)
Siobhan Tullyce652d02013-10-08 21:52:35 -04001731 admin.site.register(SiteRole)
1732 admin.site.register(SliceRole)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001733 admin.site.register(Node, NodeAdmin)
Siobhan Tullybfd11dc2013-09-03 12:59:24 -04001734 #admin.site.register(SlicePrivilege, SlicePrivilegeAdmin)
1735 #admin.site.register(SitePrivilege, SitePrivilegeAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001736 admin.site.register(Sliver, SliverAdmin)
Siobhan Tullybf1153a2013-05-27 20:53:48 -04001737 admin.site.register(Image, ImageAdmin)
Scott Baker2c3cb642014-05-19 17:55:56 -07001738 admin.site.register(DashboardView, DashboardViewAdmin)
Scott Baker37b47902014-09-02 14:37:41 -07001739 admin.site.register(Flavor, FlavorAdmin)
Tony Mack7130ac32013-03-22 21:58:00 -04001740